diff --git a/MIGRATION-NOTES.md b/MIGRATION-NOTES.md new file mode 100644 index 00000000..d80480b7 --- /dev/null +++ b/MIGRATION-NOTES.md @@ -0,0 +1,32 @@ +# MIGRATION NOTES + +A collection of random notes pop up during the migration process. + +- TODO: review the retry logic +- const { lastRound: firstRound } = suggestedParams! // TODO: document suggested params doesn't have first round anymore +- explain the type differences between transact and algod +- remove waitForIndexer + - DO NOT remove it +- ATC was removed as a transaction type in the composer +- Fee calc inside the txn constructor +- error messages changed, for example, asset tests +- `AssetHoldingReference` replaced by `HoldingReference` +- `ApplicationLocalReference` replaced by `LocalsReference` +- BoxReference is gone too +- Error name is gone (snapshot tests updated) +- TODO: remove the ATC too +- TODO: add interface for breaking change, for example, Transaction +- TODO: simplify signer + account +- TODO: take notes of the legacy functions to be removed and communicate with devrels +- TODO: standardise box ref +- TODO: keep track of the changes we make to algokit_transact to fit with algosdk +- For integration with lora to work: + - need to update subscriber to use the new utils and remove algosdk +- TODO: go ahead with resource/fee on build. Need to have backward compatibility, when resource population is set in send, do it but make sure that it only happens once. +- `encodeUnsignedSimulateTransaction` was removed from sdk +- Discuss the inconsistency of transaction and txn, txIds, txID +- Disucss the naming of foreignApps vs appReferences + access references +- Discuss appCall vs applicationCall +- SourceMap was renamed to ProgramSourceMap +- OnApplicationComplete.UpdateApplicationOC was renamed to OnApplicationComplete.UpdateApplication +- ResourceReference (algod) vs AccessReference (utils) diff --git a/package-lock.json b/package-lock.json index 2a437ba0..c0c3b843 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,15 +12,15 @@ "packages/*" ], "dependencies": { - "buffer": "^6.0.3" + "algorand-msgpack": "^1.1.0", + "buffer": "^6.0.3", + "hi-base32": "^0.5.1", + "js-sha512": "^0.8.0", + "json-bigint": "^1.0.0", + "tweetnacl": "^1.0.3", + "vlq": "^2.0.4" }, "devDependencies": { - "@algorandfoundation/algokit-abi": "*", - "@algorandfoundation/algokit-algod-client": "*", - "@algorandfoundation/algokit-common": "*", - "@algorandfoundation/algokit-indexer-client": "*", - "@algorandfoundation/algokit-kmd-client": "*", - "@algorandfoundation/algokit-transact": "*", "@algorandfoundation/tealscript": "^0.106.3", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", @@ -28,10 +28,10 @@ "@makerx/prettier-config": "^2.0.0", "@makerx/ts-toolkit": "4.0.0-beta.24", "@tsconfig/node20": "^20.1.4", + "@types/json-bigint": "^1.0.4", "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^8.8.1", "@vitest/coverage-v8": "^4.0.5", - "algosdk": "^3.5.2", "better-npm-audit": "^3.11.0", "conventional-changelog-conventionalcommits": "8.0.0", "cpy-cli": "^5.0.0", @@ -45,7 +45,7 @@ "replace-in-files-cli": "^3.0.0", "rimraf": "^6.0.1", "rolldown": "^1.0.0-beta.45", - "rolldown-plugin-dts": "^0.17.2", + "rolldown-plugin-dts": "^0.17.3", "semantic-release": "^24.1.2", "tiny-invariant": "^1.3.1", "ts-node": "^10.9.1", @@ -59,16 +59,12 @@ }, "engines": { "node": ">=20.0" - }, - "peerDependencies": { - "algosdk": "^3.5.2" } }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -97,10 +93,12 @@ "resolved": "packages/transact", "link": true }, + "node_modules/@algorandfoundation/sdk": { + "resolved": "packages/sdk", + "link": true + }, "node_modules/@algorandfoundation/tealscript": { "version": "0.106.3", - "resolved": "https://registry.npmjs.org/@algorandfoundation/tealscript/-/tealscript-0.106.3.tgz", - "integrity": "sha512-IOlJx5nx1GfDt/zknNM/kQ6ZwXWKyibSgsRJPt7QA9Qpz8LuHAxw8BZM49FB/TZExiA/lQohzdSS/gvHnOkCTw==", "dev": true, "dependencies": { "@microsoft/tsdoc": "^0.14.2", @@ -123,9 +121,8 @@ }, "node_modules/@algorandfoundation/tealscript/node_modules/typescript": { "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -136,9 +133,8 @@ }, "node_modules/@babel/code-frame": { "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.1.tgz", - "integrity": "sha512-bC49z4spJQR3j8vFtJBLqzyzFV0ciuL5HYX7qfSl3KEqeMVV+eTquRvmXxpvB0AMubRrvv7y5DILiLLPi57Ewg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/highlight": "^7.24.1", "picocolors": "^1.0.0" @@ -149,8 +145,6 @@ }, "node_modules/@babel/generator": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dev": true, "license": "MIT", "dependencies": { @@ -166,8 +160,6 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -176,8 +168,6 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -186,9 +176,8 @@ }, "node_modules/@babel/highlight": { "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.1.tgz", - "integrity": "sha512-EPmDPxidWe/Ex+HTFINpvXdPHRmgSF3T8hGvzondYjmgzTQ/0EbLpSxyt+w3zzlYSk9cNBQNF9k0dT5Z2NiBjw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -201,9 +190,8 @@ }, "node_modules/@babel/highlight/node_modules/ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -213,9 +201,8 @@ }, "node_modules/@babel/highlight/node_modules/chalk": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -227,42 +214,37 @@ }, "node_modules/@babel/highlight/node_modules/color-convert": { "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/supports-color": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -272,8 +254,6 @@ }, "node_modules/@babel/parser": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -288,8 +268,6 @@ }, "node_modules/@babel/types": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, "license": "MIT", "dependencies": { @@ -312,9 +290,8 @@ }, "node_modules/@colors/colors": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.1.90" @@ -322,9 +299,8 @@ }, "node_modules/@commitlint/cli": { "version": "19.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.6.0.tgz", - "integrity": "sha512-v17BgGD9w5KnthaKxXnEg6KLq6DYiAxyiN44TpiRtqyW8NSq+Kx99mkEG8Qo6uu6cI5eMzMojW2muJxjmPnF8w==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/format": "^19.5.0", "@commitlint/lint": "^19.6.0", @@ -343,9 +319,8 @@ }, "node_modules/@commitlint/config-conventional": { "version": "19.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.6.0.tgz", - "integrity": "sha512-DJT40iMnTYtBtUfw9ApbsLZFke1zKh6llITVJ+x9mtpHD08gsNXaIRqHTmwTZL3dNX5+WoyK7pCN/5zswvkBCQ==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/types": "^19.5.0", "conventional-changelog-conventionalcommits": "^7.0.2" @@ -356,9 +331,8 @@ }, "node_modules/@commitlint/config-conventional/node_modules/conventional-changelog-conventionalcommits": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", - "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", "dev": true, + "license": "ISC", "dependencies": { "compare-func": "^2.0.0" }, @@ -368,9 +342,8 @@ }, "node_modules/@commitlint/config-validator": { "version": "19.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.5.0.tgz", - "integrity": "sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/types": "^19.5.0", "ajv": "^8.11.0" @@ -381,9 +354,8 @@ }, "node_modules/@commitlint/ensure": { "version": "19.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.5.0.tgz", - "integrity": "sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/types": "^19.5.0", "lodash.camelcase": "^4.3.0", @@ -398,18 +370,16 @@ }, "node_modules/@commitlint/execute-rule": { "version": "19.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.5.0.tgz", - "integrity": "sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==", "dev": true, + "license": "MIT", "engines": { "node": ">=v18" } }, "node_modules/@commitlint/format": { "version": "19.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.5.0.tgz", - "integrity": "sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/types": "^19.5.0", "chalk": "^5.3.0" @@ -420,9 +390,8 @@ }, "node_modules/@commitlint/format/node_modules/chalk": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -432,9 +401,8 @@ }, "node_modules/@commitlint/is-ignored": { "version": "19.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.6.0.tgz", - "integrity": "sha512-Ov6iBgxJQFR9koOupDPHvcHU9keFupDgtB3lObdEZDroiG4jj1rzky60fbQozFKVYRTUdrBGICHG0YVmRuAJmw==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/types": "^19.5.0", "semver": "^7.6.0" @@ -445,9 +413,8 @@ }, "node_modules/@commitlint/lint": { "version": "19.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.6.0.tgz", - "integrity": "sha512-LRo7zDkXtcIrpco9RnfhOKeg8PAnE3oDDoalnrVU/EVaKHYBWYL1DlRR7+3AWn0JiBqD8yKOfetVxJGdEtZ0tg==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/is-ignored": "^19.6.0", "@commitlint/parse": "^19.5.0", @@ -460,9 +427,8 @@ }, "node_modules/@commitlint/load": { "version": "19.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.5.0.tgz", - "integrity": "sha512-INOUhkL/qaKqwcTUvCE8iIUf5XHsEPCLY9looJ/ipzi7jtGhgmtH7OOFiNvwYgH7mA8osUWOUDV8t4E2HAi4xA==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/config-validator": "^19.5.0", "@commitlint/execute-rule": "^19.5.0", @@ -481,9 +447,8 @@ }, "node_modules/@commitlint/load/node_modules/chalk": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -493,18 +458,16 @@ }, "node_modules/@commitlint/message": { "version": "19.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.5.0.tgz", - "integrity": "sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=v18" } }, "node_modules/@commitlint/parse": { "version": "19.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.5.0.tgz", - "integrity": "sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/types": "^19.5.0", "conventional-changelog-angular": "^7.0.0", @@ -516,9 +479,8 @@ }, "node_modules/@commitlint/read": { "version": "19.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.5.0.tgz", - "integrity": "sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/top-level": "^19.5.0", "@commitlint/types": "^19.5.0", @@ -532,9 +494,8 @@ }, "node_modules/@commitlint/resolve-extends": { "version": "19.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.5.0.tgz", - "integrity": "sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/config-validator": "^19.5.0", "@commitlint/types": "^19.5.0", @@ -549,9 +510,8 @@ }, "node_modules/@commitlint/rules": { "version": "19.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.6.0.tgz", - "integrity": "sha512-1f2reW7lbrI0X0ozZMesS/WZxgPa4/wi56vFuJENBmed6mWq5KsheN/nxqnl/C23ioxpPO/PL6tXpiiFy5Bhjw==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/ensure": "^19.5.0", "@commitlint/message": "^19.5.0", @@ -564,18 +524,16 @@ }, "node_modules/@commitlint/to-lines": { "version": "19.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.5.0.tgz", - "integrity": "sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=v18" } }, "node_modules/@commitlint/top-level": { "version": "19.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.5.0.tgz", - "integrity": "sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^7.0.0" }, @@ -585,9 +543,8 @@ }, "node_modules/@commitlint/top-level/node_modules/find-up": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", - "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", @@ -602,9 +559,8 @@ }, "node_modules/@commitlint/top-level/node_modules/locate-path": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^6.0.0" }, @@ -617,9 +573,8 @@ }, "node_modules/@commitlint/top-level/node_modules/p-limit": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^1.0.0" }, @@ -632,9 +587,8 @@ }, "node_modules/@commitlint/top-level/node_modules/p-locate": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^4.0.0" }, @@ -647,18 +601,16 @@ }, "node_modules/@commitlint/top-level/node_modules/path-exists": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, "node_modules/@commitlint/top-level/node_modules/yocto-queue": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", - "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.20" }, @@ -668,9 +620,8 @@ }, "node_modules/@commitlint/types": { "version": "19.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.5.0.tgz", - "integrity": "sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==", "dev": true, + "license": "MIT", "dependencies": { "@types/conventional-commits-parser": "^5.0.0", "chalk": "^5.3.0" @@ -681,9 +632,8 @@ }, "node_modules/@commitlint/types/node_modules/chalk": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -693,9 +643,8 @@ }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -705,9 +654,8 @@ }, "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1174,9 +1122,8 @@ }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.3.0" }, @@ -1189,8 +1136,6 @@ }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "license": "MIT", "engines": { @@ -1199,8 +1144,6 @@ }, "node_modules/@eslint/config-array": { "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1214,8 +1157,6 @@ }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -1225,8 +1166,6 @@ }, "node_modules/@eslint/config-array/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -1238,8 +1177,6 @@ }, "node_modules/@eslint/config-helpers": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1248,8 +1185,6 @@ }, "node_modules/@eslint/core": { "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1261,8 +1196,6 @@ }, "node_modules/@eslint/eslintrc": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1285,8 +1218,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", "dependencies": { @@ -1302,8 +1233,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -1313,15 +1242,11 @@ }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -1333,8 +1258,6 @@ }, "node_modules/@eslint/js": { "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -1346,8 +1269,6 @@ }, "node_modules/@eslint/object-schema": { "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1356,8 +1277,6 @@ }, "node_modules/@eslint/plugin-kit": { "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1370,8 +1289,6 @@ }, "node_modules/@humanfs/core": { "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1380,8 +1297,6 @@ }, "node_modules/@humanfs/node": { "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1394,8 +1309,6 @@ }, "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1408,9 +1321,8 @@ }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -1421,8 +1333,6 @@ }, "node_modules/@humanwhocodes/retry": { "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1435,9 +1345,8 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1452,9 +1361,8 @@ }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -1464,9 +1372,8 @@ }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -1479,8 +1386,6 @@ }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", "dependencies": { @@ -1490,24 +1395,19 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { @@ -1517,14 +1417,11 @@ }, "node_modules/@makerx/prettier-config": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@makerx/prettier-config/-/prettier-config-2.0.1.tgz", - "integrity": "sha512-fI2TT312JPVrgNFASiZG8b7N4RjazYvxEd7Yq05jwTdZyTiT9eRGjKHSRc+MA9uPBeOVLWNI7PKYTCqyXTw7uA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@makerx/ts-toolkit": { "version": "4.0.0-beta.24", - "resolved": "https://registry.npmjs.org/@makerx/ts-toolkit/-/ts-toolkit-4.0.0-beta.24.tgz", - "integrity": "sha512-TCTg3H6r43KoLmOQgdqrMK4pFQqUqPO44ZW3WhZvvfFOA6fOe8XG49gdKXshKB+p4tb2UupHOzRQE9dDp7bbrQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1543,8 +1440,6 @@ }, "node_modules/@makerx/ts-toolkit/node_modules/commander": { "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "license": "MIT", "engines": { @@ -1553,9 +1448,8 @@ }, "node_modules/@microsoft/tsdoc": { "version": "0.14.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", - "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.0.7", @@ -1572,8 +1466,6 @@ }, "node_modules/@noble/ed25519": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-3.0.0.tgz", - "integrity": "sha512-QyteqMNm0GLqfa5SoYbSC3+Pvykwpn95Zgth4MFVSMKBB75ELl9tX1LAVsN4c3HXOrakHsF2gL4zWDAYCcsnzg==", "dev": true, "license": "MIT", "funding": { @@ -1582,9 +1474,8 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -1595,18 +1486,16 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -1617,18 +1506,16 @@ }, "node_modules/@octokit/auth-token": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", - "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 18" } }, "node_modules/@octokit/core": { "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.2.tgz", - "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/auth-token": "^5.0.0", "@octokit/graphql": "^8.0.0", @@ -1644,9 +1531,8 @@ }, "node_modules/@octokit/endpoint": { "version": "10.1.3", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.3.tgz", - "integrity": "sha512-nBRBMpKPhQUxCsQQeW+rCJ/OPSMcj3g0nfHn01zGYZXuNDvvXudF/TYY6APj5THlurerpFN4a/dQAIAaM6BYhA==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/types": "^13.6.2", "universal-user-agent": "^7.0.2" @@ -1657,9 +1543,8 @@ }, "node_modules/@octokit/graphql": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.1.tgz", - "integrity": "sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/request": "^9.0.0", "@octokit/types": "^13.0.0", @@ -1671,15 +1556,13 @@ }, "node_modules/@octokit/openapi-types": { "version": "23.0.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-23.0.1.tgz", - "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { "version": "11.4.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.2.tgz", - "integrity": "sha512-BXJ7XPCTDXFF+wxcg/zscfgw2O/iDPtNSkwwR1W1W5c4Mb3zav/M2XvxQ23nVmKj7jpweB4g8viMeCQdm7LMVA==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/types": "^13.7.0" }, @@ -1692,9 +1575,8 @@ }, "node_modules/@octokit/plugin-retry": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.1.2.tgz", - "integrity": "sha512-XOWnPpH2kJ5VTwozsxGurw+svB2e61aWlmk5EVIYZPwFK5F9h4cyPyj9CIKRyMXMHSwpIsI3mPOdpMmrRhe7UQ==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/request-error": "^6.0.0", "@octokit/types": "^13.0.0", @@ -1709,9 +1591,8 @@ }, "node_modules/@octokit/plugin-throttling": { "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-9.3.2.tgz", - "integrity": "sha512-FqpvcTpIWFpMMwIeSoypoJXysSAQ3R+ALJhXXSG1HTP3YZOIeLmcNcimKaXxTcws+Sh6yoRl13SJ5r8sXc1Fhw==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/types": "^13.0.0", "bottleneck": "^2.15.3" @@ -1725,9 +1606,8 @@ }, "node_modules/@octokit/request": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.2.tgz", - "integrity": "sha512-dZl0ZHx6gOQGcffgm1/Sf6JfEpmh34v3Af2Uci02vzUYz6qEN6zepoRtmybWXIGXFIK8K9ylE3b+duCWqhArtg==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/endpoint": "^10.1.3", "@octokit/request-error": "^6.1.7", @@ -1741,9 +1621,8 @@ }, "node_modules/@octokit/request-error": { "version": "6.1.7", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.7.tgz", - "integrity": "sha512-69NIppAwaauwZv6aOzb+VVLwt+0havz9GT5YplkeJv7fG7a40qpLt/yZKyiDxAhgz0EtgNdNcb96Z0u+Zyuy2g==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/types": "^13.6.2" }, @@ -1753,17 +1632,14 @@ }, "node_modules/@octokit/types": { "version": "13.8.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.8.0.tgz", - "integrity": "sha512-x7DjTIbEpEWXK99DMd01QfWy0hd5h4EN+Q7shkdKds3otGQP+oWE/y0A76i1OvH9fygo4ddvNf7ZvF0t78P98A==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/openapi-types": "^23.0.1" } }, "node_modules/@oxc-project/types": { "version": "0.95.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.95.0.tgz", - "integrity": "sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ==", "dev": true, "license": "MIT", "funding": { @@ -1772,9 +1648,8 @@ }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=14" @@ -1782,8 +1657,6 @@ }, "node_modules/@playwright/test": { "version": "1.56.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz", - "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1798,18 +1671,16 @@ }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", - "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.22.0" } }, "node_modules/@pnpm/network.ca-file": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "4.2.10" }, @@ -1819,15 +1690,13 @@ }, "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@pnpm/npm-conf": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", - "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", "dev": true, + "license": "MIT", "dependencies": { "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", @@ -2392,15 +2261,13 @@ }, "node_modules/@sec-ant/readable-stream": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", - "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@semantic-release/commit-analyzer": { "version": "13.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.0.tgz", - "integrity": "sha512-KtXWczvTAB1ZFZ6B4O+w8HkfYm/OgQb1dUGNFZtDgQ0csggrmkq8sTxhd+lwGF8kMb59/RnG9o4Tn7M/I8dQ9Q==", "dev": true, + "license": "MIT", "dependencies": { "conventional-changelog-angular": "^8.0.0", "conventional-changelog-writer": "^8.0.0", @@ -2420,9 +2287,8 @@ }, "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-changelog-angular": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", - "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", "dev": true, + "license": "ISC", "dependencies": { "compare-func": "^2.0.0" }, @@ -2432,9 +2298,8 @@ }, "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-commits-parser": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.0.0.tgz", - "integrity": "sha512-TbsINLp48XeMXR8EvGjTnKGsZqBemisPoyWESlpRyR8lif0lcwzqz+NMtYSj1ooF/WYjSuu7wX0CtdeeMEQAmA==", "dev": true, + "license": "MIT", "dependencies": { "meow": "^13.0.0" }, @@ -2447,9 +2312,8 @@ }, "node_modules/@semantic-release/commit-analyzer/node_modules/meow": { "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -2459,18 +2323,16 @@ }, "node_modules/@semantic-release/error": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", - "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@semantic-release/github": { "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-11.0.1.tgz", - "integrity": "sha512-Z9cr0LgU/zgucbT9cksH0/pX9zmVda9hkDPcgIE0uvjMQ8w/mElDivGjx1w1pEQ+MuQJ5CBq3VCF16S6G4VH3A==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/core": "^6.0.0", "@octokit/plugin-paginate-rest": "^11.0.0", @@ -2498,9 +2360,8 @@ }, "node_modules/@semantic-release/github/node_modules/p-filter": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz", - "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==", "dev": true, + "license": "MIT", "dependencies": { "p-map": "^7.0.1" }, @@ -2513,9 +2374,8 @@ }, "node_modules/@semantic-release/github/node_modules/p-map": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.2.tgz", - "integrity": "sha512-z4cYYMMdKHzw4O5UkWJImbZynVIo0lSGTXc7bzB1e/rrDqkgGUNysK/o4bTr+0+xKvvLoTyGqYC4Fgljy9qe1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -2525,9 +2385,8 @@ }, "node_modules/@semantic-release/npm": { "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-12.0.0.tgz", - "integrity": "sha512-72TVYQCH9NvVsO/y13eF8vE4bNnfls518+4KcFwJUKi7AtA/ZXoNgSg9gTTfw5eMZMkiH0izUrpGXgZE/cSQhA==", "dev": true, + "license": "MIT", "dependencies": { "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", @@ -2552,9 +2411,8 @@ }, "node_modules/@semantic-release/npm/node_modules/execa": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", @@ -2575,9 +2433,8 @@ }, "node_modules/@semantic-release/npm/node_modules/get-stream": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -2587,9 +2444,8 @@ }, "node_modules/@semantic-release/npm/node_modules/hosted-git-info": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", - "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^10.0.1" }, @@ -2599,18 +2455,16 @@ }, "node_modules/@semantic-release/npm/node_modules/human-signals": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=16.17.0" } }, "node_modules/@semantic-release/npm/node_modules/is-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -2620,18 +2474,16 @@ }, "node_modules/@semantic-release/npm/node_modules/lru-cache": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, + "license": "ISC", "engines": { "node": "14 || >=16.14" } }, "node_modules/@semantic-release/npm/node_modules/mimic-fn": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -2641,9 +2493,8 @@ }, "node_modules/@semantic-release/npm/node_modules/normalize-package-data": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", - "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^7.0.0", "is-core-module": "^2.8.1", @@ -2656,9 +2507,8 @@ }, "node_modules/@semantic-release/npm/node_modules/npm-run-path": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^4.0.0" }, @@ -2671,9 +2521,8 @@ }, "node_modules/@semantic-release/npm/node_modules/onetime": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^4.0.0" }, @@ -2686,9 +2535,8 @@ }, "node_modules/@semantic-release/npm/node_modules/parse-json": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", - "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.22.13", "index-to-position": "^0.1.2", @@ -2703,9 +2551,8 @@ }, "node_modules/@semantic-release/npm/node_modules/path-key": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -2715,9 +2562,8 @@ }, "node_modules/@semantic-release/npm/node_modules/read-pkg": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", - "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", "dev": true, + "license": "MIT", "dependencies": { "@types/normalize-package-data": "^2.4.3", "normalize-package-data": "^6.0.0", @@ -2734,9 +2580,8 @@ }, "node_modules/@semantic-release/npm/node_modules/signal-exit": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -2746,9 +2591,8 @@ }, "node_modules/@semantic-release/npm/node_modules/strip-final-newline": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -2758,9 +2602,8 @@ }, "node_modules/@semantic-release/npm/node_modules/type-fest": { "version": "4.15.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz", - "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=16" }, @@ -2770,9 +2613,8 @@ }, "node_modules/@semantic-release/release-notes-generator": { "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.0.1.tgz", - "integrity": "sha512-K0w+5220TM4HZTthE5dDpIuFrnkN1NfTGPidJFm04ULT1DEZ9WG89VNXN7F0c+6nMEpWgqmPvb7vY7JkB2jyyA==", "dev": true, + "license": "MIT", "dependencies": { "conventional-changelog-angular": "^8.0.0", "conventional-changelog-writer": "^8.0.0", @@ -2794,9 +2636,8 @@ }, "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-changelog-angular": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", - "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", "dev": true, + "license": "ISC", "dependencies": { "compare-func": "^2.0.0" }, @@ -2806,9 +2647,8 @@ }, "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-commits-parser": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.0.0.tgz", - "integrity": "sha512-TbsINLp48XeMXR8EvGjTnKGsZqBemisPoyWESlpRyR8lif0lcwzqz+NMtYSj1ooF/WYjSuu7wX0CtdeeMEQAmA==", "dev": true, + "license": "MIT", "dependencies": { "meow": "^13.0.0" }, @@ -2821,9 +2661,8 @@ }, "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", - "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -2833,9 +2672,8 @@ }, "node_modules/@semantic-release/release-notes-generator/node_modules/meow": { "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -2845,9 +2683,8 @@ }, "node_modules/@sindresorhus/is": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2857,9 +2694,8 @@ }, "node_modules/@sindresorhus/merge-streams": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", - "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -2876,9 +2712,8 @@ }, "node_modules/@ts-morph/common": { "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.21.0.tgz", - "integrity": "sha512-ES110Mmne5Vi4ypUKrtVQfXFDtCsDXiUiGxF6ILVlE90dDD4fdpC1LSjydl/ml7xJWKSDZwUYD2zkOePMSrPBA==", "dev": true, + "license": "MIT", "dependencies": { "fast-glob": "^3.2.12", "minimatch": "^7.4.3", @@ -2888,9 +2723,8 @@ }, "node_modules/@ts-morph/common/node_modules/minimatch": { "version": "7.4.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", - "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -2903,32 +2737,26 @@ }, "node_modules/@tsconfig/node10": { "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@tsconfig/node16": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@tsconfig/node20": { "version": "20.1.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz", - "integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==", "dev": true, "license": "MIT" }, @@ -2956,9 +2784,8 @@ }, "node_modules/@types/conventional-commits-parser": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", - "integrity": "sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -2977,10 +2804,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/json-bigint": { + "version": "1.0.4", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, "license": "MIT" }, @@ -2996,26 +2826,21 @@ }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/semver": { "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/uuid": { "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.16.0.tgz", - "integrity": "sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q==", "dev": true, "license": "MIT", "dependencies": { @@ -3048,8 +2873,6 @@ }, "node_modules/@typescript-eslint/parser": { "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.16.0.tgz", - "integrity": "sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -3077,8 +2900,6 @@ }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.16.0.tgz", - "integrity": "sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg==", "dev": true, "license": "MIT", "dependencies": { @@ -3095,8 +2916,6 @@ }, "node_modules/@typescript-eslint/type-utils": { "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.16.0.tgz", - "integrity": "sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg==", "dev": true, "license": "MIT", "dependencies": { @@ -3123,8 +2942,6 @@ }, "node_modules/@typescript-eslint/types": { "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.16.0.tgz", - "integrity": "sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ==", "dev": true, "license": "MIT", "engines": { @@ -3137,8 +2954,6 @@ }, "node_modules/@typescript-eslint/typescript-estree": { "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.16.0.tgz", - "integrity": "sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -3166,8 +2981,6 @@ }, "node_modules/@typescript-eslint/utils": { "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.16.0.tgz", - "integrity": "sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA==", "dev": true, "license": "MIT", "dependencies": { @@ -3194,8 +3007,6 @@ }, "node_modules/@typescript-eslint/visitor-keys": { "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.16.0.tgz", - "integrity": "sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3212,8 +3023,6 @@ }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3257,9 +3066,8 @@ }, "node_modules/@vitest/coverage-v8/node_modules/istanbul-lib-source-maps": { "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@jridgewell/trace-mapping": "^0.3.23", "debug": "^4.1.1", @@ -3382,8 +3190,6 @@ }, "node_modules/acorn": { "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -3395,8 +3201,6 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3405,18 +3209,16 @@ }, "node_modules/acorn-walk": { "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } }, "node_modules/agent-base": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.3.4" }, @@ -3426,9 +3228,8 @@ }, "node_modules/aggregate-error": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", - "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", "dev": true, + "license": "MIT", "dependencies": { "clean-stack": "^5.2.0", "indent-string": "^5.0.0" @@ -3442,9 +3243,8 @@ }, "node_modules/ajv": { "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -3458,53 +3258,28 @@ }, "node_modules/algorand-msgpack": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/algorand-msgpack/-/algorand-msgpack-1.1.0.tgz", - "integrity": "sha512-08k7pBQnkaUB5p+jL7f1TRaUIlTSDE0cesFu1mD7llLao+1cAhtvvZmGE3OnisTd0xOn118QMw74SRqddqaYvw==", - "dev": true, + "license": "ISC", "engines": { "node": ">= 14" } }, - "node_modules/algosdk": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/algosdk/-/algosdk-3.5.2.tgz", - "integrity": "sha512-frhGtZl1JvfrLRKmMvUm880wj4OiWsWo2FhbreNWh7pdFsKuWPj60fV682wt/CYefLI70iwHavPOwGBkTVt0VA==", - "dev": true, - "license": "MIT", - "dependencies": { - "algorand-msgpack": "^1.1.0", - "hi-base32": "^0.5.1", - "js-sha256": "^0.9.0", - "js-sha3": "^0.8.0", - "js-sha512": "^0.8.0", - "json-bigint": "^1.0.0", - "tweetnacl": "^1.0.3", - "vlq": "^2.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/ansi-sequence-parser": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", - "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -3517,39 +3292,33 @@ }, "node_modules/any-promise": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/arg": { "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/argv-formatter": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", - "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/array-ify": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/arrify": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", - "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -3569,8 +3338,6 @@ }, "node_modules/ast-kit": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.1.3.tgz", - "integrity": "sha512-TH+b3Lv6pUjy/Nu0m6A2JULtdzLpmqF9x1Dhj00ZoEiML8qvVA9j1flkzTKNYgdEhWrjDwtWNpyyCUbfQe514g==", "dev": true, "license": "MIT", "dependencies": { @@ -3605,23 +3372,19 @@ }, "node_modules/astral-regex": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "funding": [ { "type": "github", @@ -3635,19 +3398,18 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/before-after-hook": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", - "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/better-npm-audit": { "version": "3.11.0", - "resolved": "https://registry.npmjs.org/better-npm-audit/-/better-npm-audit-3.11.0.tgz", - "integrity": "sha512-/Pt05DK6HQaRjWDc5McsCkJBZYfhgQGneKnxzPJExtRq38NttO1Hm30m0GVQeZogE94LVNBVrhWwVsoCo+at3g==", "dev": true, + "license": "MIT", "dependencies": { "commander": "^8.0.0", "dayjs": "^1.10.6", @@ -3664,17 +3426,13 @@ }, "node_modules/bignumber.js": { "version": "9.1.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", - "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", - "dev": true, + "license": "MIT", "engines": { "node": "*" } }, "node_modules/birpc": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.6.1.tgz", - "integrity": "sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==", "dev": true, "license": "MIT", "funding": { @@ -3683,14 +3441,11 @@ }, "node_modules/bottleneck": { "version": "2.19.5", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/brace-expansion": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3699,9 +3454,8 @@ }, "node_modules/braces": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -3711,8 +3465,6 @@ }, "node_modules/buffer": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "funding": [ { "type": "github", @@ -3727,6 +3479,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -3734,9 +3487,8 @@ }, "node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -3753,9 +3505,8 @@ }, "node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3769,18 +3520,16 @@ }, "node_modules/char-regex": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/clean-stack": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", - "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", "dev": true, + "license": "MIT", "dependencies": { "escape-string-regexp": "5.0.0" }, @@ -3793,9 +3542,8 @@ }, "node_modules/clean-stack/node_modules/escape-string-regexp": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -3805,9 +3553,8 @@ }, "node_modules/cli-highlight": { "version": "2.1.11", - "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", - "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", "dev": true, + "license": "ISC", "dependencies": { "chalk": "^4.0.0", "highlight.js": "^10.7.1", @@ -3826,9 +3573,8 @@ }, "node_modules/cli-highlight/node_modules/cliui": { "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -3837,15 +3583,13 @@ }, "node_modules/cli-highlight/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cli-highlight/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3857,9 +3601,8 @@ }, "node_modules/cli-highlight/node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3874,9 +3617,8 @@ }, "node_modules/cli-highlight/node_modules/yargs": { "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -3892,18 +3634,16 @@ }, "node_modules/cli-highlight/node_modules/yargs-parser": { "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/cli-table3": { "version": "0.6.4", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.4.tgz", - "integrity": "sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==", "dev": true, + "license": "MIT", "dependencies": { "string-width": "^4.2.0" }, @@ -3916,15 +3656,13 @@ }, "node_modules/cli-table3/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cli-table3/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3936,9 +3674,8 @@ }, "node_modules/cliui": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -3950,15 +3687,13 @@ }, "node_modules/cliui/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cliui/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3970,9 +3705,8 @@ }, "node_modules/cliui/node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3987,15 +3721,13 @@ }, "node_modules/code-block-writer": { "version": "12.0.0", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz", - "integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -4005,24 +3737,21 @@ }, "node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/commander": { "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true, + "license": "MIT", "engines": { "node": ">= 12" } }, "node_modules/compare-func": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", "dev": true, + "license": "MIT", "dependencies": { "array-ify": "^1.0.0", "dot-prop": "^5.1.0" @@ -4030,22 +3759,18 @@ }, "node_modules/compare-versions": { "version": "6.1.1", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", - "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", "dev": true, "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/config-chain": { "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "dev": true, + "license": "MIT", "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" @@ -4053,9 +3778,8 @@ }, "node_modules/conventional-changelog-angular": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", - "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", "dev": true, + "license": "ISC", "dependencies": { "compare-func": "^2.0.0" }, @@ -4065,9 +3789,8 @@ }, "node_modules/conventional-changelog-conventionalcommits": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-8.0.0.tgz", - "integrity": "sha512-eOvlTO6OcySPyyyk8pKz2dP4jjElYunj9hn9/s0OB+gapTO8zwS9UQWrZ1pmF2hFs3vw1xhonOLGcGjy/zgsuA==", "dev": true, + "license": "ISC", "dependencies": { "compare-func": "^2.0.0" }, @@ -4077,9 +3800,8 @@ }, "node_modules/conventional-changelog-writer": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.0.0.tgz", - "integrity": "sha512-TQcoYGRatlAnT2qEWDON/XSfnVG38JzA7E0wcGScu7RElQBkg9WWgZd1peCWFcWDh1xfb2CfsrcvOn1bbSzztA==", "dev": true, + "license": "MIT", "dependencies": { "@types/semver": "^7.5.5", "conventional-commits-filter": "^5.0.0", @@ -4096,9 +3818,8 @@ }, "node_modules/conventional-changelog-writer/node_modules/meow": { "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -4108,18 +3829,16 @@ }, "node_modules/conventional-commits-filter": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", - "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/conventional-commits-parser": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", - "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", "dev": true, + "license": "MIT", "dependencies": { "is-text-path": "^2.0.0", "JSONStream": "^1.3.5", @@ -4135,9 +3854,8 @@ }, "node_modules/convert-hrtime": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", - "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -4147,15 +3865,13 @@ }, "node_modules/core-util-is": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cosmiconfig": { "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, + "license": "MIT", "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -4179,9 +3895,8 @@ }, "node_modules/cosmiconfig-typescript-loader": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.1.0.tgz", - "integrity": "sha512-7PtBB+6FdsOvZyJtlF3hEPpACq7RQX6BVGsgC7/lfVXnKMvNCu/XY3ykreqG5w/rBNdu2z8LCIKoF3kpHHdHlA==", "dev": true, + "license": "MIT", "dependencies": { "jiti": "^1.21.6" }, @@ -4196,9 +3911,8 @@ }, "node_modules/cp-file": { "version": "10.0.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-10.0.0.tgz", - "integrity": "sha512-vy2Vi1r2epK5WqxOLnskeKeZkdZvTKfFZQCplE3XWsP+SUJyd5XAUFC9lFgTjjXJF2GMne/UML14iEmkAaDfFg==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.10", "nested-error-stacks": "^2.1.1", @@ -4213,9 +3927,8 @@ }, "node_modules/cpy": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/cpy/-/cpy-10.1.0.tgz", - "integrity": "sha512-VC2Gs20JcTyeQob6UViBLnyP0bYHkBh6EiKzot9vi2DmeGlFT9Wd7VG3NBrkNx/jYvFBeyDOMMHdHQhbtKLgHQ==", "dev": true, + "license": "MIT", "dependencies": { "arrify": "^3.0.0", "cp-file": "^10.0.0", @@ -4235,9 +3948,8 @@ }, "node_modules/cpy-cli": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cpy-cli/-/cpy-cli-5.0.0.tgz", - "integrity": "sha512-fb+DZYbL9KHc0BC4NYqGRrDIJZPXUmjjtqdw4XRRg8iV8dIfghUX/WiL+q4/B/KFTy3sK6jsbUhBaz0/Hxg7IQ==", "dev": true, + "license": "MIT", "dependencies": { "cpy": "^10.1.0", "meow": "^12.0.1" @@ -4254,9 +3966,8 @@ }, "node_modules/cpy/node_modules/globby": { "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", "dev": true, + "license": "MIT", "dependencies": { "dir-glob": "^3.0.1", "fast-glob": "^3.3.0", @@ -4273,9 +3984,8 @@ }, "node_modules/cpy/node_modules/slash": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -4285,15 +3995,13 @@ }, "node_modules/create-require": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4305,9 +4013,8 @@ }, "node_modules/crypto-random-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^1.0.1" }, @@ -4320,9 +4027,8 @@ }, "node_modules/crypto-random-string/node_modules/type-fest": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -4332,9 +4038,8 @@ }, "node_modules/dargs": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", - "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -4344,14 +4049,11 @@ }, "node_modules/dayjs": { "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { @@ -4368,33 +4070,29 @@ }, "node_modules/deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/deep-is": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/diff": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/dir-glob": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, + "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -4404,9 +4102,8 @@ }, "node_modules/dot-prop": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, + "license": "MIT", "dependencies": { "is-obj": "^2.0.0" }, @@ -4416,9 +4113,8 @@ }, "node_modules/dotenv": { "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -4428,9 +4124,8 @@ }, "node_modules/dotenv-cli": { "version": "7.4.4", - "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.4.4.tgz", - "integrity": "sha512-XkBYCG0tPIes+YZr4SpfFv76SQrV/LeCE8CI7JSEMi3VR9MvTihCGTOtbIexD6i2mXF+6px7trb1imVCXSNMDw==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.6", "dotenv": "^16.3.0", @@ -4443,17 +4138,14 @@ }, "node_modules/dotenv-expand": { "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", - "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=12" } }, "node_modules/dts-resolver": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/dts-resolver/-/dts-resolver-2.1.2.tgz", - "integrity": "sha512-xeXHBQkn2ISSXxbJWD828PFjtyg+/UrMDo7W4Ffcs7+YWCquxU8YjV1KoxuiL+eJ5pg3ll+bC6flVv61L3LKZg==", "dev": true, "license": "MIT", "engines": { @@ -4473,36 +4165,31 @@ }, "node_modules/duplexer2": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "readable-stream": "^2.0.2" } }, "node_modules/eastasianwidth": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/emoji-regex": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/emojilib": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", - "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/env-ci": { "version": "11.0.0", - "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.0.0.tgz", - "integrity": "sha512-apikxMgkipkgTvMdRT9MNqWx5VLOci79F4VBd7Op/7OPjjoanjdAvn6fglMCCEf/1bAh8eOiuEVCUs4V3qP3nQ==", "dev": true, + "license": "MIT", "dependencies": { "execa": "^8.0.0", "java-properties": "^1.0.2" @@ -4513,9 +4200,8 @@ }, "node_modules/env-ci/node_modules/execa": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", @@ -4536,9 +4222,8 @@ }, "node_modules/env-ci/node_modules/get-stream": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -4548,18 +4233,16 @@ }, "node_modules/env-ci/node_modules/human-signals": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=16.17.0" } }, "node_modules/env-ci/node_modules/is-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -4569,9 +4252,8 @@ }, "node_modules/env-ci/node_modules/mimic-fn": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -4581,9 +4263,8 @@ }, "node_modules/env-ci/node_modules/npm-run-path": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^4.0.0" }, @@ -4596,9 +4277,8 @@ }, "node_modules/env-ci/node_modules/onetime": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^4.0.0" }, @@ -4611,9 +4291,8 @@ }, "node_modules/env-ci/node_modules/path-key": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -4623,9 +4302,8 @@ }, "node_modules/env-ci/node_modules/signal-exit": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -4635,9 +4313,8 @@ }, "node_modules/env-ci/node_modules/strip-final-newline": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -4647,18 +4324,16 @@ }, "node_modules/env-paths": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/error-ex": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, + "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } @@ -4713,18 +4388,16 @@ }, "node_modules/escalade": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -4734,8 +4407,6 @@ }, "node_modules/eslint": { "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4795,8 +4466,6 @@ }, "node_modules/eslint-config-prettier": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "license": "MIT", "bin": { @@ -4808,8 +4477,6 @@ }, "node_modules/eslint-scope": { "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4825,9 +4492,8 @@ }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -4837,9 +4503,8 @@ }, "node_modules/eslint/node_modules/ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4853,8 +4518,6 @@ }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -4864,8 +4527,6 @@ }, "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4877,15 +4538,13 @@ }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4895,8 +4554,6 @@ }, "node_modules/espree": { "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4913,8 +4570,6 @@ }, "node_modules/espree/node_modules/eslint-visitor-keys": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4926,9 +4581,8 @@ }, "node_modules/esquery": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -4938,8 +4592,6 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4951,9 +4603,8 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -4970,8 +4621,6 @@ }, "node_modules/esutils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -4990,8 +4639,6 @@ }, "node_modules/fast-content-type-parse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", - "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", "dev": true, "funding": [ { @@ -5002,19 +4649,18 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ] + ], + "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -5028,9 +4674,8 @@ }, "node_modules/fast-glob/node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -5040,30 +4685,26 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fastq": { "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/figures": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", - "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", "dev": true, + "license": "MIT", "dependencies": { "is-unicode-supported": "^2.0.0" }, @@ -5076,8 +4717,6 @@ }, "node_modules/file-entry-cache": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5089,9 +4728,8 @@ }, "node_modules/fill-range": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5101,9 +4739,8 @@ }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -5117,9 +4754,8 @@ }, "node_modules/find-up-simple": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", - "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -5129,9 +4765,8 @@ }, "node_modules/find-versions": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz", - "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==", "dev": true, + "license": "MIT", "dependencies": { "semver-regex": "^4.0.5", "super-regex": "^1.0.0" @@ -5145,8 +4780,6 @@ }, "node_modules/flat-cache": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { @@ -5159,16 +4792,13 @@ }, "node_modules/flatted": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true, "license": "ISC" }, "node_modules/foreground-child": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", "dev": true, + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -5182,9 +4812,8 @@ }, "node_modules/foreground-child/node_modules/signal-exit": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -5194,9 +4823,8 @@ }, "node_modules/from2": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" @@ -5204,9 +4832,8 @@ }, "node_modules/fs-extra": { "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -5233,18 +4860,16 @@ }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/function-timeout": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.1.tgz", - "integrity": "sha512-6yPMImFFuaMPNaTMTBuolA8EanHJWF5Vju0NHpObRURT105J6x1Mf2a7J4P7Sqk2xDxv24N5L0RatEhTBhNmdA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -5254,18 +4879,16 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-stream": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -5275,8 +4898,6 @@ }, "node_modules/get-tsconfig": { "version": "4.13.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", - "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5288,9 +4909,8 @@ }, "node_modules/git-log-parser": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", - "integrity": "sha512-rnCVNfkTL8tdNryFuaY0fYiBWEBcgF748O6ZI61rslBvr2o7U65c2/6npCRqH40vuAhtgtDiqLTJjBVdrejCzA==", "dev": true, + "license": "MIT", "dependencies": { "argv-formatter": "~1.0.0", "spawn-error-forwarder": "~1.0.0", @@ -5302,18 +4922,16 @@ }, "node_modules/git-log-parser/node_modules/split2": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", - "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", "dev": true, + "license": "ISC", "dependencies": { "through2": "~2.0.0" } }, "node_modules/git-log-parser/node_modules/through2": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, + "license": "MIT", "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" @@ -5321,9 +4939,8 @@ }, "node_modules/git-raw-commits": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", - "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", "dev": true, + "license": "MIT", "dependencies": { "dargs": "^8.0.0", "meow": "^12.0.1", @@ -5338,9 +4955,8 @@ }, "node_modules/glob": { "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -5358,9 +4974,8 @@ }, "node_modules/glob-parent": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -5370,9 +4985,8 @@ }, "node_modules/global-directory": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", - "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", "dev": true, + "license": "MIT", "dependencies": { "ini": "4.1.1" }, @@ -5385,17 +4999,14 @@ }, "node_modules/global-directory/node_modules/ini": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", "dev": true, + "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/globals": { "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", "engines": { @@ -5407,9 +5018,8 @@ }, "node_modules/globby": { "version": "14.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", - "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", "dev": true, + "license": "MIT", "dependencies": { "@sindresorhus/merge-streams": "^2.1.0", "fast-glob": "^3.3.2", @@ -5427,9 +5037,8 @@ }, "node_modules/globby/node_modules/path-type": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", - "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -5439,9 +5048,8 @@ }, "node_modules/globby/node_modules/slash": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -5451,21 +5059,18 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/graphemer": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/handlebars": { "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, + "license": "MIT", "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", @@ -5484,27 +5089,24 @@ }, "node_modules/handlebars/node_modules/source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -5514,24 +5116,20 @@ }, "node_modules/hi-base32": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz", - "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==", - "dev": true + "license": "MIT" }, "node_modules/highlight.js": { "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": "*" } }, "node_modules/hook-std": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", - "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -5548,9 +5146,8 @@ }, "node_modules/http-proxy-agent": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -5561,9 +5158,8 @@ }, "node_modules/https-proxy-agent": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "4" @@ -5574,8 +5170,6 @@ }, "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "funding": [ { "type": "github", @@ -5589,22 +5183,21 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/ignore": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/import-fresh": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -5618,18 +5211,16 @@ }, "node_modules/import-fresh/node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/import-from-esm": { "version": "1.3.4", - "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-1.3.4.tgz", - "integrity": "sha512-7EyUlPFC0HOlBDpUFGfYstsU7XHxZJKAAMzCT8wZ0hMW7b+hG51LIKTDcsgtz8Pu6YC0HqRVbX+rVUtsGMUKvg==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.3.4", "import-meta-resolve": "^4.0.0" @@ -5640,9 +5231,8 @@ }, "node_modules/import-meta-resolve": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", - "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", "dev": true, + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -5650,18 +5240,16 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/indent-string": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -5671,9 +5259,8 @@ }, "node_modules/index-to-position": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz", - "integrity": "sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -5683,21 +5270,18 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/ini": { "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/into-stream": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", - "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", "dev": true, + "license": "MIT", "dependencies": { "from2": "^2.3.0", "p-is-promise": "^3.0.0" @@ -5711,15 +5295,13 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-core-module": { "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, + "license": "MIT", "dependencies": { "hasown": "^2.0.0" }, @@ -5729,27 +5311,24 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -5759,27 +5338,24 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-obj": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-plain-obj": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -5789,9 +5365,8 @@ }, "node_modules/is-text-path": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", - "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", "dev": true, + "license": "MIT", "dependencies": { "text-extensions": "^2.0.0" }, @@ -5801,9 +5376,8 @@ }, "node_modules/is-unicode-supported": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz", - "integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -5813,21 +5387,18 @@ }, "node_modules/isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/issue-parser": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.1.tgz", - "integrity": "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==", "dev": true, + "license": "MIT", "dependencies": { "lodash.capitalize": "^4.2.1", "lodash.escaperegexp": "^4.1.2", @@ -5841,9 +5412,8 @@ }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=8" } @@ -5879,9 +5449,8 @@ }, "node_modules/jackspeak": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -5894,51 +5463,33 @@ }, "node_modules/java-properties": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", - "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6.0" } }, "node_modules/jiti": { "version": "1.21.6", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", - "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "dev": true, + "license": "MIT", "bin": { "jiti": "bin/jiti.js" } }, - "node_modules/js-sha256": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", - "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==", - "dev": true - }, - "node_modules/js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", - "dev": true - }, "node_modules/js-sha512": { "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", - "integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ==", - "dev": true + "license": "MIT" }, "node_modules/js-tokens": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -5948,8 +5499,6 @@ }, "node_modules/jsesc": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", "bin": { @@ -5961,55 +5510,45 @@ }, "node_modules/json-bigint": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "dev": true, + "license": "MIT", "dependencies": { "bignumber.js": "^9.0.0" } }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-parse-better-errors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jsonc-parser": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -6019,18 +5558,16 @@ }, "node_modules/jsonparse": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true, "engines": [ "node >= 0.2.0" - ] + ], + "license": "MIT" }, "node_modules/JSONStream": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, + "license": "(MIT OR Apache-2.0)", "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" @@ -6044,9 +5581,8 @@ }, "node_modules/junk": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/junk/-/junk-4.0.1.tgz", - "integrity": "sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.20" }, @@ -6056,8 +5592,6 @@ }, "node_modules/keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { @@ -6066,9 +5600,8 @@ }, "node_modules/levn": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -6079,15 +5612,13 @@ }, "node_modules/lines-and-columns": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/load-json-file": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", @@ -6100,9 +5631,8 @@ }, "node_modules/load-json-file/node_modules/parse-json": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, + "license": "MIT", "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" @@ -6113,18 +5643,16 @@ }, "node_modules/load-json-file/node_modules/strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -6137,110 +5665,91 @@ }, "node_modules/lodash-es": { "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.camelcase": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.capitalize": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", - "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.get": { "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.kebabcase": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.mergewith": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.snakecase": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.startcase": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.truncate": { "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.uniq": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.uniqby": { "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", - "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.upperfirst": { "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lunr": { "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/magic-string": { "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6249,9 +5758,8 @@ }, "node_modules/magicast": { "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", @@ -6276,15 +5784,13 @@ }, "node_modules/make-error": { "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/marked": { "version": "12.0.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.1.tgz", - "integrity": "sha512-Y1/V2yafOcOdWQCX0XpAKXzDakPOpn6U0YLxTJs3cww6VxOzZV1BTOOYWLvH3gX38cq+iLwljHHTnMtlDfg01Q==", "dev": true, + "license": "MIT", "bin": { "marked": "bin/marked.js" }, @@ -6294,9 +5800,8 @@ }, "node_modules/marked-terminal": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.0.0.tgz", - "integrity": "sha512-sNEx8nn9Ktcm6pL0TnRz8tnXq/mSS0Q1FRSwJOAqw4lAB4l49UeDf85Gm1n9RPFm5qurCPjwi1StAQT2XExhZw==", "dev": true, + "license": "MIT", "dependencies": { "ansi-escapes": "^6.2.0", "chalk": "^5.3.0", @@ -6314,9 +5819,8 @@ }, "node_modules/marked-terminal/node_modules/ansi-escapes": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", - "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -6326,9 +5830,8 @@ }, "node_modules/marked-terminal/node_modules/chalk": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -6338,8 +5841,6 @@ }, "node_modules/memorystream": { "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true, "engines": { "node": ">= 0.10.0" @@ -6347,9 +5848,8 @@ }, "node_modules/meow": { "version": "12.1.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", - "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", "dev": true, + "license": "MIT", "engines": { "node": ">=16.10" }, @@ -6359,24 +5859,21 @@ }, "node_modules/merge-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromatch": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -6387,12 +5884,11 @@ }, "node_modules/mime": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", - "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", "dev": true, "funding": [ "https://github.com/sponsors/broofa" ], + "license": "MIT", "bin": { "mime": "bin/cli.js" }, @@ -6402,9 +5898,8 @@ }, "node_modules/minimatch": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -6417,27 +5912,24 @@ }, "node_modules/minimist": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/mkdirp": { "version": "2.1.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", - "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", "dev": true, + "license": "MIT", "bin": { "mkdirp": "dist/cjs/src/bin.js" }, @@ -6450,15 +5942,13 @@ }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/mz": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "dev": true, + "license": "MIT", "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -6486,33 +5976,28 @@ }, "node_modules/natural-compare": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/neo-async": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/nerf-dart": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", - "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/nested-error-stacks": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz", - "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-emoji": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", - "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", "dev": true, + "license": "MIT", "dependencies": { "@sindresorhus/is": "^4.6.0", "char-regex": "^1.0.2", @@ -6525,9 +6010,8 @@ }, "node_modules/node-fetch": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, + "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -6545,18 +6029,16 @@ }, "node_modules/normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/normalize-url": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", - "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -6566,8 +6048,6 @@ }, "node_modules/npm": { "version": "10.9.3", - "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.3.tgz", - "integrity": "sha512-6Eh1u5Q+kIVXeA8e7l2c/HpnFFcwrkt37xDMujD5be1gloWa9p6j3Fsv3mByXXmqJHy+2cElRMML8opNT7xIJQ==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -6727,8 +6207,6 @@ }, "node_modules/npm-normalize-package-bin": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", - "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", "dev": true, "license": "ISC", "engines": { @@ -6737,8 +6215,6 @@ }, "node_modules/npm-run-all2": { "version": "8.0.4", - "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-8.0.4.tgz", - "integrity": "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==", "dev": true, "license": "MIT", "dependencies": { @@ -6764,8 +6240,6 @@ }, "node_modules/npm-run-all2/node_modules/ansi-styles": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", "engines": { @@ -6777,8 +6251,6 @@ }, "node_modules/npm-run-all2/node_modules/isexe": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, "license": "ISC", "engines": { @@ -6787,8 +6259,6 @@ }, "node_modules/npm-run-all2/node_modules/picomatch": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", "engines": { @@ -6800,8 +6270,6 @@ }, "node_modules/npm-run-all2/node_modules/which": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", - "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, "license": "ISC", "dependencies": { @@ -9338,18 +8806,16 @@ }, "node_modules/object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/optionator": { "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, + "license": "MIT", "dependencies": { "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", @@ -9364,9 +8830,8 @@ }, "node_modules/p-each-series": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", - "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -9376,9 +8841,8 @@ }, "node_modules/p-event": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/p-event/-/p-event-5.0.1.tgz", - "integrity": "sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==", "dev": true, + "license": "MIT", "dependencies": { "p-timeout": "^5.0.2" }, @@ -9391,9 +8855,8 @@ }, "node_modules/p-filter": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-3.0.0.tgz", - "integrity": "sha512-QtoWLjXAW++uTX67HZQz1dbTpqBfiidsB6VtQUC9iR85S120+s0T5sO6s+B5MLzFcZkrEd/DGMmCjR+f2Qpxwg==", "dev": true, + "license": "MIT", "dependencies": { "p-map": "^5.1.0" }, @@ -9406,9 +8869,8 @@ }, "node_modules/p-filter/node_modules/aggregate-error": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", - "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", "dev": true, + "license": "MIT", "dependencies": { "clean-stack": "^4.0.0", "indent-string": "^5.0.0" @@ -9422,9 +8884,8 @@ }, "node_modules/p-filter/node_modules/clean-stack": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", - "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", "dev": true, + "license": "MIT", "dependencies": { "escape-string-regexp": "5.0.0" }, @@ -9437,9 +8898,8 @@ }, "node_modules/p-filter/node_modules/escape-string-regexp": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -9449,9 +8909,8 @@ }, "node_modules/p-filter/node_modules/p-map": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", - "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", "dev": true, + "license": "MIT", "dependencies": { "aggregate-error": "^4.0.0" }, @@ -9464,18 +8923,16 @@ }, "node_modules/p-is-promise": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", - "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -9488,9 +8945,8 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -9503,9 +8959,8 @@ }, "node_modules/p-map": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-6.0.0.tgz", - "integrity": "sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -9515,9 +8970,8 @@ }, "node_modules/p-reduce": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", - "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -9527,9 +8981,8 @@ }, "node_modules/p-timeout": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz", - "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -9539,15 +8992,13 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true + "dev": true, + "license": "BlueOak-1.0.0" }, "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -9557,9 +9008,8 @@ }, "node_modules/parse-json": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -9575,9 +9025,8 @@ }, "node_modules/parse-ms": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", - "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -9587,54 +9036,47 @@ }, "node_modules/parse5": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/parse5-htmlparser2-tree-adapter": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", "dev": true, + "license": "MIT", "dependencies": { "parse5": "^6.0.1" } }, "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-browserify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-scurry": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -9648,15 +9090,13 @@ }, "node_modules/path-scurry/node_modules/lru-cache": { "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/path-type": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -9670,15 +9110,13 @@ }, "node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -9688,8 +9126,6 @@ }, "node_modules/pidtree": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "dev": true, "license": "MIT", "bin": { @@ -9701,18 +9137,16 @@ }, "node_modules/pify": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/pkg-conf": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^2.0.0", "load-json-file": "^4.0.0" @@ -9723,9 +9157,8 @@ }, "node_modules/pkg-conf/node_modules/find-up": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^2.0.0" }, @@ -9735,9 +9168,8 @@ }, "node_modules/pkg-conf/node_modules/locate-path": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" @@ -9748,9 +9180,8 @@ }, "node_modules/pkg-conf/node_modules/p-limit": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^1.0.0" }, @@ -9760,9 +9191,8 @@ }, "node_modules/pkg-conf/node_modules/p-locate": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^1.1.0" }, @@ -9772,26 +9202,22 @@ }, "node_modules/pkg-conf/node_modules/p-try": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/pkg-conf/node_modules/path-exists": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/playwright": { "version": "1.56.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz", - "integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -9809,8 +9235,6 @@ }, "node_modules/playwright-core": { "version": "1.56.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz", - "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -9837,9 +9261,8 @@ }, "node_modules/polytype": { "version": "0.17.0", - "resolved": "https://registry.npmjs.org/polytype/-/polytype-0.17.0.tgz", - "integrity": "sha512-Q1y07MZqHPlGRJs8qI8bnxrQs+W3r24v25cmhbQV/lC9VNNtd+smi/2m3CUHNBDTfLtl+6SpA0EsL/J1oVsEag==", "dev": true, + "license": "ISC", "engines": { "node": ">=16.0.0" } @@ -9875,18 +9298,16 @@ }, "node_modules/prelude-ls": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/prettier": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -9899,9 +9320,8 @@ }, "node_modules/pretty-ms": { "version": "9.2.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", - "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", "dev": true, + "license": "MIT", "dependencies": { "parse-ms": "^4.0.0" }, @@ -9914,29 +9334,24 @@ }, "node_modules/process-nextick-args": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/proto-list": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -9951,13 +9366,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/rc": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -9970,17 +9385,14 @@ }, "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/read-package-json-fast": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", - "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", "dev": true, "license": "ISC", "dependencies": { @@ -9993,8 +9405,6 @@ }, "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", - "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", "dev": true, "license": "MIT", "engines": { @@ -10003,9 +9413,8 @@ }, "node_modules/read-package-up": { "version": "11.0.0", - "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", - "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", "dev": true, + "license": "MIT", "dependencies": { "find-up-simple": "^1.0.0", "read-pkg": "^9.0.0", @@ -10020,9 +9429,8 @@ }, "node_modules/read-package-up/node_modules/hosted-git-info": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", - "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^10.0.1" }, @@ -10032,18 +9440,16 @@ }, "node_modules/read-package-up/node_modules/lru-cache": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, + "license": "ISC", "engines": { "node": "14 || >=16.14" } }, "node_modules/read-package-up/node_modules/normalize-package-data": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", - "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^7.0.0", "is-core-module": "^2.8.1", @@ -10056,9 +9462,8 @@ }, "node_modules/read-package-up/node_modules/parse-json": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", - "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.22.13", "index-to-position": "^0.1.2", @@ -10073,9 +9478,8 @@ }, "node_modules/read-package-up/node_modules/read-pkg": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", - "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", "dev": true, + "license": "MIT", "dependencies": { "@types/normalize-package-data": "^2.4.3", "normalize-package-data": "^6.0.0", @@ -10092,9 +9496,8 @@ }, "node_modules/read-package-up/node_modules/type-fest": { "version": "4.15.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz", - "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=16" }, @@ -10104,9 +9507,8 @@ }, "node_modules/readable-stream": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -10119,9 +9521,8 @@ }, "node_modules/registry-auth-token": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", - "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", "dev": true, + "license": "MIT", "dependencies": { "@pnpm/npm-conf": "^2.1.0" }, @@ -10131,9 +9532,8 @@ }, "node_modules/replace-in-files-cli": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/replace-in-files-cli/-/replace-in-files-cli-3.0.0.tgz", - "integrity": "sha512-A2VjOaPF8yjiaRjuIlvX3PB0uRMQ3DpnKG4yg38wjPrqR0OSzD00ubOZqqwAunoT5emKjEZPvdkm6JRVJrBmlQ==", "dev": true, + "license": "MIT", "dependencies": { "escape-string-regexp": "^5.0.0", "globby": "^14.0.1", @@ -10153,9 +9553,8 @@ }, "node_modules/replace-in-files-cli/node_modules/escape-string-regexp": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -10165,9 +9564,8 @@ }, "node_modules/replace-in-files-cli/node_modules/meow": { "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -10177,9 +9575,8 @@ }, "node_modules/replace-in-files-cli/node_modules/signal-exit": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -10189,9 +9586,8 @@ }, "node_modules/replace-in-files-cli/node_modules/write-file-atomic": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, + "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" @@ -10202,35 +9598,30 @@ }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/require-from-string": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/resolve-from": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", "funding": { @@ -10239,9 +9630,8 @@ }, "node_modules/reusify": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -10249,9 +9639,8 @@ }, "node_modules/rimraf": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", - "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", "dev": true, + "license": "ISC", "dependencies": { "glob": "^11.0.0", "package-json-from-dist": "^1.0.0" @@ -10268,9 +9657,8 @@ }, "node_modules/rimraf/node_modules/glob": { "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^4.0.1", @@ -10291,9 +9679,8 @@ }, "node_modules/rimraf/node_modules/jackspeak": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -10306,18 +9693,16 @@ }, "node_modules/rimraf/node_modules/lru-cache": { "version": "11.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", - "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", "dev": true, + "license": "ISC", "engines": { "node": "20 || >=22" } }, "node_modules/rimraf/node_modules/minimatch": { "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -10330,9 +9715,8 @@ }, "node_modules/rimraf/node_modules/path-scurry": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" @@ -10378,9 +9762,9 @@ } }, "node_modules/rolldown-plugin-dts": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.17.2.tgz", - "integrity": "sha512-tbLm7FoDvZAhAY33wJbq0ACw+srToKZ5xFqwn/K4tayGloZPXQHyOEPEYi7whEfTCaMndZWaho9+oiQTlwIe6Q==", + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.17.3.tgz", + "integrity": "sha512-8mGnNUVNrqEdTnrlcaDxs4sAZg0No6njO+FuhQd4L56nUbJO1tHxOoKDH3mmMJg7f/BhEj/1KjU5W9kZ9zM/kQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10466,8 +9850,6 @@ }, "node_modules/run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -10483,21 +9865,20 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/semantic-release": { "version": "24.2.0", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.0.tgz", - "integrity": "sha512-fQfn6e/aYToRtVJYKqneFM1Rg3KP2gh3wSWtpYsLlz6uaPKlISrTzvYAFn+mYWo07F0X1Cz5ucU89AVE8X1mbg==", "dev": true, + "license": "MIT", "dependencies": { "@semantic-release/commit-analyzer": "^13.0.0-beta.1", "@semantic-release/error": "^4.0.0", @@ -10538,9 +9919,8 @@ }, "node_modules/semantic-release/node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", - "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -10550,9 +9930,8 @@ }, "node_modules/semantic-release/node_modules/execa": { "version": "9.5.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.1.tgz", - "integrity": "sha512-QY5PPtSonnGwhhHDNI7+3RvY285c7iuJFFB+lU+oEzMY/gEGJ808owqJsrr8Otd1E/x07po1LkUBmdAc5duPAg==", "dev": true, + "license": "MIT", "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.3", @@ -10576,9 +9955,8 @@ }, "node_modules/semantic-release/node_modules/execa/node_modules/get-stream": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", - "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", "dev": true, + "license": "MIT", "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" @@ -10592,9 +9970,8 @@ }, "node_modules/semantic-release/node_modules/hosted-git-info": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.0.2.tgz", - "integrity": "sha512-sYKnA7eGln5ov8T8gnYlkSOxFJvywzEx9BueN6xo/GKO8PGiI6uK6xx+DIGe45T3bdVjLAQDQW1aicT8z8JwQg==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^10.0.1" }, @@ -10604,18 +9981,16 @@ }, "node_modules/semantic-release/node_modules/human-signals": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", - "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18.0" } }, "node_modules/semantic-release/node_modules/is-stream": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", - "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -10625,15 +10000,13 @@ }, "node_modules/semantic-release/node_modules/lru-cache": { "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/semantic-release/node_modules/npm-run-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", - "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" @@ -10647,9 +10020,8 @@ }, "node_modules/semantic-release/node_modules/path-key": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -10659,9 +10031,8 @@ }, "node_modules/semantic-release/node_modules/signal-exit": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -10671,9 +10042,8 @@ }, "node_modules/semantic-release/node_modules/strip-final-newline": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", - "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -10683,9 +10053,8 @@ }, "node_modules/semantic-release/node_modules/unicorn-magic": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", - "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -10695,8 +10064,6 @@ }, "node_modules/semver": { "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -10708,9 +10075,8 @@ }, "node_modules/semver-diff": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^7.3.5" }, @@ -10723,9 +10089,8 @@ }, "node_modules/semver-regex": { "version": "4.0.5", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", - "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -10735,9 +10100,8 @@ }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -10747,17 +10111,14 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/shell-quote": { "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "dev": true, "license": "MIT", "engines": { @@ -10769,9 +10130,8 @@ }, "node_modules/shiki": { "version": "0.14.7", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", - "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-sequence-parser": "^1.1.0", "jsonc-parser": "^3.2.0", @@ -10781,15 +10141,13 @@ }, "node_modules/siginfo": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/signale": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", - "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^2.3.2", "figures": "^2.0.0", @@ -10801,9 +10159,8 @@ }, "node_modules/signale/node_modules/ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -10813,9 +10170,8 @@ }, "node_modules/signale/node_modules/chalk": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -10827,33 +10183,29 @@ }, "node_modules/signale/node_modules/color-convert": { "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, "node_modules/signale/node_modules/color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/signale/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/signale/node_modules/figures": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", "dev": true, + "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.5" }, @@ -10863,18 +10215,16 @@ }, "node_modules/signale/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/signale/node_modules/supports-color": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -10884,9 +10234,8 @@ }, "node_modules/skin-tone": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", - "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", "dev": true, + "license": "MIT", "dependencies": { "unicode-emoji-modifier-base": "^1.0.0" }, @@ -10896,9 +10245,8 @@ }, "node_modules/slice-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -10913,33 +10261,29 @@ }, "node_modules/source-map": { "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">= 8" } }, "node_modules/source-map-js": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/spawn-error-forwarder": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", - "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/spdx-correct": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -10947,15 +10291,13 @@ }, "node_modules/spdx-exceptions": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true + "dev": true, + "license": "CC-BY-3.0" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, + "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -10963,24 +10305,21 @@ }, "node_modules/spdx-license-ids": { "version": "3.0.17", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", - "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/split2": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "dev": true, + "license": "ISC", "engines": { "node": ">= 10.x" } }, "node_modules/stackback": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/std-env": { "version": "3.10.0", @@ -10991,9 +10330,8 @@ }, "node_modules/stream-combiner2": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", "dev": true, + "license": "MIT", "dependencies": { "duplexer2": "~0.1.0", "readable-stream": "^2.0.2" @@ -11001,18 +10339,16 @@ }, "node_modules/string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" } }, "node_modules/string-width": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -11028,9 +10364,8 @@ "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -11042,15 +10377,13 @@ }, "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/string-width/node_modules/ansi-regex": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -11060,9 +10393,8 @@ }, "node_modules/string-width/node_modules/strip-ansi": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -11075,9 +10407,8 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -11088,9 +10419,8 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -11100,8 +10430,6 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { @@ -11113,9 +10441,8 @@ }, "node_modules/super-regex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.0.0.tgz", - "integrity": "sha512-CY8u7DtbvucKuquCmOFEKhr9Besln7n9uN8eFbwcoGYWXOMW07u2o8njWaiXt11ylS3qoGF55pILjRmPlbodyg==", "dev": true, + "license": "MIT", "dependencies": { "function-timeout": "^1.0.1", "time-span": "^5.1.0" @@ -11129,9 +10456,8 @@ }, "node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -11141,9 +10467,8 @@ }, "node_modules/supports-hyperlinks": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", - "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" @@ -11154,9 +10479,8 @@ }, "node_modules/table": { "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "ajv": "^8.0.1", "lodash.truncate": "^4.4.2", @@ -11170,15 +10494,13 @@ }, "node_modules/table/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/table/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -11190,18 +10512,16 @@ }, "node_modules/temp-dir": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", - "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" } }, "node_modules/tempy": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", - "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", "dev": true, + "license": "MIT", "dependencies": { "is-stream": "^3.0.0", "temp-dir": "^3.0.0", @@ -11217,9 +10537,8 @@ }, "node_modules/tempy/node_modules/is-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -11229,9 +10548,8 @@ }, "node_modules/tempy/node_modules/type-fest": { "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=12.20" }, @@ -11241,9 +10559,8 @@ }, "node_modules/text-extensions": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", - "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -11253,18 +10570,16 @@ }, "node_modules/thenify": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "dev": true, + "license": "MIT", "dependencies": { "any-promise": "^1.0.0" } }, "node_modules/thenify-all": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "dev": true, + "license": "MIT", "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -11274,15 +10589,13 @@ }, "node_modules/through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/time-span": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", - "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", "dev": true, + "license": "MIT", "dependencies": { "convert-hrtime": "^5.0.0" }, @@ -11295,15 +10608,13 @@ }, "node_modules/tiny-invariant": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/tinybench": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/tinyexec": { "version": "0.3.2", @@ -11372,9 +10683,8 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -11384,15 +10694,13 @@ }, "node_modules/tr46": { "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/traverse": { "version": "0.6.8", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", - "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11402,9 +10710,8 @@ }, "node_modules/ts-api-utils": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.1.tgz", - "integrity": "sha512-5RU2/lxTA3YUZxju61HO2U6EoZLvBLtmV2mbTvqyu4a/7s7RmJPT+1YekhMVsQhznRWk/czIwDUg+V8Q9ZuG4w==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -11414,9 +10721,8 @@ }, "node_modules/ts-morph": { "version": "20.0.0", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-20.0.0.tgz", - "integrity": "sha512-JVmEJy2Wow5n/84I3igthL9sudQ8qzjh/6i4tmYCm6IqYyKFlNbJZi7oBdjyqcWSWYRu3CtL0xbT6fS03ESZIg==", "dev": true, + "license": "MIT", "dependencies": { "@ts-morph/common": "~0.21.0", "code-block-writer": "^12.0.0" @@ -11424,9 +10730,8 @@ }, "node_modules/ts-node": { "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, + "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -11466,23 +10771,21 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, + "license": "0BSD", "optional": true }, "node_modules/tweetnacl": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "dev": true + "license": "Unlicense" }, "node_modules/type-check": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -11492,9 +10795,8 @@ }, "node_modules/typedoc": { "version": "0.25.13", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.13.tgz", - "integrity": "sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "lunr": "^2.3.9", "marked": "^4.3.0", @@ -11513,9 +10815,8 @@ }, "node_modules/typedoc-plugin-markdown": { "version": "3.17.1", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz", - "integrity": "sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw==", "dev": true, + "license": "MIT", "dependencies": { "handlebars": "^4.7.7" }, @@ -11525,9 +10826,8 @@ }, "node_modules/typedoc/node_modules/marked": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, + "license": "MIT", "bin": { "marked": "bin/marked.js" }, @@ -11537,9 +10837,8 @@ }, "node_modules/typescript": { "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11550,8 +10849,6 @@ }, "node_modules/typescript-eslint": { "version": "8.16.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.16.0.tgz", - "integrity": "sha512-wDkVmlY6O2do4V+lZd0GtRfbtXbeD0q9WygwXXSJnC1xorE8eqyC2L1tJimqpSeFrOzRlYtWnUp/uzgHQOgfBQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11577,9 +10874,8 @@ }, "node_modules/uglify-js": { "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "dev": true, + "license": "BSD-2-Clause", "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -11597,18 +10893,16 @@ }, "node_modules/unicode-emoji-modifier-base": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", - "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/unicorn-magic": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", - "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -11618,9 +10912,8 @@ }, "node_modules/unique-string": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", "dev": true, + "license": "MIT", "dependencies": { "crypto-random-string": "^4.0.0" }, @@ -11633,67 +10926,59 @@ }, "node_modules/universal-user-agent": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", - "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } }, "node_modules/uri-js": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, "node_modules/url-join": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", - "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/uuid": { "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/validate-npm-package-license": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, + "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -11911,33 +11196,27 @@ }, "node_modules/vlq": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-2.0.4.tgz", - "integrity": "sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA==", - "dev": true + "license": "MIT" }, "node_modules/vscode-oniguruma": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/vscode-textmate": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/webidl-conversions": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/whatwg-url": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, + "license": "MIT", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -11945,9 +11224,8 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -11960,9 +11238,8 @@ }, "node_modules/why-is-node-running": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, + "license": "MIT", "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" @@ -11976,15 +11253,13 @@ }, "node_modules/wordwrap": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/wrap-ansi": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -12000,9 +11275,8 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -12017,15 +11291,13 @@ }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -12037,9 +11309,8 @@ }, "node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -12049,9 +11320,8 @@ }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -12061,9 +11331,8 @@ }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -12076,27 +11345,24 @@ }, "node_modules/xtend": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4" } }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -12112,24 +11378,21 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } }, "node_modules/yargs/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/yargs/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -12141,18 +11404,16 @@ }, "node_modules/yn": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -12162,9 +11423,8 @@ }, "node_modules/yoctocolors": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", - "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -12176,9 +11436,7 @@ "name": "@algorandfoundation/algokit-abi", "version": "0.1.0", "license": "MIT", - "devDependencies": { - "@algorandfoundation/algokit-common": "*" - }, + "devDependencies": {}, "engines": { "node": ">=20.0" } @@ -12187,16 +11445,11 @@ "name": "@algorandfoundation/algokit-algod-client", "version": "0.1.0", "license": "MIT", - "devDependencies": { - "@algorandfoundation/algokit-transact": "*" - }, + "devDependencies": {}, "engines": { "node": ">=20.0" } }, - "packages/algokit_transact/dist": { - "dev": true - }, "packages/common": { "name": "@algorandfoundation/algokit-common", "version": "0.1.0", @@ -12210,9 +11463,7 @@ "name": "@algorandfoundation/algokit-indexer-client", "version": "0.1.0", "license": "MIT", - "devDependencies": { - "@algorandfoundation/algokit-transact": "*" - }, + "devDependencies": {}, "engines": { "node": ">=20.0" } @@ -12221,23 +11472,25 @@ "name": "@algorandfoundation/algokit-kmd-client", "version": "0.1.0", "license": "MIT", - "devDependencies": { - "@algorandfoundation/algokit-transact": "*" - }, + "devDependencies": {}, "engines": { "node": ">=20.0" } }, - "packages/kmd_client/node_modules/@algorandfoundation/algokit-transact": { - "resolved": "packages/algokit_transact/dist", - "link": true + "packages/sdk": { + "name": "@algorandfoundation/sdk", + "version": "0.1.0", + "license": "MIT", + "devDependencies": {}, + "engines": { + "node": ">=20.0" + } }, "packages/transact": { "name": "@algorandfoundation/algokit-transact", "version": "0.1.0", "license": "MIT", "devDependencies": { - "@algorandfoundation/algokit-common": "*", "@noble/ed25519": "^3.0.0" }, "engines": { diff --git a/package.json b/package.json index 2582d7ad..2e836d96 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "scripts": { "build": "run-s build:*", "build:0-clean": "rimraf dist coverage", - "build:1-compile": "rolldown -c", + "build:2-compile": "rolldown -c", "build:3-copy-pkg-json": "tstk copy-package-json -c", "build:4-copy-readme": "cpy README.md LICENSE dist", "build:5-fix-readme-links": "replace-in-files --string '(./' --replacement '(https://github.com/algorandfoundation/algokit-utils-ts/blob/main/' dist/README.md", @@ -48,7 +48,7 @@ "test:watch": "vitest watch --coverage --passWithNoTests", "lint": "eslint ./src/ && npm run lint --workspaces --if-present", "lint:fix": "eslint ./src/ --fix && npm run lint --workspaces --if-present", - "check-types": "tsc --noEmit", + "check-types": "tsc --project tsconfig.typecheck.json --noEmit", "audit": "better-npm-audit audit", "format": "prettier --write . && npm run format --workspaces --if-present", "commit-lint": "commitlint --edit -o", @@ -60,18 +60,15 @@ "esbuild": "0.25.0" }, "dependencies": { - "buffer": "^6.0.3" - }, - "peerDependencies": { - "algosdk": "^3.5.2" + "algorand-msgpack": "^1.1.0", + "buffer": "^6.0.3", + "hi-base32": "^0.5.1", + "js-sha512": "^0.8.0", + "json-bigint": "^1.0.0", + "tweetnacl": "^1.0.3", + "vlq": "^2.0.4" }, "devDependencies": { - "@algorandfoundation/algokit-algod-client": "*", - "@algorandfoundation/algokit-abi": "*", - "@algorandfoundation/algokit-common": "*", - "@algorandfoundation/algokit-transact": "*", - "@algorandfoundation/algokit-indexer-client": "*", - "@algorandfoundation/algokit-kmd-client": "*", "@algorandfoundation/tealscript": "^0.106.3", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", @@ -79,10 +76,10 @@ "@makerx/prettier-config": "^2.0.0", "@makerx/ts-toolkit": "4.0.0-beta.24", "@tsconfig/node20": "^20.1.4", + "@types/json-bigint": "^1.0.4", "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^8.8.1", "@vitest/coverage-v8": "^4.0.5", - "algosdk": "^3.5.2", "better-npm-audit": "^3.11.0", "conventional-changelog-conventionalcommits": "8.0.0", "cpy-cli": "^5.0.0", @@ -96,7 +93,7 @@ "replace-in-files-cli": "^3.0.0", "rimraf": "^6.0.1", "rolldown": "^1.0.0-beta.45", - "rolldown-plugin-dts": "^0.17.2", + "rolldown-plugin-dts": "^0.17.3", "semantic-release": "^24.1.2", "tiny-invariant": "^1.3.1", "ts-node": "^10.9.1", @@ -175,4 +172,4 @@ "@semantic-release/github" ] } -} +} \ No newline at end of file diff --git a/packages/abi/.tstoolkitrc.ts b/packages/abi/.tstoolkitrc.ts deleted file mode 100644 index f19a3d47..00000000 --- a/packages/abi/.tstoolkitrc.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { TsToolkitConfig } from '@makerx/ts-toolkit' - -const config: TsToolkitConfig = { - packageConfig: { - srcDir: 'src', - outDir: 'dist', - main: 'index.js', - customSections: ['module', 'main', 'type', 'types', 'exports'], - }, -} -export default config diff --git a/packages/abi/package.json b/packages/abi/package.json index 404a7294..554e0f29 100644 --- a/packages/abi/package.json +++ b/packages/abi/package.json @@ -14,13 +14,6 @@ "**/*" ], "scripts": { - "build": "run-s build:*", - "build-watch": "rolldown --watch -c", - "build:0-clean": "rimraf dist coverage", - "build:1-lint": "npm run lint", - "build:2-compile": "rolldown -c", - "build:3-copy-pkg-json": "tstk copy-package-json -c", - "build:4-copy-readme": "cpy README.md dist", "test": "vitest run --coverage --passWithNoTests", "test:watch": "vitest watch --coverage --passWithNoTests", "lint": "eslint ./src/", @@ -32,7 +25,5 @@ }, "dependencies": {}, "peerDependencies": {}, - "devDependencies": { - "@algorandfoundation/algokit-common": "*" - } -} + "devDependencies": {} +} \ No newline at end of file diff --git a/packages/abi/rolldown.config.ts b/packages/abi/rolldown.config.ts deleted file mode 100644 index 0cd70473..00000000 --- a/packages/abi/rolldown.config.ts +++ /dev/null @@ -1,4 +0,0 @@ -import createConfig from '../../rolldown' -import pkg from './package.json' with { type: 'json' } - -export default createConfig([...Object.keys(pkg.dependencies || {})]) diff --git a/packages/abi/tsconfig.build.json b/packages/abi/tsconfig.build.json deleted file mode 100644 index 0e149d39..00000000 --- a/packages/abi/tsconfig.build.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["src/**/*.ts"], - "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "tests/**/*.*"] -} diff --git a/packages/abi/tsconfig.json b/packages/abi/tsconfig.json index 71b6c1f8..079567a1 100644 --- a/packages/abi/tsconfig.json +++ b/packages/abi/tsconfig.json @@ -1,8 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "dist", - "tsBuildInfoFile": "build/.tsbuildinfo" + "outDir": "dist" }, "include": ["src/**/*.ts", "tests/**/*.ts"] } diff --git a/packages/algod_client/.tstoolkitrc.ts b/packages/algod_client/.tstoolkitrc.ts deleted file mode 100644 index f19a3d47..00000000 --- a/packages/algod_client/.tstoolkitrc.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { TsToolkitConfig } from '@makerx/ts-toolkit' - -const config: TsToolkitConfig = { - packageConfig: { - srcDir: 'src', - outDir: 'dist', - main: 'index.js', - customSections: ['module', 'main', 'type', 'types', 'exports'], - }, -} -export default config diff --git a/packages/algod_client/package.json b/packages/algod_client/package.json index 16c3b3f8..5d3ce75f 100644 --- a/packages/algod_client/package.json +++ b/packages/algod_client/package.json @@ -14,13 +14,6 @@ "**/*" ], "scripts": { - "build": "run-s build:*", - "build-watch": "rolldown --watch -c", - "build:0-clean": "rimraf dist coverage", - "build:1-lint": "npm run lint", - "build:2-compile": "rolldown -c", - "build:3-copy-pkg-json": "tstk copy-package-json -c", - "build:4-copy-readme": "cpy README.md dist", "test": "vitest run --coverage --passWithNoTests", "test:watch": "vitest watch --coverage --passWithNoTests", "lint": "eslint ./src/", @@ -32,7 +25,5 @@ }, "dependencies": {}, "peerDependencies": {}, - "devDependencies": { - "@algorandfoundation/algokit-transact": "*" - } -} + "devDependencies": {} +} \ No newline at end of file diff --git a/packages/algod_client/rolldown.config.ts b/packages/algod_client/rolldown.config.ts deleted file mode 100644 index 0cd70473..00000000 --- a/packages/algod_client/rolldown.config.ts +++ /dev/null @@ -1,4 +0,0 @@ -import createConfig from '../../rolldown' -import pkg from './package.json' with { type: 'json' } - -export default createConfig([...Object.keys(pkg.dependencies || {})]) diff --git a/packages/algod_client/src/client.ts b/packages/algod_client/src/client.ts index 3323886b..7267f3d2 100644 --- a/packages/algod_client/src/client.ts +++ b/packages/algod_client/src/client.ts @@ -1,7 +1,7 @@ -import { AlgodApi } from './apis/api.service' -import type { BaseHttpRequest } from './core/base-http-request' import type { ClientConfig } from './core/client-config' +import type { BaseHttpRequest } from './core/base-http-request' import { FetchHttpRequest } from './core/fetch-http-request' +import { AlgodApi } from './apis/api.service' export class AlgodClient extends AlgodApi { constructor(config: ClientConfig, request?: BaseHttpRequest) { diff --git a/packages/algod_client/src/index.ts b/packages/algod_client/src/index.ts index eeced017..915506d5 100644 --- a/packages/algod_client/src/index.ts +++ b/packages/algod_client/src/index.ts @@ -1,12 +1,12 @@ -export * from './core/api-error' -export * from './core/base-http-request' export * from './core/client-config' -export * from './core/codecs' +export * from './core/base-http-request' export * from './core/fetch-http-request' -export * from './core/model-runtime' +export * from './core/api-error' export * from './core/serialization' +export * from './core/codecs' +export * from './core/model-runtime' // Generated +export * from './models' export * from './apis' export * from './client' -export * from './models' diff --git a/packages/algod_client/src/models/block-app-eval-delta.ts b/packages/algod_client/src/models/block-app-eval-delta.ts index ab81df5f..bc3b0d80 100644 --- a/packages/algod_client/src/models/block-app-eval-delta.ts +++ b/packages/algod_client/src/models/block-app-eval-delta.ts @@ -1,8 +1,8 @@ import type { ModelMetadata } from '../core/model-runtime' import { getModelMeta, registerModelMeta } from '../core/model-runtime' +import type { SignedTxnInBlock } from './signed-txn-in-block' import type { BlockStateDelta } from './block-state-delta' import { BlockStateDeltaMeta } from './block-state-delta' -import type { SignedTxnInBlock } from './signed-txn-in-block' /** * State changes from application execution, including inner transactions and logs. diff --git a/packages/algod_client/src/models/block.ts b/packages/algod_client/src/models/block.ts index 10c38662..e64b8a25 100644 --- a/packages/algod_client/src/models/block.ts +++ b/packages/algod_client/src/models/block.ts @@ -1,8 +1,8 @@ import type { ModelMetadata } from '../core/model-runtime' -import type { BlockStateProofTracking } from './block_state_proof_tracking' -import { BlockStateProofTrackingMeta } from './block_state_proof_tracking' import type { SignedTxnInBlock } from './signed-txn-in-block' import { SignedTxnInBlockMeta } from './signed-txn-in-block' +import type { BlockStateProofTracking } from './block_state_proof_tracking' +import { BlockStateProofTrackingMeta } from './block_state_proof_tracking' /** * Block contains the BlockHeader and the list of transactions (Payset). diff --git a/packages/algod_client/src/models/box-reference.ts b/packages/algod_client/src/models/box-reference.ts index ec7514cc..1202e209 100644 --- a/packages/algod_client/src/models/box-reference.ts +++ b/packages/algod_client/src/models/box-reference.ts @@ -24,7 +24,7 @@ export const BoxReferenceMeta: ModelMetadata = { wireKey: 'app', optional: false, nullable: false, - type: { kind: 'scalar' }, + type: { kind: 'scalar', isBigint: true }, // TODO: PD - make change to OAS }, { name: 'name', diff --git a/packages/algod_client/src/models/index.ts b/packages/algod_client/src/models/index.ts index 103cf785..fb0bedf6 100644 --- a/packages/algod_client/src/models/index.ts +++ b/packages/algod_client/src/models/index.ts @@ -1,183 +1,183 @@ -export { AbortCatchupMeta } from './abort-catchup' -export type { AbortCatchup } from './abort-catchup' -export { AccountMeta } from './account' +export type { GenesisAllocation } from './genesis-allocation' +export { GenesisAllocationMeta } from './genesis-allocation' +export type { Genesis } from './genesis' +export { GenesisMeta } from './genesis' +export type { LedgerStateDelta } from './ledger-state-delta' +export { LedgerStateDeltaMeta } from './ledger-state-delta' +export type { LedgerStateDeltaForTransactionGroup } from './ledger-state-delta-for-transaction-group' +export { LedgerStateDeltaForTransactionGroupMeta } from './ledger-state-delta-for-transaction-group' export type { Account } from './account' -export { AccountApplicationInformationMeta } from './account-application-information' -export type { AccountApplicationInformation } from './account-application-information' -export { AccountAssetHoldingMeta } from './account-asset-holding' +export { AccountMeta } from './account' export type { AccountAssetHolding } from './account-asset-holding' -export { AccountAssetInformationMeta } from './account-asset-information' -export type { AccountAssetInformation } from './account-asset-information' -export { AccountAssetsInformationMeta } from './account-assets-information' -export type { AccountAssetsInformation } from './account-assets-information' -export { AccountParticipationMeta } from './account-participation' +export { AccountAssetHoldingMeta } from './account-asset-holding' export type { AccountParticipation } from './account-participation' -export { AccountStateDeltaMeta } from './account-state-delta' -export type { AccountStateDelta } from './account-state-delta' -export { AddParticipationKeyMeta } from './add-participation-key' -export type { AddParticipationKey } from './add-participation-key' -export { AppCallLogsMeta } from './app-call-logs' -export type { AppCallLogs } from './app-call-logs' -export { ApplicationMeta } from './application' -export type { Application } from './application' -export { ApplicationInitialStatesMeta } from './application-initial-states' -export type { ApplicationInitialStates } from './application-initial-states' -export { ApplicationKvStorageMeta } from './application-kv-storage' -export type { ApplicationKvStorage } from './application-kv-storage' -export { ApplicationLocalReferenceMeta } from './application-local-reference' -export type { ApplicationLocalReference } from './application-local-reference' -export { ApplicationLocalStateMeta } from './application-local-state' -export type { ApplicationLocalState } from './application-local-state' -export { ApplicationParamsMeta } from './application-params' -export type { ApplicationParams } from './application-params' -export { ApplicationStateOperationMeta } from './application-state-operation' -export type { ApplicationStateOperation } from './application-state-operation' -export { ApplicationStateSchemaMeta } from './application-state-schema' -export type { ApplicationStateSchema } from './application-state-schema' -export { AssetMeta } from './asset' +export { AccountParticipationMeta } from './account-participation' export type { Asset } from './asset' -export { AssetHoldingMeta } from './asset-holding' +export { AssetMeta } from './asset' export type { AssetHolding } from './asset-holding' -export { AssetHoldingReferenceMeta } from './asset-holding-reference' -export type { AssetHoldingReference } from './asset-holding-reference' -export { AssetParamsMeta } from './asset-params' +export { AssetHoldingMeta } from './asset-holding' export type { AssetParams } from './asset-params' -export { AvmKeyValueMeta } from './avm-key-value' -export type { AvmKeyValue } from './avm-key-value' -export { AvmValueMeta } from './avm-value' +export { AssetParamsMeta } from './asset-params' +export type { AssetHoldingReference } from './asset-holding-reference' +export { AssetHoldingReferenceMeta } from './asset-holding-reference' +export type { ApplicationLocalReference } from './application-local-reference' +export { ApplicationLocalReferenceMeta } from './application-local-reference' +export type { ApplicationStateSchema } from './application-state-schema' +export { ApplicationStateSchemaMeta } from './application-state-schema' +export type { ApplicationLocalState } from './application-local-state' +export { ApplicationLocalStateMeta } from './application-local-state' +export type { ParticipationKey } from './participation-key' +export { ParticipationKeyMeta } from './participation-key' +export type { TealKeyValueStore } from './teal-key-value-store' +export { TealKeyValueStoreMeta } from './teal-key-value-store' +export type { TealKeyValue } from './teal-key-value' +export { TealKeyValueMeta } from './teal-key-value' +export type { TealValue } from './teal-value' +export { TealValueMeta } from './teal-value' export type { AvmValue } from './avm-value' -export { BoxMeta } from './box' -export type { Box } from './box' -export { BoxDescriptorMeta } from './box-descriptor' -export type { BoxDescriptor } from './box-descriptor' -export { BoxReferenceMeta } from './box-reference' -export type { BoxReference } from './box-reference' -export { BuildVersionMeta } from './build-version' -export type { BuildVersion } from './build-version' -export { DebugSettingsProfMeta } from './debug-settings-prof' -export type { DebugSettingsProf } from './debug-settings-prof' -export { DryrunRequestMeta } from './dryrun-request' -export type { DryrunRequest } from './dryrun-request' -export { DryrunSourceMeta } from './dryrun-source' -export type { DryrunSource } from './dryrun-source' -export { DryrunStateMeta } from './dryrun-state' +export { AvmValueMeta } from './avm-value' +export type { AvmKeyValue } from './avm-key-value' +export { AvmKeyValueMeta } from './avm-key-value' +export type { StateDelta } from './state-delta' +export { StateDeltaMeta } from './state-delta' +export type { AccountStateDelta } from './account-state-delta' +export { AccountStateDeltaMeta } from './account-state-delta' +export type { EvalDeltaKeyValue } from './eval-delta-key-value' +export { EvalDeltaKeyValueMeta } from './eval-delta-key-value' +export type { EvalDelta } from './eval-delta' +export { EvalDeltaMeta } from './eval-delta' +export type { Application } from './application' +export { ApplicationMeta } from './application' +export type { ApplicationParams } from './application-params' +export { ApplicationParamsMeta } from './application-params' export type { DryrunState } from './dryrun-state' -export { DryrunTxnResultMeta } from './dryrun-txn-result' +export { DryrunStateMeta } from './dryrun-state' export type { DryrunTxnResult } from './dryrun-txn-result' -export { ErrorResponseMeta } from './error-response' +export { DryrunTxnResultMeta } from './dryrun-txn-result' export type { ErrorResponse } from './error-response' -export { EvalDeltaMeta } from './eval-delta' -export type { EvalDelta } from './eval-delta' -export { EvalDeltaKeyValueMeta } from './eval-delta-key-value' -export type { EvalDeltaKeyValue } from './eval-delta-key-value' -export { GenesisMeta } from './genesis' -export type { Genesis } from './genesis' -export { GenesisAllocationMeta } from './genesis-allocation' -export type { GenesisAllocation } from './genesis-allocation' -export { GetApplicationBoxesMeta } from './get-application-boxes' -export type { GetApplicationBoxes } from './get-application-boxes' -export { GetBlockMeta } from './get-block' -export type { GetBlock } from './get-block' -export { GetBlockHashMeta } from './get-block-hash' -export type { GetBlockHash } from './get-block-hash' -export { GetBlockLogsMeta } from './get-block-logs' -export type { GetBlockLogs } from './get-block-logs' -export { GetBlockTimeStampOffsetMeta } from './get-block-time-stamp-offset' -export type { GetBlockTimeStampOffset } from './get-block-time-stamp-offset' -export { GetBlockTxidsMeta } from './get-block-txids' -export type { GetBlockTxids } from './get-block-txids' -export { GetPendingTransactionsMeta } from './get-pending-transactions' -export type { GetPendingTransactions } from './get-pending-transactions' -export { GetPendingTransactionsByAddressMeta } from './get-pending-transactions-by-address' -export type { GetPendingTransactionsByAddress } from './get-pending-transactions-by-address' -export { GetStatusMeta } from './get-status' -export type { GetStatus } from './get-status' -export { GetSupplyMeta } from './get-supply' -export type { GetSupply } from './get-supply' -export { GetSyncRoundMeta } from './get-sync-round' -export type { GetSyncRound } from './get-sync-round' -export { GetTransactionGroupLedgerStateDeltasForRoundMeta } from './get-transaction-group-ledger-state-deltas-for-round' -export type { GetTransactionGroupLedgerStateDeltasForRound } from './get-transaction-group-ledger-state-deltas-for-round' -export { LedgerStateDeltaMeta } from './ledger-state-delta' -export type { LedgerStateDelta } from './ledger-state-delta' -export { LedgerStateDeltaForTransactionGroupMeta } from './ledger-state-delta-for-transaction-group' -export type { LedgerStateDeltaForTransactionGroup } from './ledger-state-delta-for-transaction-group' -export { LightBlockHeaderProofMeta } from './light-block-header-proof' -export type { LightBlockHeaderProof } from './light-block-header-proof' -export { ParticipationKeyMeta } from './participation-key' -export type { ParticipationKey } from './participation-key' -export { PendingTransactionResponseMeta } from './pending-transaction-response' -export type { PendingTransactionResponse } from './pending-transaction-response' -export { RawTransactionMeta } from './raw-transaction' -export type { RawTransaction } from './raw-transaction' -export { ScratchChangeMeta } from './scratch-change' -export type { ScratchChange } from './scratch-change' -export { ShutdownNodeMeta } from './shutdown-node' -export type { ShutdownNode } from './shutdown-node' -export { SimulateInitialStatesMeta } from './simulate-initial-states' -export type { SimulateInitialStates } from './simulate-initial-states' -export { SimulateRequestMeta } from './simulate-request' +export { ErrorResponseMeta } from './error-response' +export type { DryrunRequest } from './dryrun-request' +export { DryrunRequestMeta } from './dryrun-request' +export type { DryrunSource } from './dryrun-source' +export { DryrunSourceMeta } from './dryrun-source' export type { SimulateRequest } from './simulate-request' -export { SimulateRequestTransactionGroupMeta } from './simulate-request-transaction-group' +export { SimulateRequestMeta } from './simulate-request' export type { SimulateRequestTransactionGroup } from './simulate-request-transaction-group' -export { SimulateTraceConfigMeta } from './simulate-trace-config' +export { SimulateRequestTransactionGroupMeta } from './simulate-request-transaction-group' export type { SimulateTraceConfig } from './simulate-trace-config' -export { SimulateTransactionMeta } from './simulate-transaction' -export type { SimulateTransaction } from './simulate-transaction' -export { SimulateTransactionGroupResultMeta } from './simulate-transaction-group-result' +export { SimulateTraceConfigMeta } from './simulate-trace-config' +export type { Box } from './box' +export { BoxMeta } from './box' +export type { BoxDescriptor } from './box-descriptor' +export { BoxDescriptorMeta } from './box-descriptor' +export type { BoxReference } from './box-reference' +export { BoxReferenceMeta } from './box-reference' +export type { Version } from './version' +export { VersionMeta } from './version' +export type { DebugSettingsProf } from './debug-settings-prof' +export { DebugSettingsProfMeta } from './debug-settings-prof' +export type { BuildVersion } from './build-version' +export { BuildVersionMeta } from './build-version' +export type { PendingTransactionResponse } from './pending-transaction-response' +export { PendingTransactionResponseMeta } from './pending-transaction-response' export type { SimulateTransactionGroupResult } from './simulate-transaction-group-result' -export { SimulateTransactionResultMeta } from './simulate-transaction-result' +export { SimulateTransactionGroupResultMeta } from './simulate-transaction-group-result' export type { SimulateTransactionResult } from './simulate-transaction-result' -export { SimulateUnnamedResourcesAccessedMeta } from './simulate-unnamed-resources-accessed' -export type { SimulateUnnamedResourcesAccessed } from './simulate-unnamed-resources-accessed' -export { SimulationEvalOverridesMeta } from './simulation-eval-overrides' +export { SimulateTransactionResultMeta } from './simulate-transaction-result' +export type { StateProof } from './state-proof' +export { StateProofMeta } from './state-proof' +export type { LightBlockHeaderProof } from './light-block-header-proof' +export { LightBlockHeaderProofMeta } from './light-block-header-proof' +export type { StateProofMessage } from './state-proof-message' +export { StateProofMessageMeta } from './state-proof-message' export type { SimulationEvalOverrides } from './simulation-eval-overrides' -export { SimulationOpcodeTraceUnitMeta } from './simulation-opcode-trace-unit' +export { SimulationEvalOverridesMeta } from './simulation-eval-overrides' +export type { ScratchChange } from './scratch-change' +export { ScratchChangeMeta } from './scratch-change' +export type { ApplicationStateOperation } from './application-state-operation' +export { ApplicationStateOperationMeta } from './application-state-operation' +export type { ApplicationKvStorage } from './application-kv-storage' +export { ApplicationKvStorageMeta } from './application-kv-storage' +export type { ApplicationInitialStates } from './application-initial-states' +export { ApplicationInitialStatesMeta } from './application-initial-states' export type { SimulationOpcodeTraceUnit } from './simulation-opcode-trace-unit' -export { SimulationTransactionExecTraceMeta } from './simulation-transaction-exec-trace' +export { SimulationOpcodeTraceUnitMeta } from './simulation-opcode-trace-unit' export type { SimulationTransactionExecTrace } from './simulation-transaction-exec-trace' -export { StartCatchupMeta } from './start-catchup' -export type { StartCatchup } from './start-catchup' -export { StateDeltaMeta } from './state-delta' -export type { StateDelta } from './state-delta' -export { StateProofMeta } from './state-proof' -export type { StateProof } from './state-proof' -export { StateProofMessageMeta } from './state-proof-message' -export type { StateProofMessage } from './state-proof-message' -export { TealCompileMeta } from './teal-compile' +export { SimulationTransactionExecTraceMeta } from './simulation-transaction-exec-trace' +export type { SimulateUnnamedResourcesAccessed } from './simulate-unnamed-resources-accessed' +export { SimulateUnnamedResourcesAccessedMeta } from './simulate-unnamed-resources-accessed' +export type { SimulateInitialStates } from './simulate-initial-states' +export { SimulateInitialStatesMeta } from './simulate-initial-states' +export type { AppCallLogs } from './app-call-logs' +export { AppCallLogsMeta } from './app-call-logs' +export type { TransactionProof } from './transaction-proof' +export { TransactionProofMeta } from './transaction-proof' +export type { AccountAssetInformation } from './account-asset-information' +export { AccountAssetInformationMeta } from './account-asset-information' +export type { AccountAssetsInformation } from './account-assets-information' +export { AccountAssetsInformationMeta } from './account-assets-information' +export type { AccountApplicationInformation } from './account-application-information' +export { AccountApplicationInformationMeta } from './account-application-information' +export type { GetPendingTransactionsByAddress } from './get-pending-transactions-by-address' +export { GetPendingTransactionsByAddressMeta } from './get-pending-transactions-by-address' +export type { GetBlock } from './get-block' +export { GetBlockMeta } from './get-block' +export type { GetBlockTxids } from './get-block-txids' +export { GetBlockTxidsMeta } from './get-block-txids' +export type { GetBlockHash } from './get-block-hash' +export { GetBlockHashMeta } from './get-block-hash' +export type { GetBlockLogs } from './get-block-logs' +export { GetBlockLogsMeta } from './get-block-logs' +export type { GetSupply } from './get-supply' +export { GetSupplyMeta } from './get-supply' +export type { AddParticipationKey } from './add-participation-key' +export { AddParticipationKeyMeta } from './add-participation-key' +export type { ShutdownNode } from './shutdown-node' +export { ShutdownNodeMeta } from './shutdown-node' +export type { GetStatus } from './get-status' +export { GetStatusMeta } from './get-status' +export type { WaitForBlock } from './wait-for-block' +export { WaitForBlockMeta } from './wait-for-block' +export type { RawTransaction } from './raw-transaction' +export { RawTransactionMeta } from './raw-transaction' +export type { SimulateTransaction } from './simulate-transaction' +export { SimulateTransactionMeta } from './simulate-transaction' +export type { TransactionParams } from './transaction-params' +export { TransactionParamsMeta } from './transaction-params' +export type { GetPendingTransactions } from './get-pending-transactions' +export { GetPendingTransactionsMeta } from './get-pending-transactions' +export type { GetTransactionGroupLedgerStateDeltasForRound } from './get-transaction-group-ledger-state-deltas-for-round' +export { GetTransactionGroupLedgerStateDeltasForRoundMeta } from './get-transaction-group-ledger-state-deltas-for-round' +export type { GetApplicationBoxes } from './get-application-boxes' +export { GetApplicationBoxesMeta } from './get-application-boxes' +export type { GetSyncRound } from './get-sync-round' +export { GetSyncRoundMeta } from './get-sync-round' export type { TealCompile } from './teal-compile' -export { TealDisassembleMeta } from './teal-disassemble' +export { TealCompileMeta } from './teal-compile' export type { TealDisassemble } from './teal-disassemble' -export { TealDryrunMeta } from './teal-dryrun' +export { TealDisassembleMeta } from './teal-disassemble' +export type { StartCatchup } from './start-catchup' +export { StartCatchupMeta } from './start-catchup' +export type { AbortCatchup } from './abort-catchup' +export { AbortCatchupMeta } from './abort-catchup' export type { TealDryrun } from './teal-dryrun' -export { TealKeyValueMeta } from './teal-key-value' -export type { TealKeyValue } from './teal-key-value' -export { TealKeyValueStoreMeta } from './teal-key-value-store' -export type { TealKeyValueStore } from './teal-key-value-store' -export { TealValueMeta } from './teal-value' -export type { TealValue } from './teal-value' -export { TransactionParamsMeta } from './transaction-params' -export type { TransactionParams } from './transaction-params' -export { TransactionProofMeta } from './transaction-proof' -export type { TransactionProof } from './transaction-proof' -export { VersionMeta } from './version' -export type { Version } from './version' -export { WaitForBlockMeta } from './wait-for-block' -export type { WaitForBlock } from './wait-for-block' +export { TealDryrunMeta } from './teal-dryrun' +export type { GetBlockTimeStampOffset } from './get-block-time-stamp-offset' +export { GetBlockTimeStampOffsetMeta } from './get-block-time-stamp-offset' -export { BlockMeta } from './block' -export type { Block } from './block' -export { BlockAccountStateDeltaMeta } from './block-account-state-delta' -export type { BlockAccountStateDelta } from './block-account-state-delta' -export { BlockAppEvalDeltaMeta } from './block-app-eval-delta' -export type { BlockAppEvalDelta } from './block-app-eval-delta' -export { BlockEvalDeltaMeta } from './block-eval-delta' export type { BlockEvalDelta } from './block-eval-delta' -export { BlockStateDeltaMeta } from './block-state-delta' +export { BlockEvalDeltaMeta } from './block-eval-delta' export type { BlockStateDelta } from './block-state-delta' -export { BlockStateProofTrackingMeta } from './block_state_proof_tracking' -export type { BlockStateProofTracking } from './block_state_proof_tracking' -export { BlockStateProofTrackingDataMeta } from './block_state_proof_tracking_data' +export { BlockStateDeltaMeta } from './block-state-delta' +export type { BlockAccountStateDelta } from './block-account-state-delta' +export { BlockAccountStateDeltaMeta } from './block-account-state-delta' +export type { BlockAppEvalDelta } from './block-app-eval-delta' +export { BlockAppEvalDeltaMeta } from './block-app-eval-delta' export type { BlockStateProofTrackingData } from './block_state_proof_tracking_data' -export { SignedTxnInBlockMeta } from './signed-txn-in-block' +export { BlockStateProofTrackingDataMeta } from './block_state_proof_tracking_data' +export type { BlockStateProofTracking } from './block_state_proof_tracking' +export { BlockStateProofTrackingMeta } from './block_state_proof_tracking' +export type { Block } from './block' +export { BlockMeta } from './block' export type { SignedTxnInBlock } from './signed-txn-in-block' +export { SignedTxnInBlockMeta } from './signed-txn-in-block' diff --git a/packages/algod_client/tsconfig.build.json b/packages/algod_client/tsconfig.build.json deleted file mode 100644 index 0e149d39..00000000 --- a/packages/algod_client/tsconfig.build.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["src/**/*.ts"], - "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "tests/**/*.*"] -} diff --git a/packages/algod_client/tsconfig.json b/packages/algod_client/tsconfig.json index 71b6c1f8..079567a1 100644 --- a/packages/algod_client/tsconfig.json +++ b/packages/algod_client/tsconfig.json @@ -1,8 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "dist", - "tsBuildInfoFile": "build/.tsbuildinfo" + "outDir": "dist" }, "include": ["src/**/*.ts", "tests/**/*.ts"] } diff --git a/packages/common/.tstoolkitrc.ts b/packages/common/.tstoolkitrc.ts deleted file mode 100644 index f19a3d47..00000000 --- a/packages/common/.tstoolkitrc.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { TsToolkitConfig } from '@makerx/ts-toolkit' - -const config: TsToolkitConfig = { - packageConfig: { - srcDir: 'src', - outDir: 'dist', - main: 'index.js', - customSections: ['module', 'main', 'type', 'types', 'exports'], - }, -} -export default config diff --git a/packages/common/package.json b/packages/common/package.json index 151f0613..7fd9f3cc 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -14,13 +14,6 @@ "**/*" ], "scripts": { - "build": "run-s build:*", - "build-watch": "rolldown --watch -c", - "build:0-clean": "rimraf dist coverage", - "build:1-lint": "npm run lint", - "build:2-compile": "rolldown -c", - "build:3-copy-pkg-json": "tstk copy-package-json -c", - "build:4-copy-readme": "cpy README.md dist", "test": "vitest run --coverage --passWithNoTests", "test:watch": "vitest watch --coverage --passWithNoTests", "lint": "eslint ./src/", @@ -33,4 +26,4 @@ "dependencies": {}, "peerDependencies": {}, "devDependencies": {} -} +} \ No newline at end of file diff --git a/packages/common/rolldown.config.ts b/packages/common/rolldown.config.ts deleted file mode 100644 index 0cd70473..00000000 --- a/packages/common/rolldown.config.ts +++ /dev/null @@ -1,4 +0,0 @@ -import createConfig from '../../rolldown' -import pkg from './package.json' with { type: 'json' } - -export default createConfig([...Object.keys(pkg.dependencies || {})]) diff --git a/packages/common/tsconfig.build.json b/packages/common/tsconfig.build.json deleted file mode 100644 index 0e149d39..00000000 --- a/packages/common/tsconfig.build.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["src/**/*.ts"], - "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "tests/**/*.*"] -} diff --git a/packages/common/tsconfig.json b/packages/common/tsconfig.json index 71b6c1f8..079567a1 100644 --- a/packages/common/tsconfig.json +++ b/packages/common/tsconfig.json @@ -1,8 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "dist", - "tsBuildInfoFile": "build/.tsbuildinfo" + "outDir": "dist" }, "include": ["src/**/*.ts", "tests/**/*.ts"] } diff --git a/packages/indexer_client/.tstoolkitrc.ts b/packages/indexer_client/.tstoolkitrc.ts deleted file mode 100644 index f19a3d47..00000000 --- a/packages/indexer_client/.tstoolkitrc.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { TsToolkitConfig } from '@makerx/ts-toolkit' - -const config: TsToolkitConfig = { - packageConfig: { - srcDir: 'src', - outDir: 'dist', - main: 'index.js', - customSections: ['module', 'main', 'type', 'types', 'exports'], - }, -} -export default config diff --git a/packages/indexer_client/package.json b/packages/indexer_client/package.json index b0aebbf4..f0acb368 100644 --- a/packages/indexer_client/package.json +++ b/packages/indexer_client/package.json @@ -14,13 +14,6 @@ "**/*" ], "scripts": { - "build": "run-s build:*", - "build-watch": "rolldown --watch -c", - "build:0-clean": "rimraf dist coverage", - "build:1-lint": "npm run lint", - "build:2-compile": "rolldown -c", - "build:3-copy-pkg-json": "tstk copy-package-json -c", - "build:4-copy-readme": "cpy README.md dist", "test": "vitest run --coverage --passWithNoTests", "test:watch": "vitest watch --coverage --passWithNoTests", "lint": "eslint ./src/", @@ -32,7 +25,5 @@ }, "dependencies": {}, "peerDependencies": {}, - "devDependencies": { - "@algorandfoundation/algokit-transact": "*" - } -} + "devDependencies": {} +} \ No newline at end of file diff --git a/packages/indexer_client/rolldown.config.ts b/packages/indexer_client/rolldown.config.ts deleted file mode 100644 index 0cd70473..00000000 --- a/packages/indexer_client/rolldown.config.ts +++ /dev/null @@ -1,4 +0,0 @@ -import createConfig from '../../rolldown' -import pkg from './package.json' with { type: 'json' } - -export default createConfig([...Object.keys(pkg.dependencies || {})]) diff --git a/packages/indexer_client/tsconfig.build.json b/packages/indexer_client/tsconfig.build.json deleted file mode 100644 index 0e149d39..00000000 --- a/packages/indexer_client/tsconfig.build.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["src/**/*.ts"], - "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "tests/**/*.*"] -} diff --git a/packages/indexer_client/tsconfig.json b/packages/indexer_client/tsconfig.json index 71b6c1f8..079567a1 100644 --- a/packages/indexer_client/tsconfig.json +++ b/packages/indexer_client/tsconfig.json @@ -1,8 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "dist", - "tsBuildInfoFile": "build/.tsbuildinfo" + "outDir": "dist" }, "include": ["src/**/*.ts", "tests/**/*.ts"] } diff --git a/packages/kmd_client/.tstoolkitrc.ts b/packages/kmd_client/.tstoolkitrc.ts deleted file mode 100644 index f19a3d47..00000000 --- a/packages/kmd_client/.tstoolkitrc.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { TsToolkitConfig } from '@makerx/ts-toolkit' - -const config: TsToolkitConfig = { - packageConfig: { - srcDir: 'src', - outDir: 'dist', - main: 'index.js', - customSections: ['module', 'main', 'type', 'types', 'exports'], - }, -} -export default config diff --git a/packages/kmd_client/package.json b/packages/kmd_client/package.json index e8d55d7c..9e7036e6 100644 --- a/packages/kmd_client/package.json +++ b/packages/kmd_client/package.json @@ -14,13 +14,6 @@ "**/*" ], "scripts": { - "build": "run-s build:*", - "build-watch": "rolldown --watch -c", - "build:0-clean": "rimraf dist coverage", - "build:1-lint": "npm run lint", - "build:2-compile": "rolldown -c", - "build:3-copy-pkg-json": "tstk copy-package-json -c", - "build:4-copy-readme": "cpy README.md dist", "test": "vitest run --coverage --passWithNoTests", "test:watch": "vitest watch --coverage --passWithNoTests", "lint": "eslint ./src/", @@ -32,7 +25,5 @@ }, "dependencies": {}, "peerDependencies": {}, - "devDependencies": { - "@algorandfoundation/algokit-transact": "*" - } -} + "devDependencies": {} +} \ No newline at end of file diff --git a/packages/kmd_client/rolldown.config.ts b/packages/kmd_client/rolldown.config.ts deleted file mode 100644 index 0cd70473..00000000 --- a/packages/kmd_client/rolldown.config.ts +++ /dev/null @@ -1,4 +0,0 @@ -import createConfig from '../../rolldown' -import pkg from './package.json' with { type: 'json' } - -export default createConfig([...Object.keys(pkg.dependencies || {})]) diff --git a/packages/kmd_client/tsconfig.build.json b/packages/kmd_client/tsconfig.build.json deleted file mode 100644 index 0e149d39..00000000 --- a/packages/kmd_client/tsconfig.build.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["src/**/*.ts"], - "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "tests/**/*.*"] -} diff --git a/packages/kmd_client/tsconfig.json b/packages/kmd_client/tsconfig.json index 71b6c1f8..079567a1 100644 --- a/packages/kmd_client/tsconfig.json +++ b/packages/kmd_client/tsconfig.json @@ -1,8 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "dist", - "tsBuildInfoFile": "build/.tsbuildinfo" + "outDir": "dist" }, "include": ["src/**/*.ts", "tests/**/*.ts"] } diff --git a/packages/sdk/README.md b/packages/sdk/README.md new file mode 100644 index 00000000..5762ec23 --- /dev/null +++ b/packages/sdk/README.md @@ -0,0 +1 @@ +# AlgoKit KMD Client diff --git a/packages/sdk/eslint.config.mjs b/packages/sdk/eslint.config.mjs new file mode 100644 index 00000000..1fef1ac9 --- /dev/null +++ b/packages/sdk/eslint.config.mjs @@ -0,0 +1,3 @@ +import config from '../../eslint.config.mjs' + +export default config diff --git a/packages/sdk/package.json b/packages/sdk/package.json new file mode 100644 index 00000000..3a620fe3 --- /dev/null +++ b/packages/sdk/package.json @@ -0,0 +1,26 @@ +{ + "name": "@algorandfoundation/sdk", + "version": "0.1.0", + "private": true, + "description": "Legacy SDK.", + "author": "Algorand Foundation", + "license": "MIT", + "engines": { + "node": ">=20.0" + }, + "type": "commonjs", + "main": "./src/index.js", + "files": [ + "**/*" + ], + "scripts": { + "test": "vitest run --coverage --passWithNoTests", + "test:watch": "vitest watch --coverage --passWithNoTests", + "check-types": "tsc --noEmit", + "audit": "better-npm-audit audit", + "pre-commit": "run-s check-types lint:fix audit format test" + }, + "dependencies": {}, + "peerDependencies": {}, + "devDependencies": {} +} \ No newline at end of file diff --git a/packages/sdk/src/abi/abi_type.ts b/packages/sdk/src/abi/abi_type.ts new file mode 100644 index 00000000..bd467a90 --- /dev/null +++ b/packages/sdk/src/abi/abi_type.ts @@ -0,0 +1,864 @@ +/* eslint-disable no-bitwise */ +/* eslint-disable no-use-before-define */ +/* eslint-disable class-methods-use-this */ + +/** + //ABI-Types: uint: An N-bit unsigned integer (8 <= N <= 512 and N % 8 = 0). + // | byte (alias for uint8) + // | ufixed x (8 <= N <= 512, N % 8 = 0, and 0 < M <= 160) + // | bool + // | address (alias for byte[32]) + // | [] + // | [] + // | string + // | (T1, ..., Tn) +*/ +import { encodeAddress, decodeAddress, Address } from '../encoding/address.js'; +import { bigIntToBytes, bytesToBigInt } from '../encoding/bigint.js'; +import { concatArrays } from '../utils/utils.js'; + +export const MAX_LEN = 2 ** 16 - 1; +export const ADDR_BYTE_SIZE = 32; +export const SINGLE_BYTE_SIZE = 1; +export const SINGLE_BOOL_SIZE = 1; +export const LENGTH_ENCODE_BYTE_SIZE = 2; + +interface Segment { + left: number; + right: number; +} + +const staticArrayRegexp = /^([a-z\d[\](),]+)\[(0|[1-9][\d]*)]$/; +const ufixedRegexp = /^ufixed([1-9][\d]*)x([1-9][\d]*)$/; + +export type ABIValue = + | boolean + | number + | bigint + | string + | Uint8Array + | ABIValue[] + | Address; + +export abstract class ABIType { + // Converts a ABIType object to a string + abstract toString(): string; + // Checks if two ABIType objects are equal in value + abstract equals(other: ABIType): boolean; + // Checks if the ABIType object (or any of its child types) have dynamic length + abstract isDynamic(): boolean; + // Returns the size of the ABIType object in bytes + abstract byteLen(): number; + // Encodes a value for the ABIType object using the ABI specs + abstract encode(value: ABIValue): Uint8Array; + // Decodes a value for the ABIType object using the ABI specs + abstract decode(byteString: Uint8Array): ABIValue; + // De-serializes the ABI type from a string using the ABI specs + static from(str: string): ABIType { + if (str.endsWith('[]')) { + const arrayArgType = ABIType.from(str.slice(0, str.length - 2)); + return new ABIArrayDynamicType(arrayArgType); + } + if (str.endsWith(']')) { + const stringMatches = str.match(staticArrayRegexp); + // Match the string itself, array element type, then array length + if (!stringMatches || stringMatches.length !== 3) { + throw new Error(`malformed static array string: ${str}`); + } + // Parse static array using regex + const arrayLengthStr = stringMatches[2]; + const arrayLength = parseInt(arrayLengthStr, 10); + if (arrayLength > MAX_LEN) { + throw new Error(`array length exceeds limit ${MAX_LEN}`); + } + // Parse the array element type + const arrayType = ABIType.from(stringMatches[1]); + return new ABIArrayStaticType(arrayType, arrayLength); + } + if (str.startsWith('uint')) { + // Checks if the parsed number contains only digits, no whitespaces + const digitsOnly = (s: string) => + [...s].every((c) => '0123456789'.includes(c)); + const typeSizeStr = str.slice(4, str.length); + if (!digitsOnly(typeSizeStr)) { + throw new Error(`malformed uint string: ${typeSizeStr}`); + } + const typeSize = parseInt(typeSizeStr, 10); + if (typeSize > MAX_LEN) { + throw new Error(`malformed uint string: ${typeSize}`); + } + return new ABIUintType(typeSize); + } + if (str === 'byte') { + return new ABIByteType(); + } + if (str.startsWith('ufixed')) { + const stringMatches = str.match(ufixedRegexp); + if (!stringMatches || stringMatches.length !== 3) { + throw new Error(`malformed ufixed type: ${str}`); + } + const ufixedSize = parseInt(stringMatches[1], 10); + const ufixedPrecision = parseInt(stringMatches[2], 10); + return new ABIUfixedType(ufixedSize, ufixedPrecision); + } + if (str === 'bool') { + return new ABIBoolType(); + } + if (str === 'address') { + return new ABIAddressType(); + } + if (str === 'string') { + return new ABIStringType(); + } + if (str.length >= 2 && str[0] === '(' && str[str.length - 1] === ')') { + const tupleContent = ABITupleType.parseTupleContent( + str.slice(1, str.length - 1) + ); + const tupleTypes: ABIType[] = []; + for (let i = 0; i < tupleContent.length; i++) { + const ti = ABIType.from(tupleContent[i]); + tupleTypes.push(ti); + } + return new ABITupleType(tupleTypes); + } + throw new Error(`cannot convert a string ${str} to an ABI type`); + } +} + +export class ABIUintType extends ABIType { + bitSize: number; + + constructor(size: number) { + super(); + if (size % 8 !== 0 || size < 8 || size > 512) { + throw new Error(`unsupported uint type bitSize: ${size}`); + } + this.bitSize = size; + } + + toString() { + return `uint${this.bitSize}`; + } + + equals(other: ABIType) { + return other instanceof ABIUintType && this.bitSize === other.bitSize; + } + + isDynamic() { + return false; + } + + byteLen() { + return this.bitSize / 8; + } + + encode(value: ABIValue) { + if (typeof value !== 'bigint' && typeof value !== 'number') { + throw new Error(`Cannot encode value as uint${this.bitSize}: ${value}`); + } + if (value >= BigInt(2 ** this.bitSize) || value < BigInt(0)) { + throw new Error( + `${value} is not a non-negative int or too big to fit in size uint${this.bitSize}` + ); + } + if (typeof value === 'number' && !Number.isSafeInteger(value)) { + throw new Error( + `${value} should be converted into a BigInt before it is encoded` + ); + } + return bigIntToBytes(value, this.bitSize / 8); + } + + decode(byteString: Uint8Array): bigint { + if (byteString.length !== this.bitSize / 8) { + throw new Error(`byte string must correspond to a uint${this.bitSize}`); + } + return bytesToBigInt(byteString); + } +} + +export class ABIUfixedType extends ABIType { + bitSize: number; + precision: number; + + constructor(size: number, denominator: number) { + super(); + if (size % 8 !== 0 || size < 8 || size > 512) { + throw new Error(`unsupported ufixed type bitSize: ${size}`); + } + if (denominator > 160 || denominator < 1) { + throw new Error(`unsupported ufixed type precision: ${denominator}`); + } + this.bitSize = size; + this.precision = denominator; + } + + toString() { + return `ufixed${this.bitSize}x${this.precision}`; + } + + equals(other: ABIType) { + return ( + other instanceof ABIUfixedType && + this.bitSize === other.bitSize && + this.precision === other.precision + ); + } + + isDynamic() { + return false; + } + + byteLen() { + return this.bitSize / 8; + } + + encode(value: ABIValue) { + if (typeof value !== 'bigint' && typeof value !== 'number') { + throw new Error(`Cannot encode value as ${this.toString()}: ${value}`); + } + if (value >= BigInt(2 ** this.bitSize) || value < BigInt(0)) { + throw new Error( + `${value} is not a non-negative int or too big to fit in size ${this.toString()}` + ); + } + if (typeof value === 'number' && !Number.isSafeInteger(value)) { + throw new Error( + `${value} should be converted into a BigInt before it is encoded` + ); + } + return bigIntToBytes(value, this.bitSize / 8); + } + + decode(byteString: Uint8Array): bigint { + if (byteString.length !== this.bitSize / 8) { + throw new Error(`byte string must correspond to a ${this.toString()}`); + } + return bytesToBigInt(byteString); + } +} + +export class ABIAddressType extends ABIType { + toString() { + return 'address'; + } + + equals(other: ABIType) { + return other instanceof ABIAddressType; + } + + isDynamic() { + return false; + } + + byteLen() { + return ADDR_BYTE_SIZE; + } + + encode(value: ABIValue) { + if (typeof value === 'string') { + const decodedAddress = decodeAddress(value); + return decodedAddress.publicKey; + } + + if (value instanceof Address) { + return value.publicKey; + } + + if (value instanceof Uint8Array) { + if (value.byteLength !== 32) { + throw new Error(`byte string must be 32 bytes long for an address`); + } + + return value; + } + + throw new Error(`Cannot encode value as ${this.toString()}: ${value}`); + } + + decode(byteString: Uint8Array): string { + if (byteString.byteLength !== 32) { + throw new Error(`byte string must be 32 bytes long for an address`); + } + return encodeAddress(byteString); + } +} + +export class ABIBoolType extends ABIType { + toString() { + return 'bool'; + } + + equals(other: ABIType) { + return other instanceof ABIBoolType; + } + + isDynamic() { + return false; + } + + byteLen() { + return SINGLE_BOOL_SIZE; + } + + encode(value: ABIValue) { + if (typeof value !== 'boolean') { + throw new Error(`Cannot encode value as bool: ${value}`); + } + if (value) { + return new Uint8Array([128]); + } + return new Uint8Array([0]); + } + + decode(byteString: Uint8Array): boolean { + if (byteString.byteLength !== 1) { + throw new Error(`bool string must be 1 byte long`); + } + const value = byteString[0]; + if (value === 128) { + return true; + } + if (value === 0) { + return false; + } + throw new Error(`boolean could not be decoded from the byte string`); + } +} + +export class ABIByteType extends ABIType { + toString() { + return 'byte'; + } + + equals(other: ABIType) { + return other instanceof ABIByteType; + } + + isDynamic() { + return false; + } + + byteLen() { + return SINGLE_BYTE_SIZE; + } + + encode(value: ABIValue) { + if (typeof value !== 'number' && typeof value !== 'bigint') { + throw new Error(`Cannot encode value as byte: ${value}`); + } + if (typeof value === 'bigint') { + // eslint-disable-next-line no-param-reassign + value = Number(value); + } + if (value < 0 || value > 255) { + throw new Error(`${value} cannot be encoded into a byte`); + } + return new Uint8Array([value]); + } + + decode(byteString: Uint8Array): number { + if (byteString.byteLength !== 1) { + throw new Error(`byte string must be 1 byte long`); + } + return byteString[0]; + } +} + +export class ABIStringType extends ABIType { + toString() { + return 'string'; + } + + equals(other: ABIType) { + return other instanceof ABIStringType; + } + + isDynamic() { + return true; + } + + byteLen(): never { + throw new Error(`${this.toString()} is a dynamic type`); + } + + encode(value: ABIValue) { + if (typeof value !== 'string' && !(value instanceof Uint8Array)) { + throw new Error(`Cannot encode value as string: ${value}`); + } + let encodedBytes: Uint8Array; + if (typeof value === 'string') { + encodedBytes = new TextEncoder().encode(value); + } else { + encodedBytes = value; + } + const encodedLength = bigIntToBytes( + encodedBytes.length, + LENGTH_ENCODE_BYTE_SIZE + ); + const mergedBytes = new Uint8Array( + encodedBytes.length + LENGTH_ENCODE_BYTE_SIZE + ); + mergedBytes.set(encodedLength); + mergedBytes.set(encodedBytes, LENGTH_ENCODE_BYTE_SIZE); + return mergedBytes; + } + + decode(byteString: Uint8Array): string { + if (byteString.length < LENGTH_ENCODE_BYTE_SIZE) { + throw new Error( + `byte string is too short to be decoded. Actual length is ${byteString.length}, but expected at least ${LENGTH_ENCODE_BYTE_SIZE}` + ); + } + const view = new DataView( + byteString.buffer, + byteString.byteOffset, + LENGTH_ENCODE_BYTE_SIZE + ); + const byteLength = view.getUint16(0); + const byteValue = byteString.slice( + LENGTH_ENCODE_BYTE_SIZE, + byteString.length + ); + if (byteLength !== byteValue.length) { + throw new Error( + `string length bytes do not match the actual length of string. Expected ${byteLength}, got ${byteValue.length}` + ); + } + return new TextDecoder('utf-8').decode(byteValue); + } +} + +export class ABIArrayStaticType extends ABIType { + childType: ABIType; + staticLength: number; + + constructor(argType: ABIType, arrayLength: number) { + super(); + if (arrayLength < 0) { + throw new Error( + `static array must have a non negative length: ${arrayLength}` + ); + } + this.childType = argType; + this.staticLength = arrayLength; + } + + toString() { + return `${this.childType.toString()}[${this.staticLength}]`; + } + + equals(other: ABIType) { + return ( + other instanceof ABIArrayStaticType && + this.staticLength === other.staticLength && + this.childType.equals(other.childType) + ); + } + + isDynamic() { + return this.childType.isDynamic(); + } + + byteLen() { + if (this.childType.constructor === ABIBoolType) { + return Math.ceil(this.staticLength / 8); + } + return this.staticLength * this.childType.byteLen(); + } + + encode(value: ABIValue) { + if (!Array.isArray(value) && !(value instanceof Uint8Array)) { + throw new Error(`Cannot encode value as ${this.toString()}: ${value}`); + } + if (value.length !== this.staticLength) { + throw new Error( + `Value array does not match static array length. Expected ${this.staticLength}, got ${value.length}` + ); + } + const convertedTuple = this.toABITupleType(); + return convertedTuple.encode(value); + } + + decode(byteString: Uint8Array): ABIValue[] { + const convertedTuple = this.toABITupleType(); + return convertedTuple.decode(byteString); + } + + toABITupleType() { + return new ABITupleType(Array(this.staticLength).fill(this.childType)); + } +} + +export class ABIArrayDynamicType extends ABIType { + childType: ABIType; + + constructor(argType: ABIType) { + super(); + this.childType = argType; + } + + toString() { + return `${this.childType.toString()}[]`; + } + + equals(other: ABIType) { + return ( + other instanceof ABIArrayDynamicType && + this.childType.equals(other.childType) + ); + } + + isDynamic() { + return true; + } + + byteLen(): never { + throw new Error(`${this.toString()} is a dynamic type`); + } + + encode(value: ABIValue) { + if (!Array.isArray(value) && !(value instanceof Uint8Array)) { + throw new Error(`Cannot encode value as ${this.toString()}: ${value}`); + } + const convertedTuple = this.toABITupleType(value.length); + const encodedTuple = convertedTuple.encode(value); + const encodedLength = bigIntToBytes( + convertedTuple.childTypes.length, + LENGTH_ENCODE_BYTE_SIZE + ); + const mergedBytes = concatArrays(encodedLength, encodedTuple); + return mergedBytes; + } + + decode(byteString: Uint8Array): ABIValue[] { + const view = new DataView(byteString.buffer, 0, LENGTH_ENCODE_BYTE_SIZE); + const byteLength = view.getUint16(0); + const convertedTuple = this.toABITupleType(byteLength); + return convertedTuple.decode( + byteString.slice(LENGTH_ENCODE_BYTE_SIZE, byteString.length) + ); + } + + toABITupleType(length: number) { + return new ABITupleType(Array(length).fill(this.childType)); + } +} + +export class ABITupleType extends ABIType { + childTypes: ABIType[]; + + constructor(argTypes: ABIType[]) { + super(); + if (argTypes.length >= MAX_LEN) { + throw new Error( + 'tuple type child type number larger than maximum uint16 error' + ); + } + this.childTypes = argTypes; + } + + toString() { + const typeStrings: string[] = []; + for (let i = 0; i < this.childTypes.length; i++) { + typeStrings[i] = this.childTypes[i].toString(); + } + return `(${typeStrings.join(',')})`; + } + + equals(other: ABIType) { + return ( + other instanceof ABITupleType && + this.childTypes.length === other.childTypes.length && + this.childTypes.every((child, index) => + child.equals(other.childTypes[index]) + ) + ); + } + + isDynamic() { + const isDynamic = (child: ABIType) => child.isDynamic(); + return this.childTypes.some(isDynamic); + } + + byteLen() { + let size = 0; + for (let i = 0; i < this.childTypes.length; i++) { + if (this.childTypes[i].constructor === ABIBoolType) { + const after = findBoolLR(this.childTypes, i, 1); + const boolNum = after + 1; + i += after; + size += Math.trunc((boolNum + 7) / 8); + } else { + const childByteSize = this.childTypes[i].byteLen(); + size += childByteSize; + } + } + return size; + } + + encode(value: ABIValue) { + if (!Array.isArray(value) && !(value instanceof Uint8Array)) { + throw new Error(`Cannot encode value as ${this.toString()}: ${value}`); + } + const values = Array.from(value); + if (value.length > MAX_LEN) { + throw new Error('length of tuple array should not exceed a uint16'); + } + const tupleTypes = this.childTypes; + const heads: Uint8Array[] = []; + const tails: Uint8Array[] = []; + const isDynamicIndex = new Map(); + let i = 0; + + while (i < tupleTypes.length) { + const tupleType = tupleTypes[i]; + if (tupleType.isDynamic()) { + // Head is not pre-determined for dynamic types; store a placeholder for now + isDynamicIndex.set(heads.length, true); + heads.push(new Uint8Array([0, 0])); + tails.push(tupleType.encode(values[i])); + } else { + if (tupleType.constructor === ABIBoolType) { + const before = findBoolLR(tupleTypes, i, -1); + let after = findBoolLR(tupleTypes, i, 1); + + // Pack bytes to heads and tails + if (before % 8 !== 0) { + throw new Error( + 'expected before index should have number of bool mod 8 equal 0' + ); + } + after = Math.min(7, after); + const compressedInt = compressMultipleBool( + values.slice(i, i + after + 1) + ); + heads.push(bigIntToBytes(compressedInt, 1)); + i += after; + } else { + const encodedTupleValue = tupleType.encode(values[i]); + heads.push(encodedTupleValue); + } + isDynamicIndex.set(i, false); + tails.push(new Uint8Array()); + } + i += 1; + } + + // Adjust head lengths for dynamic types + let headLength = 0; + for (const headElement of heads) { + headLength += headElement.length; + } + + // encode any placeholders for dynamic types + let tailLength = 0; + for (let j = 0; j < heads.length; j++) { + if (isDynamicIndex.get(j)) { + const headValue = headLength + tailLength; + if (headValue > MAX_LEN) { + throw new Error( + `byte length of ${headValue} should not exceed a uint16` + ); + } + heads[j] = bigIntToBytes(headValue, LENGTH_ENCODE_BYTE_SIZE); + } + tailLength += tails[j].length; + } + + return concatArrays(...heads, ...tails); + } + + decode(byteString: Uint8Array): ABIValue[] { + const tupleTypes = this.childTypes; + const dynamicSegments: Segment[] = []; + const valuePartition: Array = []; + let i = 0; + let iterIndex = 0; + const view = new DataView(byteString.buffer); + + while (i < tupleTypes.length) { + const tupleType = tupleTypes[i]; + if (tupleType.isDynamic()) { + if ( + byteString.slice(iterIndex, byteString.length).length < + LENGTH_ENCODE_BYTE_SIZE + ) { + throw new Error('dynamic type in tuple is too short to be decoded'); + } + // Since LENGTH_ENCODE_BYTE_SIZE is 2 and indices are at most 2 bytes, + // we can use getUint16 using the iterIndex offset. + const dynamicIndex = view.getUint16(iterIndex); + if (dynamicSegments.length > 0) { + dynamicSegments[dynamicSegments.length - 1].right = dynamicIndex; + // Check that right side of segment is greater than the left side + if (dynamicIndex < dynamicSegments[dynamicSegments.length - 1].left) { + throw new Error( + 'dynamic index segment miscalculation: left is greater than right index' + ); + } + } + // Since we do not know where the current dynamic element ends, put a placeholder and update later + const seg: Segment = { + left: dynamicIndex, + right: -1, + }; + dynamicSegments.push(seg); + valuePartition.push(null); + iterIndex += LENGTH_ENCODE_BYTE_SIZE; + } else { + // eslint-disable-next-line no-lonely-if + if (tupleType.constructor === ABIBoolType) { + const before = findBoolLR(this.childTypes, i, -1); + let after = findBoolLR(this.childTypes, i, 1); + + if (before % 8 !== 0) { + throw new Error('expected before bool number mod 8 === 0'); + } + after = Math.min(7, after); + // Parse bool in a byte to multiple byte strings + for (let boolIndex = 0; boolIndex <= after; boolIndex++) { + const boolMask = 0x80 >> boolIndex; + if ((byteString[iterIndex] & boolMask) > 0) { + valuePartition.push(new Uint8Array([128])); + } else { + valuePartition.push(new Uint8Array([0])); + } + } + i += after; + iterIndex += 1; + } else { + const currLen = tupleType.byteLen(); + valuePartition.push(byteString.slice(iterIndex, iterIndex + currLen)); + iterIndex += currLen; + } + } + if (i !== tupleTypes.length - 1 && iterIndex >= byteString.length) { + throw new Error('input byte not enough to decode'); + } + i += 1; + } + if (dynamicSegments.length > 0) { + dynamicSegments[dynamicSegments.length - 1].right = byteString.length; + iterIndex = byteString.length; + } + if (iterIndex < byteString.length) { + throw new Error('input byte not fully consumed'); + } + + // Check segment indices are valid + // If the dynamic segment are not consecutive and well-ordered, we return error + for (let j = 0; j < dynamicSegments.length; j++) { + const seg = dynamicSegments[j]; + if (seg.left > seg.right) { + throw new Error( + 'dynamic segment should display a [l, r] space with l <= r' + ); + } + if ( + j !== dynamicSegments.length - 1 && + seg.right !== dynamicSegments[j + 1].left + ) { + throw new Error('dynamic segment should be consecutive'); + } + } + + // Check dynamic element partitions + let segIndex = 0; + for (let j = 0; j < tupleTypes.length; j++) { + if (tupleTypes[j].isDynamic()) { + valuePartition[j] = byteString.slice( + dynamicSegments[segIndex].left, + dynamicSegments[segIndex].right + ); + segIndex += 1; + } + } + + // Decode each tuple element + const returnValues: ABIValue[] = []; + for (let j = 0; j < tupleTypes.length; j++) { + const valueTi = tupleTypes[j].decode(valuePartition[j]!); + returnValues.push(valueTi); + } + return returnValues; + } + + static parseTupleContent(str: string): string[] { + if (str.length === 0) { + return []; + } + if (str.endsWith(',') || str.startsWith(',')) { + throw new Error('tuple string should not start with comma'); + } + if (str.includes(',,')) { + throw new Error('tuple string should not have consecutive commas'); + } + + const tupleStrings: string[] = []; + let depth = 0; + let word = ''; + + for (const char of str) { + word += char; + if (char === '(') { + depth += 1; + } else if (char === ')') { + depth -= 1; + } else if (char === ',') { + // If the comma is at depth 0, then append the word as token. + if (depth === 0) { + tupleStrings.push(word.slice(0, word.length - 1)); + word = ''; + } + } + } + if (word.length !== 0) { + tupleStrings.push(word); + } + if (depth !== 0) { + throw new Error('tuple string has mismatched parentheses'); + } + return tupleStrings; + } +} + +// compressMultipleBool compresses consecutive bool values into a byte in ABI tuple / array value. +function compressMultipleBool(valueList: ABIValue[]): number { + let res = 0; + if (valueList.length > 8) { + throw new Error('value list passed in should be no greater than length 8'); + } + for (let i = 0; i < valueList.length; i++) { + const boolVal = valueList[i]; + if (typeof boolVal !== 'boolean') { + throw new Error('non-boolean values cannot be compressed into a byte'); + } + if (boolVal) { + res |= 1 << (7 - i); + } + } + return res; +} + +// Assume that the current index on the list of type is an ABI bool type. +// It returns the difference between the current index and the index of the furthest consecutive Bool type. +function findBoolLR(typeList: ABIType[], index: number, delta: -1 | 1): number { + let until = 0; + while (true) { + const curr = index + delta * until; + if (typeList[curr].constructor === ABIBoolType) { + if (curr !== typeList.length - 1 && delta === 1) { + until += 1; + } else if (curr > 0 && delta === -1) { + until += 1; + } else { + break; + } + } else { + until -= 1; + break; + } + } + return until; +} diff --git a/packages/sdk/src/abi/contract.ts b/packages/sdk/src/abi/contract.ts new file mode 100644 index 00000000..a3613ff0 --- /dev/null +++ b/packages/sdk/src/abi/contract.ts @@ -0,0 +1,57 @@ +import { ABIMethod, ABIMethodParams, getMethodByName } from './method.js'; +import { ARC28Event } from './event.js'; + +export interface ABIContractNetworkInfo { + appID: number; +} + +export interface ABIContractNetworks { + [network: string]: ABIContractNetworkInfo; +} + +export interface ABIContractParams { + name: string; + desc?: string; + networks?: ABIContractNetworks; + methods: ABIMethodParams[]; + events?: ARC28Event[]; +} + +export class ABIContract { + public readonly name: string; + public readonly description?: string; + public readonly networks: ABIContractNetworks; + public readonly methods: ABIMethod[]; + /** [ARC-28](https://arc.algorand.foundation/ARCs/arc-0028) events that MAY be emitted by this contract */ + public readonly events?: ARC28Event[]; + + constructor(params: ABIContractParams) { + if ( + typeof params.name !== 'string' || + !Array.isArray(params.methods) || + (params.networks && typeof params.networks !== 'object') + ) { + throw new Error('Invalid ABIContract parameters'); + } + + this.name = params.name; + this.description = params.desc; + this.networks = params.networks ? { ...params.networks } : {}; + this.methods = params.methods.map((method) => new ABIMethod(method)); + this.events = params.events; + } + + toJSON(): ABIContractParams { + return { + name: this.name, + desc: this.description, + networks: this.networks, + methods: this.methods.map((method) => method.toJSON()), + events: this.events, + }; + } + + getMethodByName(name: string): ABIMethod { + return getMethodByName(this.methods, name); + } +} diff --git a/packages/sdk/src/abi/event.ts b/packages/sdk/src/abi/event.ts new file mode 100644 index 00000000..0f865260 --- /dev/null +++ b/packages/sdk/src/abi/event.ts @@ -0,0 +1,16 @@ +/** [ARC-28](https://arc.algorand.foundation/ARCs/arc-0028) event description */ +export interface ARC28Event { + /** The name of the event */ + name: string; + /** Optional, user-friendly description for the event */ + desc?: string; + /** The arguments of the event, in order */ + args: Array<{ + /** The type of the argument */ + type: string; + /** Optional, user-friendly name for the argument */ + name?: string; + /** Optional, user-friendly description for the argument */ + desc?: string; + }>; +} diff --git a/packages/sdk/src/abi/index.ts b/packages/sdk/src/abi/index.ts new file mode 100644 index 00000000..ba8ad251 --- /dev/null +++ b/packages/sdk/src/abi/index.ts @@ -0,0 +1,6 @@ +export * from './abi_type.js'; +export * from './contract.js'; +export * from './interface.js'; +export * from './method.js'; +export * from './transaction.js'; +export * from './reference.js'; diff --git a/packages/sdk/src/abi/interface.ts b/packages/sdk/src/abi/interface.ts new file mode 100644 index 00000000..6dc91d2a --- /dev/null +++ b/packages/sdk/src/abi/interface.ts @@ -0,0 +1,35 @@ +import { ABIMethod, ABIMethodParams, getMethodByName } from './method.js'; + +export interface ABIInterfaceParams { + name: string; + desc?: string; + methods: ABIMethodParams[]; +} + +export class ABIInterface { + public readonly name: string; + public readonly description?: string; + public readonly methods: ABIMethod[]; + + constructor(params: ABIInterfaceParams) { + if (typeof params.name !== 'string' || !Array.isArray(params.methods)) { + throw new Error('Invalid ABIInterface parameters'); + } + + this.name = params.name; + this.description = params.desc; + this.methods = params.methods.map((method) => new ABIMethod(method)); + } + + toJSON(): ABIInterfaceParams { + return { + name: this.name, + desc: this.description, + methods: this.methods.map((method) => method.toJSON()), + }; + } + + getMethodByName(name: string): ABIMethod { + return getMethodByName(this.methods, name); + } +} diff --git a/packages/sdk/src/abi/method.ts b/packages/sdk/src/abi/method.ts new file mode 100644 index 00000000..353bedea --- /dev/null +++ b/packages/sdk/src/abi/method.ts @@ -0,0 +1,200 @@ +import { genericHash } from '../nacl/naclWrappers.js'; +import { ABIType, ABITupleType } from './abi_type.js'; +import { ABITransactionType, abiTypeIsTransaction } from './transaction.js'; +import { ABIReferenceType, abiTypeIsReference } from './reference.js'; +import { ARC28Event } from './event.js'; + +function parseMethodSignature(signature: string): { + name: string; + args: string[]; + returns: string; +} { + const argsStart = signature.indexOf('('); + if (argsStart === -1) { + throw new Error(`Invalid method signature: ${signature}`); + } + + let argsEnd = -1; + let depth = 0; + for (let i = argsStart; i < signature.length; i++) { + const char = signature[i]; + + if (char === '(') { + depth += 1; + } else if (char === ')') { + if (depth === 0) { + // unpaired parenthesis + break; + } + + depth -= 1; + if (depth === 0) { + argsEnd = i; + break; + } + } + } + + if (argsEnd === -1) { + throw new Error(`Invalid method signature: ${signature}`); + } + + return { + name: signature.slice(0, argsStart), + args: ABITupleType.parseTupleContent( + signature.slice(argsStart + 1, argsEnd) + ), + returns: signature.slice(argsEnd + 1), + }; +} + +export interface ABIMethodArgParams { + type: string; + name?: string; + desc?: string; +} + +export interface ABIMethodReturnParams { + type: string; + desc?: string; +} + +export interface ABIMethodParams { + name: string; + desc?: string; + args: ABIMethodArgParams[]; + returns: ABIMethodReturnParams; + /** Optional, is it a read-only method (according to [ARC-22](https://arc.algorand.foundation/ARCs/arc-0022)) */ + readonly?: boolean; + /** [ARC-28](https://arc.algorand.foundation/ARCs/arc-0028) events that MAY be emitted by this method */ + events?: ARC28Event[]; +} + +export type ABIArgumentType = ABIType | ABITransactionType | ABIReferenceType; + +export type ABIReturnType = ABIType | 'void'; + +export class ABIMethod { + public readonly name: string; + public readonly description?: string; + public readonly args: Array<{ + type: ABIArgumentType; + name?: string; + description?: string; + }>; + + public readonly returns: { type: ABIReturnType; description?: string }; + public readonly events?: ARC28Event[]; + public readonly readonly?: boolean; + + constructor(params: ABIMethodParams) { + if ( + typeof params.name !== 'string' || + typeof params.returns !== 'object' || + !Array.isArray(params.args) + ) { + throw new Error('Invalid ABIMethod parameters'); + } + + this.name = params.name; + this.description = params.desc; + this.args = params.args.map(({ type, name, desc }) => { + if (abiTypeIsTransaction(type) || abiTypeIsReference(type)) { + return { + type, + name, + description: desc, + }; + } + + return { + type: ABIType.from(type), + name, + description: desc, + }; + }); + this.returns = { + type: + params.returns.type === 'void' + ? params.returns.type + : ABIType.from(params.returns.type), + description: params.returns.desc, + }; + + this.events = params.events; + this.readonly = params.readonly; + } + + getSignature(): string { + const args = this.args.map((arg) => arg.type.toString()).join(','); + const returns = this.returns.type.toString(); + return `${this.name}(${args})${returns}`; + } + + getSelector(): Uint8Array { + const hash = genericHash(this.getSignature()); + return new Uint8Array(hash.slice(0, 4)); + } + + txnCount(): number { + let count = 1; + for (const arg of this.args) { + if (typeof arg.type === 'string' && abiTypeIsTransaction(arg.type)) { + count += 1; + } + } + return count; + } + + toJSON(): ABIMethodParams { + return { + name: this.name, + desc: this.description, + args: this.args.map(({ type, name, description }) => ({ + type: type.toString(), + name, + desc: description, + })), + returns: { + type: this.returns.type.toString(), + desc: this.returns.description, + }, + events: this.events, + readonly: this.readonly, + }; + } + + static fromSignature(signature: string): ABIMethod { + const { name, args, returns } = parseMethodSignature(signature); + + return new ABIMethod({ + name, + args: args.map((arg) => ({ type: arg })), + returns: { type: returns }, + }); + } +} + +export function getMethodByName(methods: ABIMethod[], name: string): ABIMethod { + if ( + methods === null || + !Array.isArray(methods) || + !methods.every((item) => item instanceof ABIMethod) + ) + throw new Error('Methods list provided is null or not the correct type'); + + const filteredMethods = methods.filter((m: ABIMethod) => m.name === name); + if (filteredMethods.length > 1) + throw new Error( + `found ${ + filteredMethods.length + } methods with the same name ${filteredMethods + .map((m: ABIMethod) => m.getSignature()) + .join(',')}` + ); + + if (filteredMethods.length === 0) + throw new Error(`found 0 methods with the name ${name}`); + + return filteredMethods[0]; +} diff --git a/packages/sdk/src/abi/reference.ts b/packages/sdk/src/abi/reference.ts new file mode 100644 index 00000000..c3e02346 --- /dev/null +++ b/packages/sdk/src/abi/reference.ts @@ -0,0 +1,24 @@ +export enum ABIReferenceType { + /** + * Account reference type + */ + account = 'account', + + /** + * Application reference type + */ + application = 'application', + + /** + * Asset reference type + */ + asset = 'asset', +} + +export function abiTypeIsReference(type: any): type is ABIReferenceType { + return ( + type === ABIReferenceType.account || + type === ABIReferenceType.application || + type === ABIReferenceType.asset + ); +} diff --git a/packages/sdk/src/abi/transaction.ts b/packages/sdk/src/abi/transaction.ts new file mode 100644 index 00000000..20e5ef11 --- /dev/null +++ b/packages/sdk/src/abi/transaction.ts @@ -0,0 +1,72 @@ +import type { Transaction } from '@algorandfoundation/algokit-transact' +import { TransactionType } from '@algorandfoundation/algokit-transact' + +export enum ABITransactionType { + /** + * Any transaction type + */ + any = 'txn', + + /** + * Payment transaction type + */ + pay = 'pay', + + /** + * Key registration transaction type + */ + keyreg = 'keyreg', + + /** + * Asset configuration transaction type + */ + acfg = 'acfg', + + /** + * Asset transfer transaction type + */ + axfer = 'axfer', + + /** + * Asset freeze transaction type + */ + afrz = 'afrz', + + /** + * Application transaction type + */ + appl = 'appl', +} + +export function abiTypeIsTransaction(type: any): type is ABITransactionType { + return ( + type === ABITransactionType.any || + type === ABITransactionType.pay || + type === ABITransactionType.keyreg || + type === ABITransactionType.acfg || + type === ABITransactionType.axfer || + type === ABITransactionType.afrz || + type === ABITransactionType.appl + ) +} + +export function abiCheckTransactionType(type: ABITransactionType, txn: Transaction): boolean { + if (type === ABITransactionType.any) { + return true + } + + // TODO: check this conversion + // Map ABI transaction types to numeric TransactionType enum + const typeMap: Record = { + [ABITransactionType.any]: null, + [ABITransactionType.pay]: TransactionType.Payment, + [ABITransactionType.keyreg]: TransactionType.KeyRegistration, + [ABITransactionType.acfg]: TransactionType.AssetConfig, + [ABITransactionType.axfer]: TransactionType.AssetTransfer, + [ABITransactionType.afrz]: TransactionType.AssetFreeze, + [ABITransactionType.appl]: TransactionType.AppCall, + } + + const expectedType = typeMap[type] + return expectedType !== null && txn.type === expectedType +} diff --git a/packages/sdk/src/account.ts b/packages/sdk/src/account.ts new file mode 100644 index 00000000..89c7b33e --- /dev/null +++ b/packages/sdk/src/account.ts @@ -0,0 +1,12 @@ +import * as nacl from './nacl/naclWrappers.js'; +import { Address } from './encoding/address.js'; +import Account from './types/account.js'; + +/** + * generateAccount returns a new Algorand address and its corresponding secret key + */ +export default function generateAccount(): Account { + const keys = nacl.keyPair(); + const addr = new Address(keys.publicKey); + return { addr, sk: keys.secretKey }; +} diff --git a/packages/sdk/src/appAccess.ts b/packages/sdk/src/appAccess.ts new file mode 100644 index 00000000..9a08f518 --- /dev/null +++ b/packages/sdk/src/appAccess.ts @@ -0,0 +1,84 @@ +import { ZERO_ADDRESS } from '@algorandfoundation/algokit-common' +import { AccessReference, BoxReference, HoldingReference, LocalsReference } from '@algorandfoundation/algokit-transact' +import { Address } from './encoding/address.js' + +/** + * foreignArraysToResourceReferences makes a single array of ResourceReferences from various foreign resource arrays. + * Note, runtime representation of ResourceReference uses addresses, app and asset identifiers, not indexes. + * + * @param accounts - optional array of accounts + * @param foreignAssets - optional array of foreign assets + * @param foreignApps - optional array of foreign apps + * @param holdings - optional array of holdings + * @param locals - optional array of locals + * @param boxes - optional array of boxes + */ +export function foreignArraysToResourceReferences({ + appIndex, + accounts, + foreignAssets, + foreignApps, + holdings, + locals, + boxes, +}: { + appIndex: bigint | number + accounts?: ReadonlyArray + foreignAssets?: ReadonlyArray + foreignApps?: ReadonlyArray + holdings?: ReadonlyArray + locals?: ReadonlyArray + boxes?: ReadonlyArray +}): Array { + const accessList: Array = [] + function ensureAddress(addr: string) { + if (!addr || addr === ZERO_ADDRESS) { + return + } + if (!accessList.find((rr) => rr.address === addr)) { + accessList.push({ address: addr }) + } + } + function ensureAsset(asset: number | bigint) { + if (!accessList.find((rr) => rr.assetId === BigInt(asset))) { + accessList.push({ assetId: BigInt(asset) }) + } + } + function ensureApp(app: number | bigint) { + if (!accessList.find((rr) => rr.appId === app)) { + accessList.push({ appId: BigInt(app) }) + } + } + for (const acct of accounts ?? []) { + ensureAddress(acct.toString()) + } + for (const asset of foreignAssets ?? []) { + ensureAsset(asset) + } + for (const app of foreignApps ?? []) { + ensureApp(app) + } + for (const holding of holdings ?? []) { + if (holding.address) { + ensureAddress(holding.address) + } + ensureAsset(holding.assetId) + accessList.push(holding) + } + for (const local of locals ?? []) { + if (local.address) { + ensureAddress(local.address) + } + if (local.appId && BigInt(local.appId) !== appIndex) { + ensureApp(local.appId) + } + accessList.push({ locals: local }) + } + for (const box of boxes ?? []) { + if (box.appId && BigInt(box.appId) !== appIndex) { + ensureApp(box.appId) + } + accessList.push({ box }) + } + return accessList +} diff --git a/packages/sdk/src/client/baseHTTPClient.ts b/packages/sdk/src/client/baseHTTPClient.ts new file mode 100644 index 00000000..0513f47c --- /dev/null +++ b/packages/sdk/src/client/baseHTTPClient.ts @@ -0,0 +1,64 @@ +export type Query = { + format?: F; + [key: string]: any; +}; + +export interface BaseHTTPClientResponse { + body: Uint8Array; + status: number; // status must always be 200 except when the response is inside an error + headers: Record; +} + +/** + * BaseHTTPClientError is the interface that errors thrown + * by methods of BaseHTTPClient should be using + */ +export interface BaseHTTPClientError { + response: BaseHTTPClientResponse; +} + +/** + * BaseHTTPClient is an interface abstracting the queries that can be + * made to an algod/indexer endpoint. + * The SDK normally uses the URLTokenBaseHTTPClient implementation. + * But when used via wallets, the wallet may provide a different object + * satisfying the HTTPClient interface. This is useful to allow + * wallets to provide access to paid API services without leaking + * the secret tokens/URLs. + * + * The parameter `customOptions` is an object that can be used to configure + * individual requests with specific specific to the BaseHTTPClient implementation. + * + * Note that DELETE requests have an optional query parameter. + * This is to allow future extension where DELETE may have queries + * Currently however HTTPClient does not make use of it + * + * Compared to HTTPClient, BaseHTTPClient does not deal with serialization/deserialization + * Everything is already string/Uint8Array + * and all the headers (including Accept/Content-Type) are assumed to be provided + * + * In case of non-200 status, all methods must throw an error of type + * BaseHTTPClientError + */ +export interface BaseHTTPClient { + get( + relativePath: string, + query?: Query, + requestHeaders?: Record, + customOptions?: Record + ): Promise; + post( + relativePath: string, + data: Uint8Array, + query?: Query, + requestHeaders?: Record, + customOptions?: Record + ): Promise; + delete( + relativePath: string, + data?: Uint8Array, + query?: Query, + requestHeaders?: Record, + customOptions?: Record + ): Promise; +} diff --git a/packages/sdk/src/client/client.ts b/packages/sdk/src/client/client.ts new file mode 100644 index 00000000..bbe13dee --- /dev/null +++ b/packages/sdk/src/client/client.ts @@ -0,0 +1,379 @@ +import * as utils from '../utils/utils.js'; +import { + BaseHTTPClient, + BaseHTTPClientResponse, + Query, +} from './baseHTTPClient.js'; +import { + TokenHeader, + URLTokenBaseHTTPClient, +} from './urlTokenBaseHTTPClient.js'; + +interface ErrorWithAdditionalInfo extends Error { + rawResponse: string | null; + statusCode: number; +} + +export class HTTPClientResponse { + /** + * The raw response bytes + */ + body: Uint8Array; + /** + * If the expected response type is JSON, this is the response bytes converted to a string. + */ + text?: string; + format: 'application/msgpack' | 'application/json'; + headers: Record; + status: number; + ok: boolean; + + constructor(options: { + body: Uint8Array; + text?: string; + format: 'application/msgpack' | 'application/json'; + headers: Record; + status: number; + ok: boolean; + }) { + this.body = options.body; + this.text = options.text; + this.format = options.format; + this.headers = options.headers; + this.status = options.status; + this.ok = options.ok; + } + + /** + * Returns the response body as a string, ready to be parsed as JSON. + */ + getJSONText(): string { + if (this.text === undefined) { + throw new Error( + `Response body does not contain JSON data. Format is ${this.format}` + ); + } + return this.text; + } + + /** + * Parses the response body as JSON with the given options. + */ + parseBodyAsJSON(jsonOptions: utils.ParseJSONOptions) { + if (this.text === undefined) { + throw new Error( + `Response body does not contain JSON data. Format is ${this.format}` + ); + } + // eslint-disable-next-line no-use-before-define + return HTTPClient.parseJSON(this.text, this.status, jsonOptions); + } +} + +/** + * Remove falsy values or values with a length of 0 from an object. + */ +function removeFalsyOrEmpty(obj: Record) { + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + // eslint-disable-next-line no-param-reassign + if (!obj[key] || obj[key].length === 0) delete obj[key]; + } + } + return obj; +} + +/** + * Create a new object with lower-case keys + * See https://codereview.stackexchange.com/a/162418 + * Used to ensure all headers are lower-case and to work more easily with them + */ +function tolowerCaseKeys(o: Record): Record { + /* eslint-disable no-param-reassign,no-return-assign,no-sequences */ + return Object.keys(o).reduce( + (c, k) => ((c[k.toLowerCase()] = o[k]), c), + {} as Record + ); + /* eslint-enable no-param-reassign,no-return-assign,no-sequences */ +} + +/** + * getAcceptFormat returns the correct Accept header depending on the + * requested format. + */ +function getAcceptFormat( + query?: Query<'msgpack' | 'json'> +): 'application/msgpack' | 'application/json' { + if ( + query !== undefined && + Object.prototype.hasOwnProperty.call(query, 'format') + ) { + switch (query.format) { + case 'msgpack': + return 'application/msgpack'; + case 'json': + default: + return 'application/json'; + } + } else return 'application/json'; +} + +/** + * HTTPClient is a wrapper around a BaseHTTPClient + * It takes care of setting the proper "Accept" header and of + * decoding the JSON outputs. + */ +export class HTTPClient { + private bc: BaseHTTPClient; + + /** + * Construct an HTTPClient from a BaseHTTPClient + * @param bc - the BaseHTTPClient used + */ + constructor(bc: BaseHTTPClient); + /** + * Construct an HTTPClient from a URL (baseServer+port) and a token + */ + constructor( + tokenHeader: TokenHeader, + baseServer: string, + port?: string | number, + defaultHeaders?: Record + ); + + constructor( + bcOrTokenHeader: BaseHTTPClient | TokenHeader, + baseServer?: string, + port?: string | number, + defaultHeaders: Record = {} + ) { + if (baseServer !== undefined) { + this.bc = new URLTokenBaseHTTPClient( + bcOrTokenHeader as TokenHeader, + baseServer, + port, + defaultHeaders + ); + } else { + this.bc = bcOrTokenHeader as BaseHTTPClient; + } + } + + /** + * Parse JSON using utils.parseJSON + * + * @param text - JSON data + * @param status - Status of the response (used in case parseJSON fails) + * @param jsonOptions - Options object to use to decode JSON responses. See + * utils.parseJSON for the options available. + */ + public static parseJSON( + text: string, + status: number, + jsonOptions: utils.ParseJSONOptions + ) { + try { + if (!text) { + return null; + } + return utils.parseJSON(text, jsonOptions); + } catch (err_) { + const err = err_ as ErrorWithAdditionalInfo; + // return the raw response if the response parsing fails + err.rawResponse = text || null; + // return the http status code if the response parsing fails + err.statusCode = status; + throw err; + } + } + + /** + * Serialize the data according to the requestHeaders + * Assumes that requestHeaders contain a key "content-type" + * If the content-type is "application/json", data is JSON serialized + * Otherwise, data needs to be either an UTF-8 string that is converted to an Uint8Array + * or an Uint8Array + * @private + */ + private static serializeData( + data: object, + requestHeaders: Record + ): Uint8Array { + if (!data) { + return new Uint8Array(0); // empty Uint8Array + } + if (requestHeaders['content-type'] === 'application/json') { + return new TextEncoder().encode(utils.stringifyJSON(data)); + } + if (typeof data === 'string') { + return new TextEncoder().encode(data); + } + if (data instanceof Uint8Array) { + return data; + } + throw new Error( + 'provided data is neither a string nor a Uint8Array and content-type is not application/json' + ); + } + + /** + * Convert a BaseHTTPClientResponse into a full HTTPClientResponse + * Parse the body in + * Modifies in place res and return the result + */ + private static prepareResponse( + res: BaseHTTPClientResponse, + format: 'application/msgpack' | 'application/json' + ): HTTPClientResponse { + const { body } = res; + let text: string | undefined; + + if (format !== 'application/msgpack') { + text = (body && new TextDecoder().decode(body)) || ''; + } + + return new HTTPClientResponse({ + ...res, + format, + text, + ok: Math.trunc(res.status / 100) === 2, + }); + } + + /** + * Prepare an error with a response + * (the type of errors BaseHTTPClient are supposed to throw) + * by adding the status and preparing the internal response + * @private + */ + private static prepareResponseError(err: any) { + if (err.response) { + // eslint-disable-next-line no-param-reassign + err.response = HTTPClient.prepareResponse( + err.response, + 'application/json' + ); + // eslint-disable-next-line no-param-reassign + err.status = err.response.status; + } + return err; + } + + /** + * Send a GET request. + * + * @param options - The options to use for the request. + * @param options.relativePath - The path of the request. + * @param options.query - An object containing the query parameters of the request. + * @param options.requestHeaders - An object containing additional request headers to use. + * or not. + * @param options.customOptions - An object containing additional options to pass to the + * underlying BaseHTTPClient instance. + * @returns Response object. + */ + async get({ + relativePath, + query, + requestHeaders, + customOptions, + }: { + relativePath: string; + query?: Query; + requestHeaders?: Record; + customOptions?: Record; + }): Promise { + const format = getAcceptFormat(query); + const fullHeaders = { ...(requestHeaders ?? {}), accept: format }; + + try { + const res = await this.bc.get( + relativePath, + query ? removeFalsyOrEmpty(query) : undefined, + fullHeaders, + customOptions + ); + + return HTTPClient.prepareResponse(res, format); + } catch (err) { + throw HTTPClient.prepareResponseError(err); + } + } + + /** + * Send a POST request. + * If no content-type present, adds the header "content-type: application/json" + * and data is serialized in JSON (if not empty) + * @param options - The options to use for the request. + */ + async post({ + relativePath, + data, + query, + requestHeaders, + customOptions, + }: { + relativePath: string; + data: any; + query?: Query; + requestHeaders?: Record; + customOptions?: Record; + }): Promise { + const fullHeaders = { + 'content-type': 'application/json', + ...tolowerCaseKeys(requestHeaders ?? {}), + }; + + try { + const res = await this.bc.post( + relativePath, + HTTPClient.serializeData(data, fullHeaders), + query, + fullHeaders, + customOptions + ); + + return HTTPClient.prepareResponse(res, 'application/json'); + } catch (err) { + throw HTTPClient.prepareResponseError(err); + } + } + + /** + * Send a DELETE request. + * If no content-type present, adds the header "content-type: application/json" + * and data is serialized in JSON (if not empty) + * @param options - The options to use for the request. + */ + async delete({ + relativePath, + data, + requestHeaders, + customOptions, + }: { + relativePath: string; + data: any; + requestHeaders?: Record; + customOptions?: Record; + }) { + const fullHeaders = { + 'content-type': 'application/json', + ...tolowerCaseKeys(requestHeaders ?? {}), + }; + + try { + const res = await this.bc.delete( + relativePath, + typeof data !== 'undefined' + ? HTTPClient.serializeData(data, fullHeaders) + : undefined, + undefined, + fullHeaders, + customOptions + ); + + return HTTPClient.prepareResponse(res, 'application/json'); + } catch (err) { + throw HTTPClient.prepareResponseError(err); + } + } +} diff --git a/packages/sdk/src/client/index.ts b/packages/sdk/src/client/index.ts new file mode 100644 index 00000000..e7ddc450 --- /dev/null +++ b/packages/sdk/src/client/index.ts @@ -0,0 +1,6 @@ +// Generics +export { default as JSONRequest } from './v2/jsonrequest.js'; +export { default as ServiceClient } from './v2/serviceClient.js'; +export * from './baseHTTPClient.js'; +export * from './urlTokenBaseHTTPClient.js'; +export * from './client.js'; diff --git a/packages/sdk/src/client/kmd.ts b/packages/sdk/src/client/kmd.ts new file mode 100644 index 00000000..7cf0c71d --- /dev/null +++ b/packages/sdk/src/client/kmd.ts @@ -0,0 +1,397 @@ +import type { Transaction } from '@algorandfoundation/algokit-transact' +import { encodeTransaction } from '@algorandfoundation/algokit-transact' +import { base64ToBytes, bytesToBase64, coerceToBytes } from '../encoding/binarydata.js' +import IntDecoding from '../types/intDecoding.js' +import { CustomTokenHeader, KMDTokenHeader } from './urlTokenBaseHTTPClient.js' +import ServiceClient from './v2/serviceClient.js' + +export class KmdClient extends ServiceClient { + constructor( + token: string | KMDTokenHeader | CustomTokenHeader, + baseServer = 'http://127.0.0.1', + port: string | number = 7833, + headers = {}, + ) { + super('X-KMD-API-Token', token, baseServer, port, headers) + } + + private async get(relativePath: string): Promise { + const res = await this.c.get({ + relativePath, + }) + return res.parseBodyAsJSON({ + // Using SAFE for all KMD endpoints because no integers in responses should ever be too big + intDecoding: IntDecoding.SAFE, + }) + } + + private async delete(relativePath: string, data: any): Promise { + const res = await this.c.delete({ + relativePath, + data, + }) + return res.parseBodyAsJSON({ + // Using SAFE for all KMD endpoints because no integers in responses should ever be too big + intDecoding: IntDecoding.SAFE, + }) + } + + private async post(relativePath: string, data: any): Promise { + const res = await this.c.post({ + relativePath, + data, + }) + return res.parseBodyAsJSON({ + // Using SAFE for all KMD endpoints because no integers in responses should ever be too big + intDecoding: IntDecoding.SAFE, + }) + } + + /** + * version returns a VersionResponse containing a list of kmd API versions supported by this running kmd instance. + */ + async versions() { + return this.get('/versions') + } + + /** + * listWallets returns a ListWalletsResponse containing the list of wallets known to kmd. Using a wallet ID + * returned from this endpoint, you can initialize a wallet handle with client.InitWalletHandle + */ + async listWallets() { + return this.get('/v1/wallets') + } + + /** + * createWallet creates a wallet with the specified name, password, driver, + * and master derivation key. If the master derivation key is blank, one is + * generated internally to kmd. CreateWallet returns a CreateWalletResponse + * containing information about the new wallet. + * @param walletName + * @param walletPassword + * @param walletDriverName + * @param walletMDK + */ + async createWallet(walletName: string, walletPassword: string, walletMDK: Uint8Array = new Uint8Array(), walletDriverName = 'sqlite') { + const req = { + wallet_name: walletName, + wallet_driver_name: walletDriverName, + wallet_password: walletPassword, + master_derivation_key: bytesToBase64(walletMDK), + } + return this.post('/v1/wallet', req) + } + + /** + * initWalletHandle accepts a wallet ID and a wallet password, and returns an + * initWalletHandleResponse containing a wallet handle token. This wallet + * handle token can be used for subsequent operations on this wallet, like key + * generation, transaction signing, etc.. WalletHandleTokens expire after a + * configurable number of seconds, and must be renewed periodically with + * RenewWalletHandle. It is good practice to call ReleaseWalletHandle when + * you're done interacting with this wallet. + * @param walletID + * @param walletPassword + */ + async initWalletHandle(walletID: string, walletPassword: string) { + const req = { + wallet_id: walletID, + wallet_password: walletPassword, + } + return this.post('/v1/wallet/init', req) + } + + /** + * releaseWalletHandle invalidates the passed wallet handle token, making + * it unusuable for subsequent wallet operations. + * @param walletHandle + */ + async releaseWalletHandle(walletHandle: string) { + const req = { + wallet_handle_token: walletHandle, + } + return this.post('/v1/wallet/release', req) + } + + /** + * renewWalletHandle accepts a wallet handle and attempts to renew it, moving + * the expiration time to some number of seconds in the future. It returns a + * RenewWalletHandleResponse containing the walletHandle and the number of + * seconds until expiration + * @param walletHandle + */ + async renewWalletHandle(walletHandle: string) { + const req = { + wallet_handle_token: walletHandle, + } + return this.post('/v1/wallet/renew', req) + } + + /** + * renameWallet accepts a wallet ID, wallet password, and a new wallet name, + * and renames the underlying wallet. + * @param walletID + * @param walletPassword + * @param newWalletName + */ + async renameWallet(walletID: string, walletPassword: string, newWalletName: string) { + const req = { + wallet_id: walletID, + wallet_password: walletPassword, + wallet_name: newWalletName, + } + return this.post('/v1/wallet/rename', req) + } + + /** + * getWallet accepts a wallet handle and returns high level information about + * this wallet in a GetWalletResponse. + * @param walletHandle + */ + async getWallet(walletHandle: string) { + const req = { + wallet_handle_token: walletHandle, + } + return this.post('/v1/wallet/info', req) + } + + /** + * exportMasterDerivationKey accepts a wallet handle and a wallet password, and + * returns an ExportMasterDerivationKeyResponse containing the master + * derivation key. This key can be used as an argument to CreateWallet in + * order to recover the keys generated by this wallet. The master derivation + * key can be encoded as a sequence of words using the mnemonic library, and + * @param walletHandle + * @param walletPassword + */ + async exportMasterDerivationKey(walletHandle: string, walletPassword: string) { + const req = { + wallet_handle_token: walletHandle, + wallet_password: walletPassword, + } + const res = await this.post('/v1/master-key/export', req) + return { + master_derivation_key: base64ToBytes(res.master_derivation_key), + } + } + + /** + * importKey accepts a wallet handle and an ed25519 private key, and imports + * the key into the wallet. It returns an ImportKeyResponse containing the + * address corresponding to this private key. + * @param walletHandle + * @param secretKey + */ + async importKey(walletHandle: string, secretKey: Uint8Array) { + const req = { + wallet_handle_token: walletHandle, + private_key: bytesToBase64(secretKey), + } + return this.post('/v1/key/import', req) + } + + /** + * exportKey accepts a wallet handle, wallet password, and address, and returns + * an ExportKeyResponse containing the ed25519 private key corresponding to the + * address stored in the wallet. + * @param walletHandle + * @param walletPassword + * @param addr + */ + async exportKey(walletHandle: string, walletPassword: string, addr: string) { + const req = { + wallet_handle_token: walletHandle, + address: addr, + wallet_password: walletPassword, + } + const res = await this.post('/v1/key/export', req) + return { private_key: base64ToBytes(res.private_key) } + } + + /** + * generateKey accepts a wallet handle, and then generates the next key in the + * wallet using its internal master derivation key. Two wallets with the same + * master derivation key will generate the same sequence of keys. + * @param walletHandle + */ + async generateKey(walletHandle: string) { + const req = { + wallet_handle_token: walletHandle, + display_mnemonic: false, + } + return this.post('/v1/key', req) + } + + /** + * deleteKey accepts a wallet handle, wallet password, and address, and deletes + * the information about this address from the wallet (including address and + * secret key). If DeleteKey is called on a key generated using GenerateKey, + * the same key will not be generated again. However, if a wallet is recovered + * using the master derivation key, a key generated in this way can be + * recovered. + * @param walletHandle + * @param walletPassword + * @param addr + */ + async deleteKey(walletHandle: string, walletPassword: string, addr: string) { + const req = { + wallet_handle_token: walletHandle, + address: addr, + wallet_password: walletPassword, + } + return this.delete('/v1/key', req) + } + + /** + * ListKeys accepts a wallet handle and returns a ListKeysResponse containing + * all of the addresses for which this wallet contains secret keys. + * @param walletHandle + */ + async listKeys(walletHandle: string) { + const req = { + wallet_handle_token: walletHandle, + } + return this.post('/v1/key/list', req) + } + + /** + * signTransaction accepts a wallet handle, wallet password, and a transaction, + * and returns and SignTransactionResponse containing an encoded, signed + * transaction. The transaction is signed using the key corresponding to the + * Sender field. + * @param walletHandle + * @param walletPassword + * @param transaction + */ + async signTransaction(walletHandle: string, walletPassword: string, transaction: Transaction) { + const req = { + wallet_handle_token: walletHandle, + wallet_password: walletPassword, + transaction: bytesToBase64(encodeTransaction(transaction)), + } + const res = await this.post('/v1/transaction/sign', req) + return base64ToBytes(res.signed_transaction) + } + + /** + * signTransactionWithSpecificPublicKey accepts a wallet handle, wallet password, a transaction, and a public key, + * and returns and SignTransactionResponse containing an encoded, signed + * transaction. The transaction is signed using the key corresponding to the + * publicKey arg. + * @param walletHandle + * @param walletPassword + * @param transaction + * @param publicKey - sign the txn with the key corresponding to publicKey (used for working with a rekeyed addr) + */ + async signTransactionWithSpecificPublicKey( + walletHandle: string, + walletPassword: string, + transaction: Transaction, + publicKey: Uint8Array | string, + ) { + const pk = coerceToBytes(publicKey) + + const req = { + wallet_handle_token: walletHandle, + wallet_password: walletPassword, + transaction: bytesToBase64(encodeTransaction(transaction)), + public_key: bytesToBase64(pk), + } + const res = await this.post('/v1/transaction/sign', req) + return base64ToBytes(res.signed_transaction) + } + + /** + * listMultisig accepts a wallet handle and returns a ListMultisigResponse + * containing the multisig addresses whose preimages are stored in this wallet. + * A preimage is the information needed to reconstruct this multisig address, + * including multisig version information, threshold information, and a list + * of public keys. + * @param walletHandle + */ + async listMultisig(walletHandle: string) { + const req = { + wallet_handle_token: walletHandle, + } + return this.post('/v1/multisig/list', req) + } + + /** + * importMultisig accepts a wallet handle and the information required to + * generate a multisig address. It derives this address, and stores all of the + * information within the wallet. It returns a ImportMultisigResponse with the + * derived address. + * @param walletHandle + * @param version + * @param threshold + * @param pks + */ + async importMultisig(walletHandle: string, version: number, threshold: number, pks: string[]) { + const req = { + wallet_handle_token: walletHandle, + multisig_version: version, + threshold, + pks, + } + return this.post('/v1/multisig/import', req) + } + + /** + * exportMultisig accepts a wallet handle, wallet password, and multisig + * address, and returns an ExportMultisigResponse containing the stored + * multisig preimage. The preimage contains all of the information necessary + * to derive the multisig address, including version, threshold, and a list of + * public keys. + * @param walletHandle + * @param walletPassword + * @param addr + */ + async exportMultisig(walletHandle: string, addr: string) { + const req = { + wallet_handle_token: walletHandle, + address: addr, + } + return this.post('/v1/multisig/export', req) + } + + /** + * signMultisigTransaction accepts a wallet handle, wallet password, + * transaction, public key (*not* an address), and an optional partial + * MultisigSig. It looks up the secret key corresponding to the public key, and + * returns a SignMultisigTransactionResponse containing a MultisigSig with a + * signature by the secret key included. + * @param walletHandle + * @param pw + * @param tx + * @param pk + * @param partial + */ + async signMultisigTransaction(walletHandle: string, pw: string, transaction: Transaction, pk: Uint8Array | string, partial: string) { + const pubkey = coerceToBytes(pk) + const req = { + wallet_handle_token: walletHandle, + transaction: bytesToBase64(encodeTransaction(transaction)), + public_key: bytesToBase64(pubkey), + partial_multisig: partial, + wallet_password: pw, + } + return this.post('/v1/multisig/sign', req) + } + + /** + * deleteMultisig accepts a wallet handle, wallet password, and multisig + * address, and deletes the information about this multisig address from the + * wallet (including address and secret key). + * @param walletHandle + * @param walletPassword + * @param addr + */ + async deleteMultisig(walletHandle: string, walletPassword: string, addr: string) { + const req = { + wallet_handle_token: walletHandle, + address: addr, + wallet_password: walletPassword, + } + return this.delete('/v1/multisig', req) + } +} diff --git a/packages/sdk/src/client/urlTokenBaseHTTPClient.ts b/packages/sdk/src/client/urlTokenBaseHTTPClient.ts new file mode 100644 index 00000000..9c0af8a5 --- /dev/null +++ b/packages/sdk/src/client/urlTokenBaseHTTPClient.ts @@ -0,0 +1,225 @@ +import { + BaseHTTPClient, + BaseHTTPClientResponse, + BaseHTTPClientError, + Query, +} from './baseHTTPClient.js'; + +export interface AlgodTokenHeader { + 'X-Algo-API-Token': string; +} + +export interface IndexerTokenHeader { + 'X-Indexer-API-Token': string; +} + +export interface KMDTokenHeader { + 'X-KMD-API-Token': string; +} + +export interface CustomTokenHeader { + [headerName: string]: string; +} + +class URLTokenBaseHTTPError extends Error implements BaseHTTPClientError { + constructor( + message: string, + public response: BaseHTTPClientResponse + ) { + super(message); + this.name = 'URLTokenBaseHTTPError'; + this.response = response; + } +} + +export type TokenHeader = + | AlgodTokenHeader + | IndexerTokenHeader + | KMDTokenHeader + | CustomTokenHeader; + +/** + * Implementation of BaseHTTPClient that uses a URL and a token + * and make the REST queries using fetch. + * This is the default implementation of BaseHTTPClient. + * + * Additional fetch options can be configured by using the `customOptions` parameter on + * get/post/delete requests. + */ +export class URLTokenBaseHTTPClient implements BaseHTTPClient { + private readonly baseURL: URL; + private readonly tokenHeader: TokenHeader; + + constructor( + tokenHeader: TokenHeader, + baseServer: string, + port?: string | number, + private defaultHeaders: Record = {} + ) { + // Append a trailing slash so we can use relative paths. Without the trailing + // slash, the last path segment will be replaced by the relative path. See + // usage in `addressWithPath`. + const fixedBaseServer = baseServer.endsWith('/') + ? baseServer + : `${baseServer}/`; + const baseServerURL = new URL(fixedBaseServer); + if (typeof port !== 'undefined') { + baseServerURL.port = port.toString(); + } + + if (baseServerURL.protocol.length === 0) { + throw new Error('Invalid base server URL, protocol must be defined.'); + } + + this.baseURL = baseServerURL; + this.tokenHeader = tokenHeader; + } + + /** + * Compute the URL for a path relative to the instance's address + * @param relativePath - A path string + * @param query - An optional key-value object of query parameters to add to the URL. If the + * relativePath already has query parameters on it, the additional parameters defined here will + * be added to the URL without modifying those (unless a key collision occurs). + * @returns A URL string + */ + private getURL(relativePath: string, query?: Query): string { + let fixedRelativePath: string; + if (relativePath.startsWith('./')) { + fixedRelativePath = relativePath; + } else if (relativePath.startsWith('/')) { + fixedRelativePath = `.${relativePath}`; + } else { + fixedRelativePath = `./${relativePath}`; + } + const address = new URL(fixedRelativePath, this.baseURL); + if (query) { + for (const [key, value] of Object.entries(query)) { + address.searchParams.set(key, value.toString()); + } + } + return address.toString(); + } + + private static formatFetchResponseHeaders( + headers: Headers + ): Record { + const headersObj: Record = {}; + headers.forEach((key, value) => { + headersObj[key] = value; + }); + return headersObj; + } + + private static async checkHttpError(res: Response) { + if (res.ok) { + return; + } + + let body: Uint8Array | undefined; + let bodyErrorMessage: string | undefined; + + try { + body = new Uint8Array(await res.arrayBuffer()); + const decoded: Record = JSON.parse( + new TextDecoder().decode(body) + ); + if (decoded.message) { + bodyErrorMessage = decoded.message; + } + } catch (_) { + // ignore any error that happened while we are parsing the error response + } + + let message = `Network request error. Received status ${res.status} (${res.statusText})`; + if (bodyErrorMessage) { + message += `: ${bodyErrorMessage}`; + } + + throw new URLTokenBaseHTTPError(message, { + body: body ?? new Uint8Array(), + status: res.status, + headers: URLTokenBaseHTTPClient.formatFetchResponseHeaders(res.headers), + }); + } + + private static async formatFetchResponse( + res: Response + ): Promise { + await this.checkHttpError(res); + return { + body: new Uint8Array(await res.arrayBuffer()), + status: res.status, + headers: URLTokenBaseHTTPClient.formatFetchResponseHeaders(res.headers), + }; + } + + async get( + relativePath: string, + query?: Query, + requestHeaders?: Record, + customOptions?: Record + ): Promise { + // Expand headers for use in fetch + const headers = { + ...this.tokenHeader, + ...this.defaultHeaders, + ...(requestHeaders ?? {}), + }; + + const res = await fetch(this.getURL(relativePath, query), { + headers, + ...(customOptions ?? {}), + }); + + return URLTokenBaseHTTPClient.formatFetchResponse(res); + } + + async post( + relativePath: string, + data: Uint8Array, + query?: Query, + requestHeaders?: Record, + customOptions?: Record + ): Promise { + // Expand headers for use in fetch + const headers = { + ...this.tokenHeader, + ...this.defaultHeaders, + ...(requestHeaders ?? {}), + }; + + const res = await fetch(this.getURL(relativePath, query), { + method: 'POST', + body: data, + headers, + ...(customOptions ?? {}), + }); + + return URLTokenBaseHTTPClient.formatFetchResponse(res); + } + + async delete( + relativePath: string, + data?: Uint8Array, + query?: Query, + requestHeaders?: Record, + customOptions?: Record + ): Promise { + // Expand headers for use in fetch + const headers = { + ...this.tokenHeader, + ...this.defaultHeaders, + ...(requestHeaders ?? {}), + }; + + const res = await fetch(this.getURL(relativePath, query), { + method: 'DELETE', + body: data, + headers, + ...(customOptions ?? {}), + }); + + return URLTokenBaseHTTPClient.formatFetchResponse(res); + } +} diff --git a/packages/sdk/src/client/v2/indexer/index.ts b/packages/sdk/src/client/v2/indexer/index.ts new file mode 100644 index 00000000..e2bc6ce2 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/index.ts @@ -0,0 +1,3 @@ +// Indexer +export { IndexerClient } from './indexer.js'; +export * from './models/types.js'; diff --git a/packages/sdk/src/client/v2/indexer/indexer.ts b/packages/sdk/src/client/v2/indexer/indexer.ts new file mode 100644 index 00000000..23b31105 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/indexer.ts @@ -0,0 +1,440 @@ +import ServiceClient from '../serviceClient.js'; +import MakeHealthCheck from './makeHealthCheck.js'; +import LookupAssetBalances from './lookupAssetBalances.js'; +import LookupAssetTransactions from './lookupAssetTransactions.js'; +import LookupAccountTransactions from './lookupAccountTransactions.js'; +import LookupBlock from './lookupBlock.js'; +import LookupTransactionByID from './lookupTransactionByID.js'; +import LookupAccountByID from './lookupAccountByID.js'; +import LookupAccountAssets from './lookupAccountAssets.js'; +import LookupAccountCreatedAssets from './lookupAccountCreatedAssets.js'; +import LookupAccountAppLocalStates from './lookupAccountAppLocalStates.js'; +import LookupAccountCreatedApplications from './lookupAccountCreatedApplications.js'; +import LookupAssetByID from './lookupAssetByID.js'; +import LookupApplications from './lookupApplications.js'; +import LookupApplicationLogs from './lookupApplicationLogs.js'; +import LookupApplicationBoxByIDandName from './lookupApplicationBoxByIDandName.js'; +import SearchAccounts from './searchAccounts.js'; +import SearchForBlockHeaders from './searchForBlockHeaders.js'; +import SearchForTransactions from './searchForTransactions.js'; +import SearchForAssets from './searchForAssets.js'; +import SearchForApplications from './searchForApplications.js'; +import SearchForApplicationBoxes from './searchForApplicationBoxes.js'; +import { BaseHTTPClient } from '../../baseHTTPClient.js'; +import { + CustomTokenHeader, + IndexerTokenHeader, +} from '../../urlTokenBaseHTTPClient.js'; +import { Address } from '../../../encoding/address.js'; + +/** + * The Indexer provides a REST API interface of API calls to support searching the Algorand Blockchain. + * + * The Indexer REST APIs retrieve the blockchain data from a PostgreSQL compatible database that must be populated. + * + * This database is populated using the same indexer instance or a separate instance of the indexer which must connect to the algod process of a running Algorand node to read block data. + * + * This node must also be an Archival node to make searching the entire blockchain possible. + * + * #### Relevant Information + * [Learn more about Indexer](https://developer.algorand.org/docs/get-details/indexer/) + * + * [Run Indexer in Postman OAS3](https://developer.algorand.org/docs/rest-apis/restendpoints/#algod-indexer-and-kmd-rest-endpoints) + */ +export class IndexerClient extends ServiceClient { + /** + * Create an IndexerClient from + * * either a token, baseServer, port, and optional headers + * * or a base client server for interoperability with external dApp wallets + * + * #### Example + * ```typescript + * const token = ""; + * const server = "http://localhost"; + * const port = 8980; + * const indexerClient = new algosdk.Indexer(token, server, port); + * ``` + * @remarks + * The above configuration is for a sandbox private network. + * For applications on production, you are encouraged to run your own node with indexer, or use an Algorand REST API provider with a dedicated API key. + * + * @param tokenOrBaseClient - The API token for the Indexer API + * @param baseServer - REST endpoint + * @param port - Port number if specifically configured by the server + * @param headers - Optional headers + */ + constructor( + tokenOrBaseClient: + | string + | IndexerTokenHeader + | CustomTokenHeader + | BaseHTTPClient, + baseServer = 'http://127.0.0.1', + port: string | number = 8080, + headers: Record = {} + ) { + super('X-Indexer-API-Token', tokenOrBaseClient, baseServer, port, headers); + } + + /** + * Returns the health object for the service. + * Returns 200 if healthy. + * + * #### Example + * ```typescript + * const health = await indexerClient.makeHealthCheck().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-health) + * @category GET + */ + makeHealthCheck() { + return new MakeHealthCheck(this.c); + } + + /** + * Returns the list of accounts who hold the given asset and their balance. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetBalances = await indexerClient.lookupAssetBalances(assetId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assetsasset-idbalances) + * @param index - The asset ID to look up. + * @category GET + */ + lookupAssetBalances(index: number | bigint) { + return new LookupAssetBalances(this.c, index); + } + + /** + * Returns transactions relating to the given asset. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetTxns = await indexerClient.lookupAssetTransactions(assetId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assetsasset-idtransactions) + * @param index - The asset ID to look up. + * @category GET + */ + lookupAssetTransactions(index: number | bigint) { + return new LookupAssetTransactions(this.c, index); + } + + /** + * Returns transactions relating to the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient.lookupAccountTransactions(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idtransactions) + * @param account - The address of the account. + * @category GET + */ + lookupAccountTransactions(account: string | Address) { + return new LookupAccountTransactions(this.c, account); + } + + /** + * Returns the block for the passed round. + * + * #### Example + * ```typescript + * const targetBlock = 18309917; + * const blockInfo = await indexerClient.lookupBlock(targetBlock).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2blocksround-number) + * @param round - The number of the round to look up. + * @category GET + */ + lookupBlock(round: number | bigint) { + return new LookupBlock(this.c, round); + } + + /** + * Returns information about the given transaction. + * + * #### Example + * ```typescript + * const txnId = "MEUOC4RQJB23CQZRFRKYEI6WBO73VTTPST5A7B3S5OKBUY6LFUDA"; + * const txnInfo = await indexerClient.lookupTransactionByID(txnId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2transactionstxid) + * @param txID - The ID of the transaction to look up. + * @category GET + */ + lookupTransactionByID(txID: string) { + return new LookupTransactionByID(this.c, txID); + } + + /** + * Returns information about the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await indexerClient.lookupAccountByID(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-id) + * @param account - The address of the account to look up. + * @category GET + */ + lookupAccountByID(account: string | Address) { + return new LookupAccountByID(this.c, account); + } + + /** + * Returns asset about the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAssets = await indexerClient.lookupAccountAssets(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idassets) + * @param account - The address of the account to look up. + * @category GET + */ + lookupAccountAssets(account: string | Address) { + return new LookupAccountAssets(this.c, account); + } + + /** + * Returns asset information created by the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountCreatedAssets = await indexerClient.lookupAccountCreatedAssets(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idcreated-assets) + * @param account - The address of the account to look up. + * @category GET + */ + lookupAccountCreatedAssets(account: string | Address) { + return new LookupAccountCreatedAssets(this.c, account); + } + + /** + * Returns application local state about the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAppLocalStates = await indexerClient.lookupAccountAppLocalStates(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idapps-local-state) + * @param account - The address of the account to look up. + * @category GET + */ + lookupAccountAppLocalStates(account: string | Address) { + return new LookupAccountAppLocalStates(this.c, account); + } + + /** + * Returns application information created by the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountCreatedApps = await indexerClient.lookupAccountCreatedApplications(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idcreated-applications) + * @param account - The address of the account to look up. + * @category GET + */ + lookupAccountCreatedApplications(account: string | Address) { + return new LookupAccountCreatedApplications(this.c, account); + } + + /** + * Returns information about the passed asset. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetInfo = await indexerClient.lookupAssetByID(assetId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assetsasset-id) + * @param index - The ID of the asset ot look up. + * @category GET + */ + lookupAssetByID(index: number | bigint) { + return new LookupAssetByID(this.c, index); + } + + /** + * Returns information about the passed application. + * + * #### Example + * ```typescript + * const appId = 60553466; + * const appInfo = await indexerClient.lookupApplications(appId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-id) + * @param index - The ID of the application to look up. + * @category GET + */ + lookupApplications(index: number | bigint) { + return new LookupApplications(this.c, index); + } + + /** + * Returns log messages generated by the passed in application. + * + * #### Example + * ```typescript + * const appId = 60553466; + * const appLogs = await indexerClient.lookupApplicationLogs(appId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-idlogs) + * @param appID - The ID of the application which generated the logs. + * @category GET + */ + lookupApplicationLogs(appID: number | bigint) { + return new LookupApplicationLogs(this.c, appID); + } + + /** + * Returns information about indexed accounts. + * + * #### Example + * ```typescript + * const accounts = await indexerClient.searchAccounts().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accounts) + * @category GET + */ + searchAccounts() { + return new SearchAccounts(this.c); + } + + /** + * Returns information about indexed block headers. + * + * #### Example + * ```typescript + * const bhs = await indexerClient.searchForBlockHeaders().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2block-headers) + * @category GET + */ + searchForBlockHeaders() { + return new SearchForBlockHeaders(this.c); + } + + /** + * Returns information about indexed transactions. + * + * #### Example + * ```typescript + * const txns = await indexerClient.searchForTransactions().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2transactions) + * @category GET + */ + searchForTransactions() { + return new SearchForTransactions(this.c); + } + + /** + * Returns information about indexed assets. + * + * #### Example + * ```typescript + * const assets = await indexerClient.searchForAssets().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assets) + * @category GET + */ + searchForAssets() { + return new SearchForAssets(this.c); + } + + /** + * Returns information about indexed applications. + * + * #### Example + * ```typescript + * const apps = await indexerClient.searchForApplications().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applications) + * @category GET + */ + searchForApplications() { + return new SearchForApplications(this.c); + } + + /** + * Returns information about indexed application boxes. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const appID = 1234; + * + * const responsePage1 = await indexerClient + * .searchForApplicationBoxes(appID) + * .limit(maxResults) + * .do(); + * const boxNamesPage1 = responsePage1.boxes.map(box => box.name); + * + * const responsePage2 = await indexerClient + * .searchForApplicationBoxes(appID) + * .limit(maxResults) + * .nextToken(responsePage1.nextToken) + * .do(); + * const boxNamesPage2 = responsePage2.boxes.map(box => box.name); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-idboxes) + * @param appID - The ID of the application with boxes. + * @category GET + */ + searchForApplicationBoxes(appID: number | bigint) { + return new SearchForApplicationBoxes(this.c, appID); + } + + /** + * Returns information about the application box given its name. + * + * #### Example + * ```typescript + * const boxName = Buffer.from("foo"); + * const boxResponse = await indexerClient + * .LookupApplicationBoxByIDandName(1234, boxName) + * .do(); + * const boxValue = boxResponse.value; + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-idbox) + * @param appID - The ID of the application with boxes. + * @category GET + */ + lookupApplicationBoxByIDandName(appID: number | bigint, boxName: Uint8Array) { + return new LookupApplicationBoxByIDandName(this.c, appID, boxName); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupAccountAppLocalStates.ts b/packages/sdk/src/client/v2/indexer/lookupAccountAppLocalStates.ts new file mode 100644 index 00000000..61901c52 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupAccountAppLocalStates.ts @@ -0,0 +1,145 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { ApplicationLocalStatesResponse } from './models/types.js'; + +export default class LookupAccountAppLocalStates extends JSONRequest { + private account: string | Address; + + /** + * Returns application local state about the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAppLocalStates = await indexerClient.lookupAccountAppLocalStates(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idapps-local-state) + * @param account - The address of the account to look up. + * @category GET + */ + constructor(c: HTTPClient, account: string | Address) { + super(c); + this.account = account.toString(); + } + + /** + * @returns `/v2/accounts/${account}/apps-local-state` + */ + path() { + return `/v2/accounts/${this.account}/apps-local-state`; + } + + /** + * Add a limit for filter. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * const accountAssets = await indexerClient + * .lookupAccountAppLocalStates(address) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Specify round to filter with. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const targetBlock = 18309917; + * const accountAssets = await indexerClient + * .lookupAccountAppLocalStates(address) + * .round(targetBlock) + * .do(); + * ``` + * @param round + * @category query + */ + round(round: number | bigint) { + this.query.round = round; + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * + * const accountAssetsPage1 = await indexerClient + * .lookupAccountAppLocalStates(address) + * .limit(maxResults) + * .do(); + * + * const accountAssetsPage2 = await indexerClient + * .lookupAccountAppLocalStates(address) + * .limit(maxResults) + * .next(accountAssetsPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Include all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAssets = await indexerClient + * .lookupAccountAppLocalStates(address) + * .includeAll(false) + * .do(); + * ``` + * @param value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + /** + * Specify an applicationID to search for. + * + * #### Example + * ```typescript + * const applicationID = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountApplications = await indexerClient + * .lookupAccountAppLocalStates(address) + * .applicationID(applicationID) + * .do(); + * ``` + * @param index - the applicationID + * @category query + */ + applicationID(index: number | bigint) { + this.query['application-id'] = index; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): ApplicationLocalStatesResponse { + return decodeJSON(response.getJSONText(), ApplicationLocalStatesResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupAccountAssets.ts b/packages/sdk/src/client/v2/indexer/lookupAccountAssets.ts new file mode 100644 index 00000000..58e6aef5 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupAccountAssets.ts @@ -0,0 +1,146 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { AssetHoldingsResponse } from './models/types.js'; + +export default class LookupAccountAssets extends JSONRequest { + private account: string; + + /** + * Returns asset about the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAssets = await indexerClient.lookupAccountAssets(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idassets) + * @param account - The address of the account to look up. + * @category GET + */ + constructor(c: HTTPClient, account: string | Address) { + super(c); + this.account = account.toString(); + } + + /** + * @returns `/v2/accounts/${account}/assets` + */ + path() { + return `/v2/accounts/${this.account}/assets`; + } + + /** + * Add a limit for filter. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * const accountAssets = await indexerClient + * .lookupAccountAssets(address) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Specify round to filter with. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const targetBlock = 18309917; + * const accountAssets = await indexerClient + * .lookupAccountAssets(address) + * .round(targetBlock) + * .do(); + * ``` + * @param round + * @category query + */ + round(round: number | bigint) { + this.query.round = round; + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * + * const accountAssetsPage1 = await indexerClient + * .lookupAccountAssets(address) + * .limit(maxResults) + * .do(); + * + * const accountAssetsPage2 = await indexerClient + * .lookupAccountAssets(address) + * .limit(maxResults) + * .next(accountAssetsPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Include all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAssets = await indexerClient + * .lookupAccountAssets(address) + * .includeAll(false) + * .do(); + * ``` + * @param value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + /** + * Specify an assetID to search for. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const assetAssets = await indexerClient + * .lookupAccountAssets(address) + * .assetId(assetId) + * .do(); + * ``` + * @param index - the assetID + * @category query + */ + assetId(index: number | bigint) { + this.query['asset-id'] = index; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AssetHoldingsResponse { + return decodeJSON(response.getJSONText(), AssetHoldingsResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupAccountByID.ts b/packages/sdk/src/client/v2/indexer/lookupAccountByID.ts new file mode 100644 index 00000000..60393d95 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupAccountByID.ts @@ -0,0 +1,114 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { AccountResponse } from './models/types.js'; + +export default class LookupAccountByID extends JSONRequest { + private account: string; + + /** + * Returns information about the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await indexerClient.lookupAccountByID(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-id) + * @param account - The address of the account to look up. + * @category GET + */ + constructor(c: HTTPClient, account: string | Address) { + super(c); + this.account = account.toString(); + } + + /** + * @returns `/v2/accounts/${account}` + */ + path() { + return `/v2/accounts/${this.account}`; + } + + /** + * Specify round to filter with. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const targetBlock = 18309917; + * const accountInfo = await indexerClient + * .lookupAccountByID(address) + * .round(targetBlock) + * .do(); + * ``` + * @param round + */ + round(round: number | bigint) { + this.query.round = round; + return this; + } + + /** + * Include all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates. + * + * #### Example 1 + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await indexerClient + * .lookupAccountByID(address) + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await indexerClient + * .lookupAccountByID(address) + * .includeAll() + * .do(); + * ``` + * @param value + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + /** + * Exclude additional items such as asset holdings, application local data stored for this account, asset parameters created by this account, and application parameters created by this account. + * + * #### Example 1 + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await indexerClient + * .lookupAccountByID(address) + * .exclude("all") + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountInfo = await indexerClient + * .lookupAccountByID(address) + * .exclude("assets,created-assets") + * .do(); + * ``` + * @remarks By default, it behaves as exclude=none + * @param exclude - Array of `all`, `assets`, `created-assets`, `apps-local-state`, `created-apps`, `none` + * @category query + */ + exclude(exclude: string) { + this.query.exclude = exclude; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AccountResponse { + return decodeJSON(response.getJSONText(), AccountResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupAccountCreatedApplications.ts b/packages/sdk/src/client/v2/indexer/lookupAccountCreatedApplications.ts new file mode 100644 index 00000000..1fad042d --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupAccountCreatedApplications.ts @@ -0,0 +1,146 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { ApplicationsResponse } from './models/types.js'; + +export default class LookupAccountCreatedApplications extends JSONRequest { + private account: string; + + /** + * Returns application information created by the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountCreatedApps = await indexerClient.lookupAccountCreatedApplications(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idcreated-applications) + * @param account - The address of the account to look up. + * @category GET + */ + constructor(c: HTTPClient, account: string | Address) { + super(c); + this.account = account.toString(); + } + + /** + * @returns `/v2/accounts/${account}/created-applications` + */ + path() { + return `/v2/accounts/${this.account}/created-applications`; + } + + /** + * Add a limit for filter. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * const accountAssets = await indexerClient + * .lookupAccountCreatedApplications(address) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Specify round to filter with. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const targetBlock = 18309917; + * const accountAssets = await indexerClient + * .lookupAccountCreatedApplications(address) + * .round(targetBlock) + * .do(); + * ``` + * @param round + * @category query + */ + round(round: number | bigint) { + this.query.round = round; + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * + * const accountAssetsPage1 = await indexerClient + * .lookupAccountCreatedApplications(address) + * .limit(maxResults) + * .do(); + * + * const accountAssetsPage2 = await indexerClient + * .lookupAccountCreatedApplications(address) + * .limit(maxResults) + * .next(accountAssetsPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Include all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAssets = await indexerClient + * .lookupAccountCreatedApplications(address) + * .includeAll(false) + * .do(); + * ``` + * @param value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + /** + * Specify an applicationID to search for. + * + * #### Example + * ```typescript + * const applicationID = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountApplications = await indexerClient + * .lookupAccountAppLocalStates(address) + * .applicationID(applicationID) + * .do(); + * ``` + * @param index - the applicationID + * @category query + */ + applicationID(index: number | bigint) { + this.query['application-id'] = index; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): ApplicationsResponse { + return decodeJSON(response.getJSONText(), ApplicationsResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupAccountCreatedAssets.ts b/packages/sdk/src/client/v2/indexer/lookupAccountCreatedAssets.ts new file mode 100644 index 00000000..7414e38a --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupAccountCreatedAssets.ts @@ -0,0 +1,147 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { AssetsResponse } from './models/types.js'; + +export default class LookupAccountCreatedAssets extends JSONRequest { + private account: string; + + /** + * Returns asset information created by the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountCreatedAssets = await indexerClient.lookupAccountCreatedAssets(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idcreated-assets) + * @param account - The address of the account to look up. + * @category GET + */ + constructor(c: HTTPClient, account: string | Address) { + super(c); + this.account = account.toString(); + } + + /** + * @returns `/v2/accounts/${account}/created-assets` + */ + path() { + return `/v2/accounts/${this.account}/created-assets`; + } + + /** + * Add a limit for filter. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * const accountAssets = await indexerClient + * .lookupAccountCreatedAssets(address) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Specify round to filter with. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const targetBlock = 18309917; + * const accountAssets = await indexerClient + * .lookupAccountCreatedAssets(address) + * .round(targetBlock) + * .do(); + * ``` + * @param round + * @category query + */ + round(round: number | bigint) { + this.query.round = round; + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const maxResults = 20; + * + * const accountAssetsPage1 = await indexerClient + * .lookupAccountCreatedAssets(address) + * .limit(maxResults) + * .do(); + * ``` + * + * const accountAssetsPage2 = await indexerClient + * .lookupAccountCreatedAssets(address) + * .limit(maxResults) + * .next(accountAssetsPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Include all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountAssets = await indexerClient + * .lookupAccountCreatedAssets(address) + * .includeAll(false) + * .do(); + * ``` + * @param value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + /** + * Specify an assetID to search for. + * + * #### Example + * ```typescript + * const assetID = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const assetAssets = await indexerClient + * .lookupAccountCreatedAssets(address) + * .assetID(assetID) + * .do(); + * ``` + * @param index - the assetID + * @category query + */ + assetID(index: number | bigint) { + this.query['asset-id'] = index; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AssetsResponse { + return decodeJSON(response.getJSONText(), AssetsResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupAccountTransactions.ts b/packages/sdk/src/client/v2/indexer/lookupAccountTransactions.ts new file mode 100644 index 00000000..c9c54fc7 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupAccountTransactions.ts @@ -0,0 +1,400 @@ +import { bytesToBase64 } from '../../../encoding/binarydata.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import JSONRequest from '../jsonrequest.js'; +import { Address } from '../../../encoding/address.js'; +import { TransactionsResponse } from './models/types.js'; + +/** + * Accept base64 string or Uint8Array and output base64 string + * @param data - Base64 string or Uint8Array + * @returns The inputted base64 string, or a base64 string representation of the Uint8Array + */ +export function base64StringFunnel(data: Uint8Array | string) { + if (typeof data === 'string') { + return data; + } + return bytesToBase64(data); +} + +export default class LookupAccountTransactions extends JSONRequest { + private account: string; + + /** + * Returns transactions relating to the given account. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient.lookupAccountTransactions(address).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accountsaccount-idtransactions) + * @param account - The address of the account. + */ + constructor(c: HTTPClient, account: string | Address) { + super(c); + this.account = account.toString(); + } + + /** + * @returns `/v2/accounts/${account}/transactions` + */ + path() { + return `/v2/accounts/${this.account}/transactions`; + } + + /** + * Specifies a prefix which must be contained in the note field. + * + * #### Example + * ```typescript + * const notePrefixBase64Encoded = "Y3JlYXRl"; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .notePrefix(notePrefixBase64Encoded) + * .do(); + * ``` + * + * @param prefix - base64 string or uint8array + * @category query + */ + notePrefix(prefix: Uint8Array | string) { + this.query['note-prefix'] = base64StringFunnel(prefix); + return this; + } + + /** + * Type of transaction to filter with. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .txType("appl") + * .do(); + * ``` + * + * @param type - one of `pay`, `keyreg`, `acfg`, `axfer`, `afrz`, `appl`, `stpf` + * @category query + */ + txType(type: string) { + this.query['tx-type'] = type; + return this; + } + + /** + * Type of signature to filter with. + * - sig: Standard + * - msig: MultiSig + * - lsig: LogicSig + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .sigType("msig") + * .do(); + * ``` + * + * @param type - one of `sig`, `msig`, `lsig` + * @category query + */ + sigType(type: string) { + this.query['sig-type'] = type; + return this; + } + + /** + * Lookup the specific transaction by ID. + * + * #### Example + * ```typescript + * const txId = "MEUOC4RQJB23CQZRFRKYEI6WBO73VTTPST5A7B3S5OKBUY6LFUDA"; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .txid(txId) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupTransactionByID(txnId).do()` + * @param txid + * @category query + */ + txid(txid: string) { + this.query.txid = txid; + return this; + } + + /** + * Include results for the specified round. + * + * #### Example + * ```typescript + * const targetBlock = 18309917; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .round(targetBlock) + * .do(); + * ``` + * + * @param round + * @category query + */ + round(round: number | bigint) { + this.query.round = round; + return this; + } + + /** + * Include results at or after the specified min-round. + * + * #### Example + * ```typescript + * const minRound = 18309917; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .minRound(minRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + minRound(round: number | bigint) { + this.query['min-round'] = round; + return this; + } + + /** + * Include results at or before the specified max-round. + * + * #### Example + * ```typescript + * const maxRound = 18309917; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .maxRound(maxRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + maxRound(round: number | bigint) { + this.query['max-round'] = round; + return this; + } + + /** + * Asset ID to filter with. + * + * #### Example + * ```typescript + * const assetID = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .assetID(assetID) + * .do(); + * ``` + * + * @param id + * @category query + */ + assetID(id: number | bigint) { + this.query['asset-id'] = id; + return this; + } + + /** + * Maximum number of results to return. + * + * #### Example + * ```typescript + * const maxResults = 25; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Include results before the given time. + * + * #### Example + * ```typescript + * const beforeTime = "2022-02-02T20:20:22.02Z"; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .beforeTime(beforeTime) + * .do(); + * ``` + * + * @param before - rfc3339 string or Date object + * @category query + */ + beforeTime(before: string | Date) { + this.query['before-time'] = + before instanceof Date ? before.toISOString() : before; + return this; + } + + /** + * Include results after the given time. + * + * #### Example + * ```typescript + * const afterTime = "2022-10-21T00:00:11.55Z"; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .afterTime(afterTime) + * .do(); + * ``` + * + * @param after - rfc3339 string or Date object + * @category query + */ + afterTime(after: string | Date) { + this.query['after-time'] = + after instanceof Date ? after.toISOString() : after; + return this; + } + + /** + * Filtered results should have an amount greater than this value, as int, representing microAlgos, unless an asset-id is provided, in which case units are in the asset's units. + * + * #### Example 1 + * ```typescript + * const minBalance = 300000; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetID = 163650; + * const minBalance = 300000; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .assetID(assetID) + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * + * @param greater + * @category query + */ + currencyGreaterThan(greater: number | bigint) { + // We convert the following to a string for now to correctly include zero values in request parameters. + this.query['currency-greater-than'] = greater.toString(); + return this; + } + + /** + * Filtered results should have an amount less than this value, as int, representing microAlgos, unless an asset-id is provided, in which case units are in the asset's units. + * + * #### Example 1 + * ```typescript + * const maxBalance = 500000; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetID = 163650; + * const maxBalance = 500000; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .assetID(assetID) + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * @param lesser + * @category query + */ + currencyLessThan(lesser: number | bigint) { + this.query['currency-less-than'] = lesser; + return this; + } + + /** + * The next page of results. Use the next token provided by the previous results. + * + * #### Example + * ```typescript + * const maxResults = 25; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * + * const accountTxnsPage1 = await indexerClient + * .lookupAccountTransactions(address) + * .limit(maxResults) + * .do(); + * + * const accountTxnsPage2 = await indexerClient + * .lookupAccountTransactions(address) + * .limit(maxResults) + * .nextToken(accountTxnsPage1["next-token"]) + * .do(); + * ``` + * + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Whether or not to include rekeying transactions. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accountTxns = await indexerClient + * .lookupAccountTransactions(address) + * .rekeyTo(false) + * .do(); + * ``` + * + * @param rekeyTo + * @category query + */ + rekeyTo(rekeyTo: boolean) { + this.query['rekey-to'] = rekeyTo; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): TransactionsResponse { + return decodeJSON(response.getJSONText(), TransactionsResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts b/packages/sdk/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts new file mode 100644 index 00000000..90b5267a --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupApplicationBoxByIDandName.ts @@ -0,0 +1,45 @@ +import { bytesToBase64 } from '../../../encoding/binarydata.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import JSONRequest from '../jsonrequest.js'; +import { Box } from './models/types.js'; + +export default class LookupApplicationBoxByIDandName extends JSONRequest { + private index: bigint; + + /** + * Returns information about indexed application boxes. + * + * #### Example + * ```typescript + * const boxName = Buffer.from("foo"); + * const boxResponse = await indexerClient + * .LookupApplicationBoxByIDandName(1234, boxName) + * .do(); + * const boxValue = boxResponse.value; + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-idbox) + * @oaram index - application index. + * @category GET + */ + constructor(c: HTTPClient, index: number | bigint, boxName: Uint8Array) { + super(c); + this.index = BigInt(index); + // Encode query in base64 format and append the encoding prefix. + const encodedName = bytesToBase64(boxName); + this.query.name = encodeURI(`b64:${encodedName}`); + } + + /** + * @returns `/v2/applications/${index}/box` + */ + path() { + return `/v2/applications/${this.index}/box`; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): Box { + return decodeJSON(response.getJSONText(), Box); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupApplicationLogs.ts b/packages/sdk/src/client/v2/indexer/lookupApplicationLogs.ts new file mode 100644 index 00000000..b64d8011 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupApplicationLogs.ts @@ -0,0 +1,164 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { ApplicationLogsResponse } from './models/types.js'; + +export default class LookupApplicationLogs extends JSONRequest { + private appID: bigint; + + /** + * Returns log messages generated by the passed in application. + * + * #### Example + * ```typescript + * const appId = 60553466; + * const appLogs = await indexerClient.lookupApplicationLogs(appId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-idlogs) + * @param appID - The ID of the application which generated the logs. + * @category GET + */ + constructor(c: HTTPClient, appID: number | bigint) { + super(c); + this.appID = BigInt(appID); + } + + /** + * @returns `/v2/applications/${appID}/logs` + */ + path() { + return `/v2/applications/${this.appID}/logs`; + } + + /** + * Limit results for pagination. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const appLogs = await indexerClient + * .lookupApplicationLogs(appId) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Include results at or after the specified min-round. + * + * #### Example + * ```typescript + * const minRound = 18309917; + * const appLogs = await indexerClient + * .lookupApplicationLogs(appId) + * .minRound(minRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + minRound(round: number | bigint) { + this.query['min-round'] = round; + return this; + } + + /** + * Include results at or before the specified max-round. + * + * #### Example + * ```typescript + * const maxRound = 18309917; + * const appLogs = await indexerClient + * .lookupApplicationLogs(appId) + * .maxRound(maxRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + maxRound(round: number | bigint) { + this.query['max-round'] = round; + return this; + } + + /** + * The next page of results. + * + * #### Example + * ```typescript + * const maxResults = 25; + * + * const appLogsPage1 = await indexerClient + * .lookupApplicationLogs(appId) + * .limit(maxResults) + * .do(); + * + * const appLogsPage2 = await indexerClient + * .lookupApplicationLogs(appId) + * .limit(maxResults) + * .nextToken(appLogsPage1["next-token"]) + * .do(); + * ``` + * + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Only include transactions with this sender address. + * + * #### Example + * ```typescript + * const sender = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const appLogs = await indexerClient + * .lookupApplicationLogs(appId) + * .sender(sender) + * .do(); + * ``` + * + * @param senderAddress + * @category query + */ + sender(senderAddress: string) { + this.query['sender-address'] = senderAddress; + return this; + } + + /** + * Lookup the specific transaction by ID. + * + * #### Example + * ```typescript + * const txId = "MEUOC4RQJB23CQZRFRKYEI6WBO73VTTPST5A7B3S5OKBUY6LFUDA"; + * const appLogs = await indexerClient + * .lookupApplicationLogs(appId) + * .txid(txId) + * .do(); + * ``` + * + * @param txid + * @category query + */ + txid(txid: string) { + this.query.txid = txid; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): ApplicationLogsResponse { + return decodeJSON(response.getJSONText(), ApplicationLogsResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupApplications.ts b/packages/sdk/src/client/v2/indexer/lookupApplications.ts new file mode 100644 index 00000000..70cd7415 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupApplications.ts @@ -0,0 +1,67 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { ApplicationResponse } from './models/types.js'; + +export default class LookupApplications extends JSONRequest { + private index: bigint; + + /** + * Returns information about the passed application. + * + * #### Example + * ```typescript + * const appId = 60553466; + * const appInfo = await indexerClient.lookupApplications(appId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-id) + * @param index - The ID of the application to look up. + * @category GET + */ + constructor(c: HTTPClient, index: number | bigint) { + super(c); + this.index = BigInt(index); + } + + /** + * @returns `/v2/applications/${index}` + */ + path() { + return `/v2/applications/${this.index}`; + } + + /** + * Includes all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example 1 + * ```typescript + * const appId = 60553466; + * const appInfo = await indexerClient + * .lookupApplications(appId) + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const appId = 60553466; + * const appInfo = await indexerClient + * .lookupApplications(appId) + * .includeAll() + * .do(); + * ``` + * + * @param value - default true when called without passing a value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): ApplicationResponse { + return decodeJSON(response.getJSONText(), ApplicationResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupAssetBalances.ts b/packages/sdk/src/client/v2/indexer/lookupAssetBalances.ts new file mode 100644 index 00000000..85c59e10 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupAssetBalances.ts @@ -0,0 +1,155 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { AssetBalancesResponse } from './models/types.js'; + +export default class LookupAssetBalances extends JSONRequest { + private index: bigint; + + /** + * Returns the list of accounts which hold the given asset and their balance. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetBalances = await indexerClient.lookupAssetBalances(assetId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assetsasset-idbalances) + * @param index - The asset ID to look up. + */ + constructor(c: HTTPClient, index: number | bigint) { + super(c); + this.index = BigInt(index); + } + + /** + * @returns `/v2/assets/${index}/balances` + */ + path() { + return `/v2/assets/${this.index}/balances`; + } + + /** + * Limit results for pagination. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const maxResults = 20; + * const assetBalances = await indexerClient + * .lookupAssetBalances(assetId) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Filtered results should have an asset balance greater than this value. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const minBalance = 1000000; + * const assetBalances = await indexerClient + * .lookupAssetBalances(assetId) + * .currencyGreaterThan(minBalance) + * .do(); + * ``` + * @param greater + * @category query + */ + currencyGreaterThan(greater: number | bigint) { + // We convert the following to a string for now to correctly include zero values in request parameters. + this.query['currency-greater-than'] = greater.toString(); + return this; + } + + /** + * Filtered results should have an asset balance less than this value. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const maxBalance = 2000000; + * const assetBalances = await indexerClient + * .lookupAssetBalances(assetId) + * .currencyLessThan(maxBalance) + * .do(); + * ``` + * @param lesser + * @category query + */ + currencyLessThan(lesser: number | bigint) { + this.query['currency-less-than'] = lesser; + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const maxResults = 20; + * + * const assetBalancesPage1 = await indexerClient + * .lookupAssetBalances(assetId) + * .limit(maxResults) + * .do(); + * + * const assetBalancesPage2 = await indexerClient + * .lookupAssetBalances(assetId) + * .limit(maxResults) + * .nextToken(assetBalancesPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Include all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates. + * + * #### Example 1 + * ```typescript + * const assetId = 163650; + * const assetBalances = await indexerClient + * .lookupAssetBalances(assetId) + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetId = 163650; + * const assetBalances = await indexerClient + * .lookupAssetBalances(assetId) + * .includeAll() + * .do(); + * ``` + * + * @param value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AssetBalancesResponse { + return decodeJSON(response.getJSONText(), AssetBalancesResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupAssetByID.ts b/packages/sdk/src/client/v2/indexer/lookupAssetByID.ts new file mode 100644 index 00000000..e83e6d4b --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupAssetByID.ts @@ -0,0 +1,66 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { AssetResponse } from './models/types.js'; + +export default class LookupAssetByID extends JSONRequest { + private index: bigint; + + /** + * Returns asset information of the queried asset. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetInfo = await indexerClient.lookupAssetByID(assetId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assetsasset-id) + * @param index - The asset ID to look up. + */ + constructor(c: HTTPClient, index: number | bigint) { + super(c); + this.index = BigInt(index); + } + + /** + * @returns `/v2/assets/${index}` + */ + path() { + return `/v2/assets/${this.index}`; + } + + /** + * Includes all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example 1 + * ```typescript + * const assetId = 163650; + * const assetInfo = await indexerClient + * .lookupAssetByID(assetId) + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetId = 163650; + * const assetInfo = await indexerClient + * .lookupAssetByID(assetId) + * .includeAll() + * .do(); + * ``` + * + * @param value - default true when called without passing a value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AssetResponse { + return decodeJSON(response.getJSONText(), AssetResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupAssetTransactions.ts b/packages/sdk/src/client/v2/indexer/lookupAssetTransactions.ts new file mode 100644 index 00000000..d4a5c77e --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupAssetTransactions.ts @@ -0,0 +1,407 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { base64StringFunnel } from './lookupAccountTransactions.js'; +import { Address } from '../../../encoding/address.js'; +import { TransactionsResponse } from './models/types.js'; + +export default class LookupAssetTransactions extends JSONRequest { + private index: bigint; + + /** + * Returns transactions relating to the given asset. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetTxns = await indexerClient.lookupAssetTransactions(assetId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assetsasset-idtransactions) + * @param index - The asset ID to look up. + */ + constructor(c: HTTPClient, index: number | bigint) { + super(c); + this.index = BigInt(index); + } + + /** + * @returns `/v2/assets/${index}/transactions` + */ + path() { + return `/v2/assets/${this.index}/transactions`; + } + + /** + * Specifies a prefix which must be contained in the note field. + * + * #### Example + * ```typescript + * const notePrefixBase64Encoded = "Y3JlYXRl"; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .notePrefix(notePrefixBase64Encoded) + * .do(); + * ``` + * + * @param prefix - base64 string or uint8array + * @category query + */ + notePrefix(prefix: Uint8Array | string) { + this.query['note-prefix'] = base64StringFunnel(prefix); + return this; + } + + /** + * Type of transaction to filter with. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .txType("axfer") + * .do(); + * ``` + * + * @param type - one of `pay`, `keyreg`, `acfg`, `axfer`, `afrz`, `appl` + * @category query + */ + txType(type: string) { + this.query['tx-type'] = type; + return this; + } + + /** + * Type of signature to filter with. + * - sig: Standard + * - msig: MultiSig + * - lsig: LogicSig + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .sigType("lsig") + * .do(); + * ``` + * + * @param type - one of `sig`, `msig`, `lsig` + * @category query + */ + sigType(type: string) { + this.query['sig-type'] = type; + return this; + } + + /** + * Lookup the specific transaction by ID. + * + * #### Example + * ```typescript + * const txId = "MEUOC4RQJB23CQZRFRKYEI6WBO73VTTPST5A7B3S5OKBUY6LFUDA"; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .txid(txId) + * .do(); + * ``` + * + * @param txid + * @category query + */ + txid(txid: string) { + this.query.txid = txid; + return this; + } + + /** + * Include results for the specified round. + * + * #### Example + * ```typescript + * const targetBlock = 18309917; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .round(targetBlock) + * .do(); + * ``` + * + * @param round + * @category query + */ + round(round: number | bigint) { + this.query.round = round; + return this; + } + + /** + * Include results at or after the specified min-round. + * + * #### Example + * ```typescript + * const minRound = 18309917; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .minRound(minRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + minRound(round: number | bigint) { + this.query['min-round'] = round; + return this; + } + + /** + * Include results at or before the specified max-round. + * + * #### Example + * ```typescript + * const maxRound = 18309917; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .maxRound(maxRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + maxRound(round: number | bigint) { + this.query['max-round'] = round; + return this; + } + + /** + * Maximum number of results to return. + * + * #### Example + * ```typescript + * const maxResults = 25; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Include results before the given time. + * + * #### Example + * ```typescript + * const beforeTime = "2022-02-02T20:20:22.02Z"; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .beforeTime(beforeTime) + * .do(); + * ``` + * + * @param before - rfc3339 string or Date object + * @category query + */ + beforeTime(before: string | Date) { + this.query['before-time'] = + before instanceof Date ? before.toISOString() : before; + return this; + } + + /** + * Include results after the given time. + * + * #### Example + * ```typescript + * const afterTime = "2022-10-21T00:00:11.55Z"; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .afterTime(afterTime) + * .do(); + * ``` + * + * @param after - rfc3339 string or Date object + * @category query + */ + afterTime(after: string | Date) { + this.query['after-time'] = + after instanceof Date ? after.toISOString() : after; + return this; + } + + /** + * Filtered results should have an amount greater than this value, as int, representing asset units. + * + * #### Example + * ```typescript + * const minBalance = 300000; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * + * @param greater + * @category query + */ + currencyGreaterThan(greater: number | bigint) { + // We convert the following to a string for now to correctly include zero values in request parameters. + this.query['currency-greater-than'] = greater.toString(); + return this; + } + + /** + * Filtered results should have an amount less than this value, as int, representing asset units. + * + * #### Example + * ```typescript + * const maxBalance = 500000; + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * @param lesser + * @category query + */ + currencyLessThan(lesser: number | bigint) { + this.query['currency-less-than'] = lesser; + return this; + } + + /** + * Combined with address, defines what address to filter on, as string. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const role = "sender"; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .address(address) + * .addressRole(role) + * .do(); + * ``` + * + * @param role - one of `sender`, `receiver`, `freeze-target` + * @category query + */ + addressRole(role: string) { + this.query['address-role'] = role; + return this; + } + + /** + * Only include transactions with this address in one of the transaction fields. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .address(address) + * .do(); + * ``` + * + * @param address + * @category query + */ + address(address: string | Address) { + this.query.address = address.toString(); + return this; + } + + /** + * Whether or not to consider the `close-to` field as a receiver when filtering transactions, as bool. Set to `true` to ignore `close-to`. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .excludeCloseTo(true) + * .do(); + * ``` + * + * @param exclude + * @category query + */ + excludeCloseTo(exclude: boolean) { + this.query['exclude-close-to'] = exclude; + return this; + } + + /** + * The next page of results. + * + * #### Example + * ```typescript + * const maxResults = 25; + * const assetId = 163650; + * + * const assetTxnsPage1 = await indexerClient + * .lookupAssetTransactions(assetId) + * .limit(maxResults) + * .do(); + * + * const assetTxnsPage2 = await indexerClient + * .lookupAssetTransactions(assetId) + * .limit(maxResults) + * .nextToken(assetTxnsPage1["next-token"]) + * .do(); + * ``` + * + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Whether or not to include rekeying transactions. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assetTxns = await indexerClient + * .lookupAssetTransactions(assetId) + * .rekeyTo(false) + * .do(); + * ``` + * + * @param rekeyTo + * @category query + */ + rekeyTo(rekeyTo: boolean) { + this.query['rekey-to'] = rekeyTo; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): TransactionsResponse { + return decodeJSON(response.getJSONText(), TransactionsResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupBlock.ts b/packages/sdk/src/client/v2/indexer/lookupBlock.ts new file mode 100644 index 00000000..cf841e3c --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupBlock.ts @@ -0,0 +1,47 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Block } from './models/types.js'; + +export default class LookupBlock extends JSONRequest { + private round: bigint; + + /** + * Returns the block for the passed round. + * + * #### Example + * ```typescript + * const targetBlock = 18309917; + * const blockInfo = await indexerClient.lookupBlock(targetBlock).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2blocksround-number) + * @param round - The number of the round to look up. + * @category GET + */ + constructor(c: HTTPClient, round: number | bigint) { + super(c); + this.round = BigInt(round); + } + + /** + * @returns `/v2/blocks/${round}` + */ + path() { + return `/v2/blocks/${this.round}`; + } + + /** + * Header only flag. When this is set to true, returned block does not contain the + * transactions. + */ + headerOnly(headerOnly: boolean) { + this.query['header-only'] = headerOnly; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): Block { + return decodeJSON(response.getJSONText(), Block); + } +} diff --git a/packages/sdk/src/client/v2/indexer/lookupTransactionByID.ts b/packages/sdk/src/client/v2/indexer/lookupTransactionByID.ts new file mode 100644 index 00000000..a01fa6f8 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/lookupTransactionByID.ts @@ -0,0 +1,38 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { TransactionResponse } from './models/types.js'; + +export default class LookupTransactionByID extends JSONRequest { + /** + * Returns information about the given transaction. + * + * #### Example + * ```typescript + * const txnId = "MEUOC4RQJB23CQZRFRKYEI6WBO73VTTPST5A7B3S5OKBUY6LFUDA"; + * const txnInfo = await indexerClient.lookupTransactionByID(txnId).do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2transactionstxid) + * @param txID - The ID of the transaction to look up. + * @category GET + */ + constructor( + c: HTTPClient, + private txID: string + ) { + super(c); + } + + /** + * @returns `/v2/transactions/${txID}` + */ + path() { + return `/v2/transactions/${this.txID}`; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): TransactionResponse { + return decodeJSON(response.getJSONText(), TransactionResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/makeHealthCheck.ts b/packages/sdk/src/client/v2/indexer/makeHealthCheck.ts new file mode 100644 index 00000000..415ca738 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/makeHealthCheck.ts @@ -0,0 +1,31 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { HealthCheck } from './models/types.js'; + +/** + * Returns the health object for the service. + * Returns 200 if healthy. + * + * #### Example + * ```typescript + * const health = await indexerClient.makeHealthCheck().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-health) + * @category GET + */ +export default class MakeHealthCheck extends JSONRequest { + /** + * @returns `/health` + */ + // eslint-disable-next-line class-methods-use-this + path() { + return '/health'; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): HealthCheck { + return decodeJSON(response.getJSONText(), HealthCheck); + } +} diff --git a/packages/sdk/src/client/v2/indexer/models/types.ts b/packages/sdk/src/client/v2/indexer/models/types.ts new file mode 100644 index 00000000..f588ab41 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/models/types.ts @@ -0,0 +1,9177 @@ +/** + * NOTICE: This file was generated. Editing this file manually is not recommended. + */ + +/* eslint-disable no-use-before-define */ +import { ensureBigInt, ensureSafeInteger } from '../../../../utils/utils.js'; +import { Encodable, Schema } from '../../../../encoding/encoding.js'; +import { + NamedMapSchema, + ArraySchema, + Uint64Schema, + StringSchema, + BooleanSchema, + ByteArraySchema, + OptionalSchema, +} from '../../../../encoding/schema/index.js'; +import { base64ToBytes } from '../../../../encoding/binarydata.js'; +import { Address } from '../../../../encoding/address.js'; +import { UntypedValue } from '../../untypedmodel.js'; + +/** + * Account information at a given round. + * Definition: + * data/basics/userBalance.go : AccountData + */ +export class Account implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'address', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'amount', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'amount-without-pending-rewards', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'min-balance', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'pending-rewards', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { key: 'rewards', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'status', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'total-apps-opted-in', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'total-assets-opted-in', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'total-box-bytes', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'total-boxes', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'total-created-apps', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'total-created-assets', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'apps-local-state', + valueSchema: new OptionalSchema( + new ArraySchema(ApplicationLocalState.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'apps-total-extra-pages', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'apps-total-schema', + valueSchema: new OptionalSchema( + ApplicationStateSchema.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'assets', + valueSchema: new OptionalSchema( + new ArraySchema(AssetHolding.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'auth-addr', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'closed-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'created-apps', + valueSchema: new OptionalSchema( + new ArraySchema(Application.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'created-assets', + valueSchema: new OptionalSchema( + new ArraySchema(Asset.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'created-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'deleted', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'incentive-eligible', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'last-heartbeat', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'last-proposed', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'participation', + valueSchema: new OptionalSchema(AccountParticipation.encodingSchema), + omitEmpty: true, + }, + { + key: 'reward-base', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'sig-type', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * the account public key + */ + public address: string; + + /** + * total number of MicroAlgos in the account + */ + public amount: bigint; + + /** + * specifies the amount of MicroAlgos in the account, without the pending rewards. + */ + public amountWithoutPendingRewards: bigint; + + /** + * MicroAlgo balance required by the account. + * The requirement grows based on asset and application usage. + */ + public minBalance: number; + + /** + * amount of MicroAlgos of pending rewards in this account. + */ + public pendingRewards: bigint; + + /** + * total rewards of MicroAlgos the account has received, including pending rewards. + */ + public rewards: bigint; + + /** + * The round for which this information is relevant. + */ + public round: bigint; + + /** + * voting status of the account's MicroAlgos + * * Offline - indicates that the associated account is delegated. + * * Online - indicates that the associated account used as part of the delegation + * pool. + * * NotParticipating - indicates that the associated account is neither a + * delegator nor a delegate. + */ + public status: string; + + /** + * The count of all applications that have been opted in, equivalent to the count + * of application local data (AppLocalState objects) stored in this account. + */ + public totalAppsOptedIn: number; + + /** + * The count of all assets that have been opted in, equivalent to the count of + * AssetHolding objects held by this account. + */ + public totalAssetsOptedIn: number; + + /** + * For app-accounts only. The total number of bytes allocated for the keys and + * values of boxes which belong to the associated application. + */ + public totalBoxBytes: number; + + /** + * For app-accounts only. The total number of boxes which belong to the associated + * application. + */ + public totalBoxes: number; + + /** + * The count of all apps (AppParams objects) created by this account. + */ + public totalCreatedApps: number; + + /** + * The count of all assets (AssetParams objects) created by this account. + */ + public totalCreatedAssets: number; + + /** + * application local data stored in this account. + * Note the raw object uses `map[int] -> AppLocalState` for this type. + */ + public appsLocalState?: ApplicationLocalState[]; + + /** + * the sum of all extra application program pages for this account. + */ + public appsTotalExtraPages?: number; + + /** + * the sum of all of the local schemas and global schemas in this account. + * Note: the raw account uses `StateSchema` for this type. + */ + public appsTotalSchema?: ApplicationStateSchema; + + /** + * assets held by this account. + * Note the raw object uses `map[int] -> AssetHolding` for this type. + */ + public assets?: AssetHolding[]; + + /** + * The address against which signing should be checked. If empty, the address of + * the current account is used. This field can be updated in any transaction by + * setting the RekeyTo field. + */ + public authAddr?: Address; + + /** + * Round during which this account was most recently closed. + */ + public closedAtRound?: bigint; + + /** + * parameters of applications created by this account including app global data. + * Note: the raw account uses `map[int] -> AppParams` for this type. + */ + public createdApps?: Application[]; + + /** + * parameters of assets created by this account. + * Note: the raw account uses `map[int] -> Asset` for this type. + */ + public createdAssets?: Asset[]; + + /** + * Round during which this account first appeared in a transaction. + */ + public createdAtRound?: bigint; + + /** + * Whether or not this account is currently closed. + */ + public deleted?: boolean; + + /** + * can the account receive block incentives if its balance is in range at proposal + * time. + */ + public incentiveEligible?: boolean; + + /** + * The round in which this account last went online, or explicitly renewed their + * online status. + */ + public lastHeartbeat?: number; + + /** + * The round in which this account last proposed the block. + */ + public lastProposed?: number; + + /** + * AccountParticipation describes the parameters used by this account in consensus + * protocol. + */ + public participation?: AccountParticipation; + + /** + * used as part of the rewards computation. Only applicable to accounts which are + * participating. + */ + public rewardBase?: bigint; + + /** + * the type of signature used by this account, must be one of: + * * sig + * * msig + * * lsig + * * or null if unknown + */ + public sigType?: string; + + /** + * Creates a new `Account` object. + * @param address - the account public key + * @param amount - total number of MicroAlgos in the account + * @param amountWithoutPendingRewards - specifies the amount of MicroAlgos in the account, without the pending rewards. + * @param minBalance - MicroAlgo balance required by the account. + * The requirement grows based on asset and application usage. + * @param pendingRewards - amount of MicroAlgos of pending rewards in this account. + * @param rewards - total rewards of MicroAlgos the account has received, including pending rewards. + * @param round - The round for which this information is relevant. + * @param status - voting status of the account's MicroAlgos + * * Offline - indicates that the associated account is delegated. + * * Online - indicates that the associated account used as part of the delegation + * pool. + * * NotParticipating - indicates that the associated account is neither a + * delegator nor a delegate. + * @param totalAppsOptedIn - The count of all applications that have been opted in, equivalent to the count + * of application local data (AppLocalState objects) stored in this account. + * @param totalAssetsOptedIn - The count of all assets that have been opted in, equivalent to the count of + * AssetHolding objects held by this account. + * @param totalBoxBytes - For app-accounts only. The total number of bytes allocated for the keys and + * values of boxes which belong to the associated application. + * @param totalBoxes - For app-accounts only. The total number of boxes which belong to the associated + * application. + * @param totalCreatedApps - The count of all apps (AppParams objects) created by this account. + * @param totalCreatedAssets - The count of all assets (AssetParams objects) created by this account. + * @param appsLocalState - application local data stored in this account. + * Note the raw object uses `map[int] -> AppLocalState` for this type. + * @param appsTotalExtraPages - the sum of all extra application program pages for this account. + * @param appsTotalSchema - the sum of all of the local schemas and global schemas in this account. + * Note: the raw account uses `StateSchema` for this type. + * @param assets - assets held by this account. + * Note the raw object uses `map[int] -> AssetHolding` for this type. + * @param authAddr - The address against which signing should be checked. If empty, the address of + * the current account is used. This field can be updated in any transaction by + * setting the RekeyTo field. + * @param closedAtRound - Round during which this account was most recently closed. + * @param createdApps - parameters of applications created by this account including app global data. + * Note: the raw account uses `map[int] -> AppParams` for this type. + * @param createdAssets - parameters of assets created by this account. + * Note: the raw account uses `map[int] -> Asset` for this type. + * @param createdAtRound - Round during which this account first appeared in a transaction. + * @param deleted - Whether or not this account is currently closed. + * @param incentiveEligible - can the account receive block incentives if its balance is in range at proposal + * time. + * @param lastHeartbeat - The round in which this account last went online, or explicitly renewed their + * online status. + * @param lastProposed - The round in which this account last proposed the block. + * @param participation - AccountParticipation describes the parameters used by this account in consensus + * protocol. + * @param rewardBase - used as part of the rewards computation. Only applicable to accounts which are + * participating. + * @param sigType - the type of signature used by this account, must be one of: + * * sig + * * msig + * * lsig + * * or null if unknown + */ + constructor({ + address, + amount, + amountWithoutPendingRewards, + minBalance, + pendingRewards, + rewards, + round, + status, + totalAppsOptedIn, + totalAssetsOptedIn, + totalBoxBytes, + totalBoxes, + totalCreatedApps, + totalCreatedAssets, + appsLocalState, + appsTotalExtraPages, + appsTotalSchema, + assets, + authAddr, + closedAtRound, + createdApps, + createdAssets, + createdAtRound, + deleted, + incentiveEligible, + lastHeartbeat, + lastProposed, + participation, + rewardBase, + sigType, + }: { + address: string; + amount: number | bigint; + amountWithoutPendingRewards: number | bigint; + minBalance: number | bigint; + pendingRewards: number | bigint; + rewards: number | bigint; + round: number | bigint; + status: string; + totalAppsOptedIn: number | bigint; + totalAssetsOptedIn: number | bigint; + totalBoxBytes: number | bigint; + totalBoxes: number | bigint; + totalCreatedApps: number | bigint; + totalCreatedAssets: number | bigint; + appsLocalState?: ApplicationLocalState[]; + appsTotalExtraPages?: number | bigint; + appsTotalSchema?: ApplicationStateSchema; + assets?: AssetHolding[]; + authAddr?: Address | string; + closedAtRound?: number | bigint; + createdApps?: Application[]; + createdAssets?: Asset[]; + createdAtRound?: number | bigint; + deleted?: boolean; + incentiveEligible?: boolean; + lastHeartbeat?: number | bigint; + lastProposed?: number | bigint; + participation?: AccountParticipation; + rewardBase?: number | bigint; + sigType?: string; + }) { + this.address = address; + this.amount = ensureBigInt(amount); + this.amountWithoutPendingRewards = ensureBigInt( + amountWithoutPendingRewards + ); + this.minBalance = ensureSafeInteger(minBalance); + this.pendingRewards = ensureBigInt(pendingRewards); + this.rewards = ensureBigInt(rewards); + this.round = ensureBigInt(round); + this.status = status; + this.totalAppsOptedIn = ensureSafeInteger(totalAppsOptedIn); + this.totalAssetsOptedIn = ensureSafeInteger(totalAssetsOptedIn); + this.totalBoxBytes = ensureSafeInteger(totalBoxBytes); + this.totalBoxes = ensureSafeInteger(totalBoxes); + this.totalCreatedApps = ensureSafeInteger(totalCreatedApps); + this.totalCreatedAssets = ensureSafeInteger(totalCreatedAssets); + this.appsLocalState = appsLocalState; + this.appsTotalExtraPages = + typeof appsTotalExtraPages === 'undefined' + ? undefined + : ensureSafeInteger(appsTotalExtraPages); + this.appsTotalSchema = appsTotalSchema; + this.assets = assets; + this.authAddr = + typeof authAddr === 'string' ? Address.fromString(authAddr) : authAddr; + this.closedAtRound = + typeof closedAtRound === 'undefined' + ? undefined + : ensureBigInt(closedAtRound); + this.createdApps = createdApps; + this.createdAssets = createdAssets; + this.createdAtRound = + typeof createdAtRound === 'undefined' + ? undefined + : ensureBigInt(createdAtRound); + this.deleted = deleted; + this.incentiveEligible = incentiveEligible; + this.lastHeartbeat = + typeof lastHeartbeat === 'undefined' + ? undefined + : ensureSafeInteger(lastHeartbeat); + this.lastProposed = + typeof lastProposed === 'undefined' + ? undefined + : ensureSafeInteger(lastProposed); + this.participation = participation; + this.rewardBase = + typeof rewardBase === 'undefined' ? undefined : ensureBigInt(rewardBase); + this.sigType = sigType; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Account.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['address', this.address], + ['amount', this.amount], + ['amount-without-pending-rewards', this.amountWithoutPendingRewards], + ['min-balance', this.minBalance], + ['pending-rewards', this.pendingRewards], + ['rewards', this.rewards], + ['round', this.round], + ['status', this.status], + ['total-apps-opted-in', this.totalAppsOptedIn], + ['total-assets-opted-in', this.totalAssetsOptedIn], + ['total-box-bytes', this.totalBoxBytes], + ['total-boxes', this.totalBoxes], + ['total-created-apps', this.totalCreatedApps], + ['total-created-assets', this.totalCreatedAssets], + [ + 'apps-local-state', + typeof this.appsLocalState !== 'undefined' + ? this.appsLocalState.map((v) => v.toEncodingData()) + : undefined, + ], + ['apps-total-extra-pages', this.appsTotalExtraPages], + [ + 'apps-total-schema', + typeof this.appsTotalSchema !== 'undefined' + ? this.appsTotalSchema.toEncodingData() + : undefined, + ], + [ + 'assets', + typeof this.assets !== 'undefined' + ? this.assets.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'auth-addr', + typeof this.authAddr !== 'undefined' + ? this.authAddr.toString() + : undefined, + ], + ['closed-at-round', this.closedAtRound], + [ + 'created-apps', + typeof this.createdApps !== 'undefined' + ? this.createdApps.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'created-assets', + typeof this.createdAssets !== 'undefined' + ? this.createdAssets.map((v) => v.toEncodingData()) + : undefined, + ], + ['created-at-round', this.createdAtRound], + ['deleted', this.deleted], + ['incentive-eligible', this.incentiveEligible], + ['last-heartbeat', this.lastHeartbeat], + ['last-proposed', this.lastProposed], + [ + 'participation', + typeof this.participation !== 'undefined' + ? this.participation.toEncodingData() + : undefined, + ], + ['reward-base', this.rewardBase], + ['sig-type', this.sigType], + ]); + } + + static fromEncodingData(data: unknown): Account { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Account: ${data}`); + } + return new Account({ + address: data.get('address'), + amount: data.get('amount'), + amountWithoutPendingRewards: data.get('amount-without-pending-rewards'), + minBalance: data.get('min-balance'), + pendingRewards: data.get('pending-rewards'), + rewards: data.get('rewards'), + round: data.get('round'), + status: data.get('status'), + totalAppsOptedIn: data.get('total-apps-opted-in'), + totalAssetsOptedIn: data.get('total-assets-opted-in'), + totalBoxBytes: data.get('total-box-bytes'), + totalBoxes: data.get('total-boxes'), + totalCreatedApps: data.get('total-created-apps'), + totalCreatedAssets: data.get('total-created-assets'), + appsLocalState: + typeof data.get('apps-local-state') !== 'undefined' + ? data + .get('apps-local-state') + .map((v: unknown) => ApplicationLocalState.fromEncodingData(v)) + : undefined, + appsTotalExtraPages: data.get('apps-total-extra-pages'), + appsTotalSchema: + typeof data.get('apps-total-schema') !== 'undefined' + ? ApplicationStateSchema.fromEncodingData( + data.get('apps-total-schema') + ) + : undefined, + assets: + typeof data.get('assets') !== 'undefined' + ? data + .get('assets') + .map((v: unknown) => AssetHolding.fromEncodingData(v)) + : undefined, + authAddr: data.get('auth-addr'), + closedAtRound: data.get('closed-at-round'), + createdApps: + typeof data.get('created-apps') !== 'undefined' + ? data + .get('created-apps') + .map((v: unknown) => Application.fromEncodingData(v)) + : undefined, + createdAssets: + typeof data.get('created-assets') !== 'undefined' + ? data + .get('created-assets') + .map((v: unknown) => Asset.fromEncodingData(v)) + : undefined, + createdAtRound: data.get('created-at-round'), + deleted: data.get('deleted'), + incentiveEligible: data.get('incentive-eligible'), + lastHeartbeat: data.get('last-heartbeat'), + lastProposed: data.get('last-proposed'), + participation: + typeof data.get('participation') !== 'undefined' + ? AccountParticipation.fromEncodingData(data.get('participation')) + : undefined, + rewardBase: data.get('reward-base'), + sigType: data.get('sig-type'), + }); + } +} + +/** + * AccountParticipation describes the parameters used by this account in consensus + * protocol. + */ +export class AccountParticipation implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'selection-participation-key', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'vote-first-valid', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'vote-key-dilution', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'vote-last-valid', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'vote-participation-key', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'state-proof-key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * Selection public key (if any) currently registered for this round. + */ + public selectionParticipationKey: Uint8Array; + + /** + * First round for which this participation is valid. + */ + public voteFirstValid: bigint; + + /** + * Number of subkeys in each batch of participation keys. + */ + public voteKeyDilution: bigint; + + /** + * Last round for which this participation is valid. + */ + public voteLastValid: bigint; + + /** + * root participation public key (if any) currently registered for this round. + */ + public voteParticipationKey: Uint8Array; + + /** + * Root of the state proof key (if any) + */ + public stateProofKey?: Uint8Array; + + /** + * Creates a new `AccountParticipation` object. + * @param selectionParticipationKey - Selection public key (if any) currently registered for this round. + * @param voteFirstValid - First round for which this participation is valid. + * @param voteKeyDilution - Number of subkeys in each batch of participation keys. + * @param voteLastValid - Last round for which this participation is valid. + * @param voteParticipationKey - root participation public key (if any) currently registered for this round. + * @param stateProofKey - Root of the state proof key (if any) + */ + constructor({ + selectionParticipationKey, + voteFirstValid, + voteKeyDilution, + voteLastValid, + voteParticipationKey, + stateProofKey, + }: { + selectionParticipationKey: string | Uint8Array; + voteFirstValid: number | bigint; + voteKeyDilution: number | bigint; + voteLastValid: number | bigint; + voteParticipationKey: string | Uint8Array; + stateProofKey?: string | Uint8Array; + }) { + this.selectionParticipationKey = + typeof selectionParticipationKey === 'string' + ? base64ToBytes(selectionParticipationKey) + : selectionParticipationKey; + this.voteFirstValid = ensureBigInt(voteFirstValid); + this.voteKeyDilution = ensureBigInt(voteKeyDilution); + this.voteLastValid = ensureBigInt(voteLastValid); + this.voteParticipationKey = + typeof voteParticipationKey === 'string' + ? base64ToBytes(voteParticipationKey) + : voteParticipationKey; + this.stateProofKey = + typeof stateProofKey === 'string' + ? base64ToBytes(stateProofKey) + : stateProofKey; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountParticipation.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['selection-participation-key', this.selectionParticipationKey], + ['vote-first-valid', this.voteFirstValid], + ['vote-key-dilution', this.voteKeyDilution], + ['vote-last-valid', this.voteLastValid], + ['vote-participation-key', this.voteParticipationKey], + ['state-proof-key', this.stateProofKey], + ]); + } + + static fromEncodingData(data: unknown): AccountParticipation { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountParticipation: ${data}`); + } + return new AccountParticipation({ + selectionParticipationKey: data.get('selection-participation-key'), + voteFirstValid: data.get('vote-first-valid'), + voteKeyDilution: data.get('vote-key-dilution'), + voteLastValid: data.get('vote-last-valid'), + voteParticipationKey: data.get('vote-participation-key'), + stateProofKey: data.get('state-proof-key'), + }); + } +} + +/** + * + */ +export class AccountResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'account', + valueSchema: Account.encodingSchema, + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * Account information at a given round. + * Definition: + * data/basics/userBalance.go : AccountData + */ + public account: Account; + + /** + * Round at which the results were computed. + */ + public currentRound: bigint; + + /** + * Creates a new `AccountResponse` object. + * @param account - Account information at a given round. + * Definition: + * data/basics/userBalance.go : AccountData + * @param currentRound - Round at which the results were computed. + */ + constructor({ + account, + currentRound, + }: { + account: Account; + currentRound: number | bigint; + }) { + this.account = account; + this.currentRound = ensureBigInt(currentRound); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['account', this.account.toEncodingData()], + ['current-round', this.currentRound], + ]); + } + + static fromEncodingData(data: unknown): AccountResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountResponse: ${data}`); + } + return new AccountResponse({ + account: Account.fromEncodingData(data.get('account') ?? new Map()), + currentRound: data.get('current-round'), + }); + } +} + +/** + * Application state delta. + */ +export class AccountStateDelta implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'address', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'delta', + valueSchema: new ArraySchema(EvalDeltaKeyValue.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + public address: string; + + /** + * Application state delta. + */ + public delta: EvalDeltaKeyValue[]; + + /** + * Creates a new `AccountStateDelta` object. + * @param address - + * @param delta - Application state delta. + */ + constructor({ + address, + delta, + }: { + address: string; + delta: EvalDeltaKeyValue[]; + }) { + this.address = address; + this.delta = delta; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountStateDelta.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['address', this.address], + ['delta', this.delta.map((v) => v.toEncodingData())], + ]); + } + + static fromEncodingData(data: unknown): AccountStateDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountStateDelta: ${data}`); + } + return new AccountStateDelta({ + address: data.get('address'), + delta: (data.get('delta') ?? []).map((v: unknown) => + EvalDeltaKeyValue.fromEncodingData(v) + ), + }); + } +} + +/** + * + */ +export class AccountsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'accounts', + valueSchema: new ArraySchema(Account.encodingSchema), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + public accounts: Account[]; + + /** + * Round at which the results were computed. + */ + public currentRound: bigint; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `AccountsResponse` object. + * @param accounts - + * @param currentRound - Round at which the results were computed. + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + accounts, + currentRound, + nextToken, + }: { + accounts: Account[]; + currentRound: number | bigint; + nextToken?: string; + }) { + this.accounts = accounts; + this.currentRound = ensureBigInt(currentRound); + this.nextToken = nextToken; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AccountsResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['accounts', this.accounts.map((v) => v.toEncodingData())], + ['current-round', this.currentRound], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): AccountsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AccountsResponse: ${data}`); + } + return new AccountsResponse({ + accounts: (data.get('accounts') ?? []).map((v: unknown) => + Account.fromEncodingData(v) + ), + currentRound: data.get('current-round'), + nextToken: data.get('next-token'), + }); + } +} + +/** + * Application index and its parameters + */ +export class Application implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'params', + valueSchema: ApplicationParams.encodingSchema, + omitEmpty: true, + }, + { + key: 'created-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'deleted', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'deleted-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * application index. + */ + public id: bigint; + + /** + * application parameters. + */ + public params: ApplicationParams; + + /** + * Round when this application was created. + */ + public createdAtRound?: bigint; + + /** + * Whether or not this application is currently deleted. + */ + public deleted?: boolean; + + /** + * Round when this application was deleted. + */ + public deletedAtRound?: bigint; + + /** + * Creates a new `Application` object. + * @param id - application index. + * @param params - application parameters. + * @param createdAtRound - Round when this application was created. + * @param deleted - Whether or not this application is currently deleted. + * @param deletedAtRound - Round when this application was deleted. + */ + constructor({ + id, + params, + createdAtRound, + deleted, + deletedAtRound, + }: { + id: number | bigint; + params: ApplicationParams; + createdAtRound?: number | bigint; + deleted?: boolean; + deletedAtRound?: number | bigint; + }) { + this.id = ensureBigInt(id); + this.params = params; + this.createdAtRound = + typeof createdAtRound === 'undefined' + ? undefined + : ensureBigInt(createdAtRound); + this.deleted = deleted; + this.deletedAtRound = + typeof deletedAtRound === 'undefined' + ? undefined + : ensureBigInt(deletedAtRound); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Application.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['id', this.id], + ['params', this.params.toEncodingData()], + ['created-at-round', this.createdAtRound], + ['deleted', this.deleted], + ['deleted-at-round', this.deletedAtRound], + ]); + } + + static fromEncodingData(data: unknown): Application { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Application: ${data}`); + } + return new Application({ + id: data.get('id'), + params: ApplicationParams.fromEncodingData( + data.get('params') ?? new Map() + ), + createdAtRound: data.get('created-at-round'), + deleted: data.get('deleted'), + deletedAtRound: data.get('deleted-at-round'), + }); + } +} + +/** + * Stores local state associated with an application. + */ +export class ApplicationLocalState implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'schema', + valueSchema: ApplicationStateSchema.encodingSchema, + omitEmpty: true, + }, + { + key: 'closed-out-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'deleted', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'key-value', + valueSchema: new OptionalSchema( + new ArraySchema(TealKeyValue.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'opted-in-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * The application which this local state is for. + */ + public id: bigint; + + /** + * schema. + */ + public schema: ApplicationStateSchema; + + /** + * Round when account closed out of the application. + */ + public closedOutAtRound?: bigint; + + /** + * Whether or not the application local state is currently deleted from its + * account. + */ + public deleted?: boolean; + + /** + * storage. + */ + public keyValue?: TealKeyValue[]; + + /** + * Round when the account opted into the application. + */ + public optedInAtRound?: bigint; + + /** + * Creates a new `ApplicationLocalState` object. + * @param id - The application which this local state is for. + * @param schema - schema. + * @param closedOutAtRound - Round when account closed out of the application. + * @param deleted - Whether or not the application local state is currently deleted from its + * account. + * @param keyValue - storage. + * @param optedInAtRound - Round when the account opted into the application. + */ + constructor({ + id, + schema, + closedOutAtRound, + deleted, + keyValue, + optedInAtRound, + }: { + id: number | bigint; + schema: ApplicationStateSchema; + closedOutAtRound?: number | bigint; + deleted?: boolean; + keyValue?: TealKeyValue[]; + optedInAtRound?: number | bigint; + }) { + this.id = ensureBigInt(id); + this.schema = schema; + this.closedOutAtRound = + typeof closedOutAtRound === 'undefined' + ? undefined + : ensureBigInt(closedOutAtRound); + this.deleted = deleted; + this.keyValue = keyValue; + this.optedInAtRound = + typeof optedInAtRound === 'undefined' + ? undefined + : ensureBigInt(optedInAtRound); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationLocalState.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['id', this.id], + ['schema', this.schema.toEncodingData()], + ['closed-out-at-round', this.closedOutAtRound], + ['deleted', this.deleted], + [ + 'key-value', + typeof this.keyValue !== 'undefined' + ? this.keyValue.map((v) => v.toEncodingData()) + : undefined, + ], + ['opted-in-at-round', this.optedInAtRound], + ]); + } + + static fromEncodingData(data: unknown): ApplicationLocalState { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationLocalState: ${data}`); + } + return new ApplicationLocalState({ + id: data.get('id'), + schema: ApplicationStateSchema.fromEncodingData( + data.get('schema') ?? new Map() + ), + closedOutAtRound: data.get('closed-out-at-round'), + deleted: data.get('deleted'), + keyValue: + typeof data.get('key-value') !== 'undefined' + ? data + .get('key-value') + .map((v: unknown) => TealKeyValue.fromEncodingData(v)) + : undefined, + optedInAtRound: data.get('opted-in-at-round'), + }); + } +} + +/** + * + */ +export class ApplicationLocalStatesResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'apps-local-states', + valueSchema: new ArraySchema(ApplicationLocalState.encodingSchema), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + public appsLocalStates: ApplicationLocalState[]; + + /** + * Round at which the results were computed. + */ + public currentRound: bigint; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `ApplicationLocalStatesResponse` object. + * @param appsLocalStates - + * @param currentRound - Round at which the results were computed. + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + appsLocalStates, + currentRound, + nextToken, + }: { + appsLocalStates: ApplicationLocalState[]; + currentRound: number | bigint; + nextToken?: string; + }) { + this.appsLocalStates = appsLocalStates; + this.currentRound = ensureBigInt(currentRound); + this.nextToken = nextToken; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationLocalStatesResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + [ + 'apps-local-states', + this.appsLocalStates.map((v) => v.toEncodingData()), + ], + ['current-round', this.currentRound], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): ApplicationLocalStatesResponse { + if (!(data instanceof Map)) { + throw new Error( + `Invalid decoded ApplicationLocalStatesResponse: ${data}` + ); + } + return new ApplicationLocalStatesResponse({ + appsLocalStates: (data.get('apps-local-states') ?? []).map((v: unknown) => + ApplicationLocalState.fromEncodingData(v) + ), + currentRound: data.get('current-round'), + nextToken: data.get('next-token'), + }); + } +} + +/** + * Stores the global information associated with an application. + */ +export class ApplicationLogData implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'logs', + valueSchema: new ArraySchema(new ByteArraySchema()), + omitEmpty: true, + }, + { key: 'txid', valueSchema: new StringSchema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + + /** + * Logs for the application being executed by the transaction. + */ + public logs: Uint8Array[]; + + /** + * Transaction ID + */ + public txid: string; + + /** + * Creates a new `ApplicationLogData` object. + * @param logs - Logs for the application being executed by the transaction. + * @param txid - Transaction ID + */ + constructor({ logs, txid }: { logs: Uint8Array[]; txid: string }) { + this.logs = logs; + this.txid = txid; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationLogData.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['logs', this.logs], + ['txid', this.txid], + ]); + } + + static fromEncodingData(data: unknown): ApplicationLogData { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationLogData: ${data}`); + } + return new ApplicationLogData({ + logs: data.get('logs'), + txid: data.get('txid'), + }); + } +} + +/** + * + */ +export class ApplicationLogsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'application-id', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'log-data', + valueSchema: new OptionalSchema( + new ArraySchema(ApplicationLogData.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (appidx) application index. + */ + public applicationId: bigint; + + /** + * Round at which the results were computed. + */ + public currentRound: bigint; + + public logData?: ApplicationLogData[]; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `ApplicationLogsResponse` object. + * @param applicationId - (appidx) application index. + * @param currentRound - Round at which the results were computed. + * @param logData - + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + applicationId, + currentRound, + logData, + nextToken, + }: { + applicationId: number | bigint; + currentRound: number | bigint; + logData?: ApplicationLogData[]; + nextToken?: string; + }) { + this.applicationId = ensureBigInt(applicationId); + this.currentRound = ensureBigInt(currentRound); + this.logData = logData; + this.nextToken = nextToken; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationLogsResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['application-id', this.applicationId], + ['current-round', this.currentRound], + [ + 'log-data', + typeof this.logData !== 'undefined' + ? this.logData.map((v) => v.toEncodingData()) + : undefined, + ], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): ApplicationLogsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationLogsResponse: ${data}`); + } + return new ApplicationLogsResponse({ + applicationId: data.get('application-id'), + currentRound: data.get('current-round'), + logData: + typeof data.get('log-data') !== 'undefined' + ? data + .get('log-data') + .map((v: unknown) => ApplicationLogData.fromEncodingData(v)) + : undefined, + nextToken: data.get('next-token'), + }); + } +} + +/** + * Stores the global information associated with an application. + */ +export class ApplicationParams implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'approval-program', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'clear-state-program', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'creator', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'extra-program-pages', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'global-state', + valueSchema: new OptionalSchema( + new ArraySchema(TealKeyValue.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'global-state-schema', + valueSchema: new OptionalSchema( + ApplicationStateSchema.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'local-state-schema', + valueSchema: new OptionalSchema( + ApplicationStateSchema.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'version', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * approval program. + */ + public approvalProgram: Uint8Array; + + /** + * clear state program. + */ + public clearStateProgram: Uint8Array; + + /** + * The address that created this application. This is the address where the + * parameters and global state for this application can be found. + */ + public creator?: Address; + + /** + * the number of extra program pages available to this app. + */ + public extraProgramPages?: number; + + /** + * global state + */ + public globalState?: TealKeyValue[]; + + /** + * global schema + */ + public globalStateSchema?: ApplicationStateSchema; + + /** + * local schema + */ + public localStateSchema?: ApplicationStateSchema; + + /** + * the number of updates to the application programs + */ + public version?: number; + + /** + * Creates a new `ApplicationParams` object. + * @param approvalProgram - approval program. + * @param clearStateProgram - clear state program. + * @param creator - The address that created this application. This is the address where the + * parameters and global state for this application can be found. + * @param extraProgramPages - the number of extra program pages available to this app. + * @param globalState - global state + * @param globalStateSchema - global schema + * @param localStateSchema - local schema + * @param version - the number of updates to the application programs + */ + constructor({ + approvalProgram, + clearStateProgram, + creator, + extraProgramPages, + globalState, + globalStateSchema, + localStateSchema, + version, + }: { + approvalProgram: string | Uint8Array; + clearStateProgram: string | Uint8Array; + creator?: Address | string; + extraProgramPages?: number | bigint; + globalState?: TealKeyValue[]; + globalStateSchema?: ApplicationStateSchema; + localStateSchema?: ApplicationStateSchema; + version?: number | bigint; + }) { + this.approvalProgram = + typeof approvalProgram === 'string' + ? base64ToBytes(approvalProgram) + : approvalProgram; + this.clearStateProgram = + typeof clearStateProgram === 'string' + ? base64ToBytes(clearStateProgram) + : clearStateProgram; + this.creator = + typeof creator === 'string' ? Address.fromString(creator) : creator; + this.extraProgramPages = + typeof extraProgramPages === 'undefined' + ? undefined + : ensureSafeInteger(extraProgramPages); + this.globalState = globalState; + this.globalStateSchema = globalStateSchema; + this.localStateSchema = localStateSchema; + this.version = + typeof version === 'undefined' ? undefined : ensureSafeInteger(version); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationParams.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['approval-program', this.approvalProgram], + ['clear-state-program', this.clearStateProgram], + [ + 'creator', + typeof this.creator !== 'undefined' + ? this.creator.toString() + : undefined, + ], + ['extra-program-pages', this.extraProgramPages], + [ + 'global-state', + typeof this.globalState !== 'undefined' + ? this.globalState.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'global-state-schema', + typeof this.globalStateSchema !== 'undefined' + ? this.globalStateSchema.toEncodingData() + : undefined, + ], + [ + 'local-state-schema', + typeof this.localStateSchema !== 'undefined' + ? this.localStateSchema.toEncodingData() + : undefined, + ], + ['version', this.version], + ]); + } + + static fromEncodingData(data: unknown): ApplicationParams { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationParams: ${data}`); + } + return new ApplicationParams({ + approvalProgram: data.get('approval-program'), + clearStateProgram: data.get('clear-state-program'), + creator: data.get('creator'), + extraProgramPages: data.get('extra-program-pages'), + globalState: + typeof data.get('global-state') !== 'undefined' + ? data + .get('global-state') + .map((v: unknown) => TealKeyValue.fromEncodingData(v)) + : undefined, + globalStateSchema: + typeof data.get('global-state-schema') !== 'undefined' + ? ApplicationStateSchema.fromEncodingData( + data.get('global-state-schema') + ) + : undefined, + localStateSchema: + typeof data.get('local-state-schema') !== 'undefined' + ? ApplicationStateSchema.fromEncodingData( + data.get('local-state-schema') + ) + : undefined, + version: data.get('version'), + }); + } +} + +/** + * + */ +export class ApplicationResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'application', + valueSchema: new OptionalSchema(Application.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * Round at which the results were computed. + */ + public currentRound: bigint; + + /** + * Application index and its parameters + */ + public application?: Application; + + /** + * Creates a new `ApplicationResponse` object. + * @param currentRound - Round at which the results were computed. + * @param application - Application index and its parameters + */ + constructor({ + currentRound, + application, + }: { + currentRound: number | bigint; + application?: Application; + }) { + this.currentRound = ensureBigInt(currentRound); + this.application = application; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['current-round', this.currentRound], + [ + 'application', + typeof this.application !== 'undefined' + ? this.application.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): ApplicationResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationResponse: ${data}`); + } + return new ApplicationResponse({ + currentRound: data.get('current-round'), + application: + typeof data.get('application') !== 'undefined' + ? Application.fromEncodingData(data.get('application')) + : undefined, + }); + } +} + +/** + * Specifies maximums on the number of each type that may be stored. + */ +export class ApplicationStateSchema implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'num-byte-slice', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { key: 'num-uint', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + + /** + * number of byte slices. + */ + public numByteSlice: number; + + /** + * number of uints. + */ + public numUint: number; + + /** + * Creates a new `ApplicationStateSchema` object. + * @param numByteSlice - number of byte slices. + * @param numUint - number of uints. + */ + constructor({ + numByteSlice, + numUint, + }: { + numByteSlice: number | bigint; + numUint: number | bigint; + }) { + this.numByteSlice = ensureSafeInteger(numByteSlice); + this.numUint = ensureSafeInteger(numUint); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationStateSchema.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['num-byte-slice', this.numByteSlice], + ['num-uint', this.numUint], + ]); + } + + static fromEncodingData(data: unknown): ApplicationStateSchema { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationStateSchema: ${data}`); + } + return new ApplicationStateSchema({ + numByteSlice: data.get('num-byte-slice'), + numUint: data.get('num-uint'), + }); + } +} + +/** + * + */ +export class ApplicationsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'applications', + valueSchema: new ArraySchema(Application.encodingSchema), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + public applications: Application[]; + + /** + * Round at which the results were computed. + */ + public currentRound: bigint; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `ApplicationsResponse` object. + * @param applications - + * @param currentRound - Round at which the results were computed. + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + applications, + currentRound, + nextToken, + }: { + applications: Application[]; + currentRound: number | bigint; + nextToken?: string; + }) { + this.applications = applications; + this.currentRound = ensureBigInt(currentRound); + this.nextToken = nextToken; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ApplicationsResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['applications', this.applications.map((v) => v.toEncodingData())], + ['current-round', this.currentRound], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): ApplicationsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ApplicationsResponse: ${data}`); + } + return new ApplicationsResponse({ + applications: (data.get('applications') ?? []).map((v: unknown) => + Application.fromEncodingData(v) + ), + currentRound: data.get('current-round'), + nextToken: data.get('next-token'), + }); + } +} + +/** + * Specifies both the unique identifier and the parameters for an asset + */ +export class Asset implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'index', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'params', + valueSchema: AssetParams.encodingSchema, + omitEmpty: true, + }, + { + key: 'created-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'deleted', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'destroyed-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * unique asset identifier + */ + public index: bigint; + + /** + * AssetParams specifies the parameters for an asset. + * (apar) when part of an AssetConfig transaction. + * Definition: + * data/transactions/asset.go : AssetParams + */ + public params: AssetParams; + + /** + * Round during which this asset was created. + */ + public createdAtRound?: bigint; + + /** + * Whether or not this asset is currently deleted. + */ + public deleted?: boolean; + + /** + * Round during which this asset was destroyed. + */ + public destroyedAtRound?: bigint; + + /** + * Creates a new `Asset` object. + * @param index - unique asset identifier + * @param params - AssetParams specifies the parameters for an asset. + * (apar) when part of an AssetConfig transaction. + * Definition: + * data/transactions/asset.go : AssetParams + * @param createdAtRound - Round during which this asset was created. + * @param deleted - Whether or not this asset is currently deleted. + * @param destroyedAtRound - Round during which this asset was destroyed. + */ + constructor({ + index, + params, + createdAtRound, + deleted, + destroyedAtRound, + }: { + index: number | bigint; + params: AssetParams; + createdAtRound?: number | bigint; + deleted?: boolean; + destroyedAtRound?: number | bigint; + }) { + this.index = ensureBigInt(index); + this.params = params; + this.createdAtRound = + typeof createdAtRound === 'undefined' + ? undefined + : ensureBigInt(createdAtRound); + this.deleted = deleted; + this.destroyedAtRound = + typeof destroyedAtRound === 'undefined' + ? undefined + : ensureBigInt(destroyedAtRound); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Asset.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['index', this.index], + ['params', this.params.toEncodingData()], + ['created-at-round', this.createdAtRound], + ['deleted', this.deleted], + ['destroyed-at-round', this.destroyedAtRound], + ]); + } + + static fromEncodingData(data: unknown): Asset { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Asset: ${data}`); + } + return new Asset({ + index: data.get('index'), + params: AssetParams.fromEncodingData(data.get('params') ?? new Map()), + createdAtRound: data.get('created-at-round'), + deleted: data.get('deleted'), + destroyedAtRound: data.get('destroyed-at-round'), + }); + } +} + +/** + * + */ +export class AssetBalancesResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'balances', + valueSchema: new ArraySchema(MiniAssetHolding.encodingSchema), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + public balances: MiniAssetHolding[]; + + /** + * Round at which the results were computed. + */ + public currentRound: bigint; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `AssetBalancesResponse` object. + * @param balances - + * @param currentRound - Round at which the results were computed. + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + balances, + currentRound, + nextToken, + }: { + balances: MiniAssetHolding[]; + currentRound: number | bigint; + nextToken?: string; + }) { + this.balances = balances; + this.currentRound = ensureBigInt(currentRound); + this.nextToken = nextToken; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetBalancesResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['balances', this.balances.map((v) => v.toEncodingData())], + ['current-round', this.currentRound], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): AssetBalancesResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetBalancesResponse: ${data}`); + } + return new AssetBalancesResponse({ + balances: (data.get('balances') ?? []).map((v: unknown) => + MiniAssetHolding.fromEncodingData(v) + ), + currentRound: data.get('current-round'), + nextToken: data.get('next-token'), + }); + } +} + +/** + * Describes an asset held by an account. + * Definition: + * data/basics/userBalance.go : AssetHolding + */ +export class AssetHolding implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'amount', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'asset-id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'is-frozen', valueSchema: new BooleanSchema(), omitEmpty: true }, + { + key: 'deleted', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'opted-in-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'opted-out-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * number of units held. + */ + public amount: bigint; + + /** + * Asset ID of the holding. + */ + public assetId: bigint; + + /** + * whether or not the holding is frozen. + */ + public isFrozen: boolean; + + /** + * Whether or not the asset holding is currently deleted from its account. + */ + public deleted?: boolean; + + /** + * Round during which the account opted into this asset holding. + */ + public optedInAtRound?: bigint; + + /** + * Round during which the account opted out of this asset holding. + */ + public optedOutAtRound?: bigint; + + /** + * Creates a new `AssetHolding` object. + * @param amount - number of units held. + * @param assetId - Asset ID of the holding. + * @param isFrozen - whether or not the holding is frozen. + * @param deleted - Whether or not the asset holding is currently deleted from its account. + * @param optedInAtRound - Round during which the account opted into this asset holding. + * @param optedOutAtRound - Round during which the account opted out of this asset holding. + */ + constructor({ + amount, + assetId, + isFrozen, + deleted, + optedInAtRound, + optedOutAtRound, + }: { + amount: number | bigint; + assetId: number | bigint; + isFrozen: boolean; + deleted?: boolean; + optedInAtRound?: number | bigint; + optedOutAtRound?: number | bigint; + }) { + this.amount = ensureBigInt(amount); + this.assetId = ensureBigInt(assetId); + this.isFrozen = isFrozen; + this.deleted = deleted; + this.optedInAtRound = + typeof optedInAtRound === 'undefined' + ? undefined + : ensureBigInt(optedInAtRound); + this.optedOutAtRound = + typeof optedOutAtRound === 'undefined' + ? undefined + : ensureBigInt(optedOutAtRound); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetHolding.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['amount', this.amount], + ['asset-id', this.assetId], + ['is-frozen', this.isFrozen], + ['deleted', this.deleted], + ['opted-in-at-round', this.optedInAtRound], + ['opted-out-at-round', this.optedOutAtRound], + ]); + } + + static fromEncodingData(data: unknown): AssetHolding { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetHolding: ${data}`); + } + return new AssetHolding({ + amount: data.get('amount'), + assetId: data.get('asset-id'), + isFrozen: data.get('is-frozen'), + deleted: data.get('deleted'), + optedInAtRound: data.get('opted-in-at-round'), + optedOutAtRound: data.get('opted-out-at-round'), + }); + } +} + +/** + * + */ +export class AssetHoldingsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'assets', + valueSchema: new ArraySchema(AssetHolding.encodingSchema), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + public assets: AssetHolding[]; + + /** + * Round at which the results were computed. + */ + public currentRound: bigint; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `AssetHoldingsResponse` object. + * @param assets - + * @param currentRound - Round at which the results were computed. + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + assets, + currentRound, + nextToken, + }: { + assets: AssetHolding[]; + currentRound: number | bigint; + nextToken?: string; + }) { + this.assets = assets; + this.currentRound = ensureBigInt(currentRound); + this.nextToken = nextToken; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetHoldingsResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['assets', this.assets.map((v) => v.toEncodingData())], + ['current-round', this.currentRound], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): AssetHoldingsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetHoldingsResponse: ${data}`); + } + return new AssetHoldingsResponse({ + assets: (data.get('assets') ?? []).map((v: unknown) => + AssetHolding.fromEncodingData(v) + ), + currentRound: data.get('current-round'), + nextToken: data.get('next-token'), + }); + } +} + +/** + * AssetParams specifies the parameters for an asset. + * (apar) when part of an AssetConfig transaction. + * Definition: + * data/transactions/asset.go : AssetParams + */ +export class AssetParams implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'creator', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'decimals', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'total', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'clawback', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'default-frozen', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'freeze', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'manager', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'metadata-hash', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'name', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'name-b64', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'reserve', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'unit-name', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'unit-name-b64', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'url', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'url-b64', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * The address that created this asset. This is the address where the parameters + * for this asset can be found, and also the address where unwanted asset units can + * be sent in the worst case. + */ + public creator: string; + + /** + * The number of digits to use after the decimal point when displaying this asset. + * If 0, the asset is not divisible. If 1, the base unit of the asset is in tenths. + * If 2, the base unit of the asset is in hundredths, and so on. This value must be + * between 0 and 19 (inclusive). + */ + public decimals: number; + + /** + * The total number of units of this asset. + */ + public total: bigint; + + /** + * Address of account used to clawback holdings of this asset. If empty, clawback + * is not permitted. + */ + public clawback?: string; + + /** + * Whether holdings of this asset are frozen by default. + */ + public defaultFrozen?: boolean; + + /** + * Address of account used to freeze holdings of this asset. If empty, freezing is + * not permitted. + */ + public freeze?: string; + + /** + * Address of account used to manage the keys of this asset and to destroy it. + */ + public manager?: string; + + /** + * A commitment to some unspecified asset metadata. The format of this metadata is + * up to the application. + */ + public metadataHash?: Uint8Array; + + /** + * Name of this asset, as supplied by the creator. Included only when the asset + * name is composed of printable utf-8 characters. + */ + public name?: string; + + /** + * Base64 encoded name of this asset, as supplied by the creator. + */ + public nameB64?: Uint8Array; + + /** + * Address of account holding reserve (non-minted) units of this asset. + */ + public reserve?: string; + + /** + * Name of a unit of this asset, as supplied by the creator. Included only when the + * name of a unit of this asset is composed of printable utf-8 characters. + */ + public unitName?: string; + + /** + * Base64 encoded name of a unit of this asset, as supplied by the creator. + */ + public unitNameB64?: Uint8Array; + + /** + * URL where more information about the asset can be retrieved. Included only when + * the URL is composed of printable utf-8 characters. + */ + public url?: string; + + /** + * Base64 encoded URL where more information about the asset can be retrieved. + */ + public urlB64?: Uint8Array; + + /** + * Creates a new `AssetParams` object. + * @param creator - The address that created this asset. This is the address where the parameters + * for this asset can be found, and also the address where unwanted asset units can + * be sent in the worst case. + * @param decimals - The number of digits to use after the decimal point when displaying this asset. + * If 0, the asset is not divisible. If 1, the base unit of the asset is in tenths. + * If 2, the base unit of the asset is in hundredths, and so on. This value must be + * between 0 and 19 (inclusive). + * @param total - The total number of units of this asset. + * @param clawback - Address of account used to clawback holdings of this asset. If empty, clawback + * is not permitted. + * @param defaultFrozen - Whether holdings of this asset are frozen by default. + * @param freeze - Address of account used to freeze holdings of this asset. If empty, freezing is + * not permitted. + * @param manager - Address of account used to manage the keys of this asset and to destroy it. + * @param metadataHash - A commitment to some unspecified asset metadata. The format of this metadata is + * up to the application. + * @param name - Name of this asset, as supplied by the creator. Included only when the asset + * name is composed of printable utf-8 characters. + * @param nameB64 - Base64 encoded name of this asset, as supplied by the creator. + * @param reserve - Address of account holding reserve (non-minted) units of this asset. + * @param unitName - Name of a unit of this asset, as supplied by the creator. Included only when the + * name of a unit of this asset is composed of printable utf-8 characters. + * @param unitNameB64 - Base64 encoded name of a unit of this asset, as supplied by the creator. + * @param url - URL where more information about the asset can be retrieved. Included only when + * the URL is composed of printable utf-8 characters. + * @param urlB64 - Base64 encoded URL where more information about the asset can be retrieved. + */ + constructor({ + creator, + decimals, + total, + clawback, + defaultFrozen, + freeze, + manager, + metadataHash, + name, + nameB64, + reserve, + unitName, + unitNameB64, + url, + urlB64, + }: { + creator: string; + decimals: number | bigint; + total: number | bigint; + clawback?: string; + defaultFrozen?: boolean; + freeze?: string; + manager?: string; + metadataHash?: string | Uint8Array; + name?: string; + nameB64?: string | Uint8Array; + reserve?: string; + unitName?: string; + unitNameB64?: string | Uint8Array; + url?: string; + urlB64?: string | Uint8Array; + }) { + this.creator = creator; + this.decimals = ensureSafeInteger(decimals); + this.total = ensureBigInt(total); + this.clawback = clawback; + this.defaultFrozen = defaultFrozen; + this.freeze = freeze; + this.manager = manager; + this.metadataHash = + typeof metadataHash === 'string' + ? base64ToBytes(metadataHash) + : metadataHash; + this.name = name; + this.nameB64 = + typeof nameB64 === 'string' ? base64ToBytes(nameB64) : nameB64; + this.reserve = reserve; + this.unitName = unitName; + this.unitNameB64 = + typeof unitNameB64 === 'string' + ? base64ToBytes(unitNameB64) + : unitNameB64; + this.url = url; + this.urlB64 = typeof urlB64 === 'string' ? base64ToBytes(urlB64) : urlB64; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetParams.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['creator', this.creator], + ['decimals', this.decimals], + ['total', this.total], + ['clawback', this.clawback], + ['default-frozen', this.defaultFrozen], + ['freeze', this.freeze], + ['manager', this.manager], + ['metadata-hash', this.metadataHash], + ['name', this.name], + ['name-b64', this.nameB64], + ['reserve', this.reserve], + ['unit-name', this.unitName], + ['unit-name-b64', this.unitNameB64], + ['url', this.url], + ['url-b64', this.urlB64], + ]); + } + + static fromEncodingData(data: unknown): AssetParams { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetParams: ${data}`); + } + return new AssetParams({ + creator: data.get('creator'), + decimals: data.get('decimals'), + total: data.get('total'), + clawback: data.get('clawback'), + defaultFrozen: data.get('default-frozen'), + freeze: data.get('freeze'), + manager: data.get('manager'), + metadataHash: data.get('metadata-hash'), + name: data.get('name'), + nameB64: data.get('name-b64'), + reserve: data.get('reserve'), + unitName: data.get('unit-name'), + unitNameB64: data.get('unit-name-b64'), + url: data.get('url'), + urlB64: data.get('url-b64'), + }); + } +} + +/** + * + */ +export class AssetResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'asset', valueSchema: Asset.encodingSchema, omitEmpty: true }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * Specifies both the unique identifier and the parameters for an asset + */ + public asset: Asset; + + /** + * Round at which the results were computed. + */ + public currentRound: bigint; + + /** + * Creates a new `AssetResponse` object. + * @param asset - Specifies both the unique identifier and the parameters for an asset + * @param currentRound - Round at which the results were computed. + */ + constructor({ + asset, + currentRound, + }: { + asset: Asset; + currentRound: number | bigint; + }) { + this.asset = asset; + this.currentRound = ensureBigInt(currentRound); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['asset', this.asset.toEncodingData()], + ['current-round', this.currentRound], + ]); + } + + static fromEncodingData(data: unknown): AssetResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetResponse: ${data}`); + } + return new AssetResponse({ + asset: Asset.fromEncodingData(data.get('asset') ?? new Map()), + currentRound: data.get('current-round'), + }); + } +} + +/** + * + */ +export class AssetsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'assets', + valueSchema: new ArraySchema(Asset.encodingSchema), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + public assets: Asset[]; + + /** + * Round at which the results were computed. + */ + public currentRound: bigint; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `AssetsResponse` object. + * @param assets - + * @param currentRound - Round at which the results were computed. + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + assets, + currentRound, + nextToken, + }: { + assets: Asset[]; + currentRound: number | bigint; + nextToken?: string; + }) { + this.assets = assets; + this.currentRound = ensureBigInt(currentRound); + this.nextToken = nextToken; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return AssetsResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['assets', this.assets.map((v) => v.toEncodingData())], + ['current-round', this.currentRound], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): AssetsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded AssetsResponse: ${data}`); + } + return new AssetsResponse({ + assets: (data.get('assets') ?? []).map((v: unknown) => + Asset.fromEncodingData(v) + ), + currentRound: data.get('current-round'), + nextToken: data.get('next-token'), + }); + } +} + +/** + * Block information. + * Definition: + * data/bookkeeping/block.go : Block + */ +export class Block implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'genesis-hash', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { key: 'genesis-id', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'previous-block-hash', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'seed', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'timestamp', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'transactions-root', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'transactions-root-sha256', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }, + { + key: 'bonus', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'fees-collected', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'participation-updates', + valueSchema: new OptionalSchema(ParticipationUpdates.encodingSchema), + omitEmpty: true, + }, + { + key: 'previous-block-hash-512', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'proposer', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'proposer-payout', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'rewards', + valueSchema: new OptionalSchema(BlockRewards.encodingSchema), + omitEmpty: true, + }, + { + key: 'state-proof-tracking', + valueSchema: new OptionalSchema( + new ArraySchema(StateProofTracking.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'transactions', + valueSchema: new OptionalSchema( + new ArraySchema(Transaction.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'transactions-root-sha512', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'txn-counter', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'upgrade-state', + valueSchema: new OptionalSchema(BlockUpgradeState.encodingSchema), + omitEmpty: true, + }, + { + key: 'upgrade-vote', + valueSchema: new OptionalSchema(BlockUpgradeVote.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (gh) hash to which this block belongs. + */ + public genesisHash: Uint8Array; + + /** + * (gen) ID to which this block belongs. + */ + public genesisId: string; + + /** + * (prev) Previous block hash. + */ + public previousBlockHash: Uint8Array; + + /** + * (rnd) Current round on which this block was appended to the chain. + */ + public round: bigint; + + /** + * (seed) Sortition seed. + */ + public seed: Uint8Array; + + /** + * (ts) Block creation timestamp in seconds since eposh + */ + public timestamp: number; + + /** + * (txn) TransactionsRoot authenticates the set of transactions appearing in the + * block. More specifically, it's the root of a merkle tree whose leaves are the + * block's Txids, in lexicographic order. For the empty block, it's 0. Note that + * the TxnRoot does not authenticate the signatures on the transactions, only the + * transactions themselves. Two blocks with the same transactions but in a + * different order and with different signatures will have the same TxnRoot. + */ + public transactionsRoot: Uint8Array; + + /** + * (txn256) TransactionsRootSHA256 is an auxiliary TransactionRoot, built using a + * vector commitment instead of a merkle tree, and SHA256 hash function instead of + * the default SHA512_256. This commitment can be used on environments where only + * the SHA256 function exists. + */ + public transactionsRootSha256: Uint8Array; + + /** + * the potential bonus payout for this block. + */ + public bonus?: number; + + /** + * the sum of all fees paid by transactions in this block. + */ + public feesCollected?: number; + + /** + * Participation account data that needs to be checked/acted on by the network. + */ + public participationUpdates?: ParticipationUpdates; + + /** + * (prev512) Previous block hash, using SHA-512. + */ + public previousBlockHash512?: Uint8Array; + + /** + * the proposer of this block. + */ + public proposer?: Address; + + /** + * the actual amount transferred to the proposer from the fee sink. + */ + public proposerPayout?: number; + + /** + * Fields relating to rewards, + */ + public rewards?: BlockRewards; + + /** + * Tracks the status of state proofs. + */ + public stateProofTracking?: StateProofTracking[]; + + /** + * (txns) list of transactions corresponding to a given round. + */ + public transactions?: Transaction[]; + + /** + * (txn512) TransactionsRootSHA512 is an auxiliary TransactionRoot, built using a + * vector commitment instead of a merkle tree, and SHA512 hash function instead of + * the default SHA512_256. + */ + public transactionsRootSha512?: Uint8Array; + + /** + * (tc) TxnCounter counts the number of transactions committed in the ledger, from + * the time at which support for this feature was introduced. + * Specifically, TxnCounter is the number of the next transaction that will be + * committed after this block. It is 0 when no transactions have ever been + * committed (since TxnCounter started being supported). + */ + public txnCounter?: number; + + /** + * Fields relating to a protocol upgrade. + */ + public upgradeState?: BlockUpgradeState; + + /** + * Fields relating to voting for a protocol upgrade. + */ + public upgradeVote?: BlockUpgradeVote; + + /** + * Creates a new `Block` object. + * @param genesisHash - (gh) hash to which this block belongs. + * @param genesisId - (gen) ID to which this block belongs. + * @param previousBlockHash - (prev) Previous block hash. + * @param round - (rnd) Current round on which this block was appended to the chain. + * @param seed - (seed) Sortition seed. + * @param timestamp - (ts) Block creation timestamp in seconds since eposh + * @param transactionsRoot - (txn) TransactionsRoot authenticates the set of transactions appearing in the + * block. More specifically, it's the root of a merkle tree whose leaves are the + * block's Txids, in lexicographic order. For the empty block, it's 0. Note that + * the TxnRoot does not authenticate the signatures on the transactions, only the + * transactions themselves. Two blocks with the same transactions but in a + * different order and with different signatures will have the same TxnRoot. + * @param transactionsRootSha256 - (txn256) TransactionsRootSHA256 is an auxiliary TransactionRoot, built using a + * vector commitment instead of a merkle tree, and SHA256 hash function instead of + * the default SHA512_256. This commitment can be used on environments where only + * the SHA256 function exists. + * @param bonus - the potential bonus payout for this block. + * @param feesCollected - the sum of all fees paid by transactions in this block. + * @param participationUpdates - Participation account data that needs to be checked/acted on by the network. + * @param previousBlockHash512 - (prev512) Previous block hash, using SHA-512. + * @param proposer - the proposer of this block. + * @param proposerPayout - the actual amount transferred to the proposer from the fee sink. + * @param rewards - Fields relating to rewards, + * @param stateProofTracking - Tracks the status of state proofs. + * @param transactions - (txns) list of transactions corresponding to a given round. + * @param transactionsRootSha512 - (txn512) TransactionsRootSHA512 is an auxiliary TransactionRoot, built using a + * vector commitment instead of a merkle tree, and SHA512 hash function instead of + * the default SHA512_256. + * @param txnCounter - (tc) TxnCounter counts the number of transactions committed in the ledger, from + * the time at which support for this feature was introduced. + * Specifically, TxnCounter is the number of the next transaction that will be + * committed after this block. It is 0 when no transactions have ever been + * committed (since TxnCounter started being supported). + * @param upgradeState - Fields relating to a protocol upgrade. + * @param upgradeVote - Fields relating to voting for a protocol upgrade. + */ + constructor({ + genesisHash, + genesisId, + previousBlockHash, + round, + seed, + timestamp, + transactionsRoot, + transactionsRootSha256, + bonus, + feesCollected, + participationUpdates, + previousBlockHash512, + proposer, + proposerPayout, + rewards, + stateProofTracking, + transactions, + transactionsRootSha512, + txnCounter, + upgradeState, + upgradeVote, + }: { + genesisHash: string | Uint8Array; + genesisId: string; + previousBlockHash: string | Uint8Array; + round: number | bigint; + seed: string | Uint8Array; + timestamp: number | bigint; + transactionsRoot: string | Uint8Array; + transactionsRootSha256: string | Uint8Array; + bonus?: number | bigint; + feesCollected?: number | bigint; + participationUpdates?: ParticipationUpdates; + previousBlockHash512?: string | Uint8Array; + proposer?: Address | string; + proposerPayout?: number | bigint; + rewards?: BlockRewards; + stateProofTracking?: StateProofTracking[]; + transactions?: Transaction[]; + transactionsRootSha512?: string | Uint8Array; + txnCounter?: number | bigint; + upgradeState?: BlockUpgradeState; + upgradeVote?: BlockUpgradeVote; + }) { + this.genesisHash = + typeof genesisHash === 'string' + ? base64ToBytes(genesisHash) + : genesisHash; + this.genesisId = genesisId; + this.previousBlockHash = + typeof previousBlockHash === 'string' + ? base64ToBytes(previousBlockHash) + : previousBlockHash; + this.round = ensureBigInt(round); + this.seed = typeof seed === 'string' ? base64ToBytes(seed) : seed; + this.timestamp = ensureSafeInteger(timestamp); + this.transactionsRoot = + typeof transactionsRoot === 'string' + ? base64ToBytes(transactionsRoot) + : transactionsRoot; + this.transactionsRootSha256 = + typeof transactionsRootSha256 === 'string' + ? base64ToBytes(transactionsRootSha256) + : transactionsRootSha256; + this.bonus = + typeof bonus === 'undefined' ? undefined : ensureSafeInteger(bonus); + this.feesCollected = + typeof feesCollected === 'undefined' + ? undefined + : ensureSafeInteger(feesCollected); + this.participationUpdates = participationUpdates; + this.previousBlockHash512 = + typeof previousBlockHash512 === 'string' + ? base64ToBytes(previousBlockHash512) + : previousBlockHash512; + this.proposer = + typeof proposer === 'string' ? Address.fromString(proposer) : proposer; + this.proposerPayout = + typeof proposerPayout === 'undefined' + ? undefined + : ensureSafeInteger(proposerPayout); + this.rewards = rewards; + this.stateProofTracking = stateProofTracking; + this.transactions = transactions; + this.transactionsRootSha512 = + typeof transactionsRootSha512 === 'string' + ? base64ToBytes(transactionsRootSha512) + : transactionsRootSha512; + this.txnCounter = + typeof txnCounter === 'undefined' + ? undefined + : ensureSafeInteger(txnCounter); + this.upgradeState = upgradeState; + this.upgradeVote = upgradeVote; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Block.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['genesis-hash', this.genesisHash], + ['genesis-id', this.genesisId], + ['previous-block-hash', this.previousBlockHash], + ['round', this.round], + ['seed', this.seed], + ['timestamp', this.timestamp], + ['transactions-root', this.transactionsRoot], + ['transactions-root-sha256', this.transactionsRootSha256], + ['bonus', this.bonus], + ['fees-collected', this.feesCollected], + [ + 'participation-updates', + typeof this.participationUpdates !== 'undefined' + ? this.participationUpdates.toEncodingData() + : undefined, + ], + ['previous-block-hash-512', this.previousBlockHash512], + [ + 'proposer', + typeof this.proposer !== 'undefined' + ? this.proposer.toString() + : undefined, + ], + ['proposer-payout', this.proposerPayout], + [ + 'rewards', + typeof this.rewards !== 'undefined' + ? this.rewards.toEncodingData() + : undefined, + ], + [ + 'state-proof-tracking', + typeof this.stateProofTracking !== 'undefined' + ? this.stateProofTracking.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'transactions', + typeof this.transactions !== 'undefined' + ? this.transactions.map((v) => v.toEncodingData()) + : undefined, + ], + ['transactions-root-sha512', this.transactionsRootSha512], + ['txn-counter', this.txnCounter], + [ + 'upgrade-state', + typeof this.upgradeState !== 'undefined' + ? this.upgradeState.toEncodingData() + : undefined, + ], + [ + 'upgrade-vote', + typeof this.upgradeVote !== 'undefined' + ? this.upgradeVote.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): Block { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Block: ${data}`); + } + return new Block({ + genesisHash: data.get('genesis-hash'), + genesisId: data.get('genesis-id'), + previousBlockHash: data.get('previous-block-hash'), + round: data.get('round'), + seed: data.get('seed'), + timestamp: data.get('timestamp'), + transactionsRoot: data.get('transactions-root'), + transactionsRootSha256: data.get('transactions-root-sha256'), + bonus: data.get('bonus'), + feesCollected: data.get('fees-collected'), + participationUpdates: + typeof data.get('participation-updates') !== 'undefined' + ? ParticipationUpdates.fromEncodingData( + data.get('participation-updates') + ) + : undefined, + previousBlockHash512: data.get('previous-block-hash-512'), + proposer: data.get('proposer'), + proposerPayout: data.get('proposer-payout'), + rewards: + typeof data.get('rewards') !== 'undefined' + ? BlockRewards.fromEncodingData(data.get('rewards')) + : undefined, + stateProofTracking: + typeof data.get('state-proof-tracking') !== 'undefined' + ? data + .get('state-proof-tracking') + .map((v: unknown) => StateProofTracking.fromEncodingData(v)) + : undefined, + transactions: + typeof data.get('transactions') !== 'undefined' + ? data + .get('transactions') + .map((v: unknown) => Transaction.fromEncodingData(v)) + : undefined, + transactionsRootSha512: data.get('transactions-root-sha512'), + txnCounter: data.get('txn-counter'), + upgradeState: + typeof data.get('upgrade-state') !== 'undefined' + ? BlockUpgradeState.fromEncodingData(data.get('upgrade-state')) + : undefined, + upgradeVote: + typeof data.get('upgrade-vote') !== 'undefined' + ? BlockUpgradeVote.fromEncodingData(data.get('upgrade-vote')) + : undefined, + }); + } +} + +/** + * + */ +export class BlockHeadersResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'blocks', + valueSchema: new ArraySchema(Block.encodingSchema), + omitEmpty: true, + }, + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + public blocks: Block[]; + + /** + * Round at which the results were computed. + */ + public currentRound: bigint; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `BlockHeadersResponse` object. + * @param blocks - + * @param currentRound - Round at which the results were computed. + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + blocks, + currentRound, + nextToken, + }: { + blocks: Block[]; + currentRound: number | bigint; + nextToken?: string; + }) { + this.blocks = blocks; + this.currentRound = ensureBigInt(currentRound); + this.nextToken = nextToken; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BlockHeadersResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['blocks', this.blocks.map((v) => v.toEncodingData())], + ['current-round', this.currentRound], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): BlockHeadersResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BlockHeadersResponse: ${data}`); + } + return new BlockHeadersResponse({ + blocks: (data.get('blocks') ?? []).map((v: unknown) => + Block.fromEncodingData(v) + ), + currentRound: data.get('current-round'), + nextToken: data.get('next-token'), + }); + } +} + +/** + * Fields relating to rewards, + */ +export class BlockRewards implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'fee-sink', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'rewards-calculation-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'rewards-level', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'rewards-pool', + valueSchema: new StringSchema(), + omitEmpty: true, + }, + { + key: 'rewards-rate', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'rewards-residue', + valueSchema: new Uint64Schema(), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (fees) accepts transaction fees, it can only spend to the incentive pool. + */ + public feeSink: string; + + /** + * (rwcalr) number of leftover MicroAlgos after the distribution of rewards-rate + * MicroAlgos for every reward unit in the next round. + */ + public rewardsCalculationRound: bigint; + + /** + * (earn) How many rewards, in MicroAlgos, have been distributed to each RewardUnit + * of MicroAlgos since genesis. + */ + public rewardsLevel: bigint; + + /** + * (rwd) accepts periodic injections from the fee-sink and continually + * redistributes them as rewards. + */ + public rewardsPool: string; + + /** + * (rate) Number of new MicroAlgos added to the participation stake from rewards at + * the next round. + */ + public rewardsRate: bigint; + + /** + * (frac) Number of leftover MicroAlgos after the distribution of + * RewardsRate/rewardUnits MicroAlgos for every reward unit in the next round. + */ + public rewardsResidue: bigint; + + /** + * Creates a new `BlockRewards` object. + * @param feeSink - (fees) accepts transaction fees, it can only spend to the incentive pool. + * @param rewardsCalculationRound - (rwcalr) number of leftover MicroAlgos after the distribution of rewards-rate + * MicroAlgos for every reward unit in the next round. + * @param rewardsLevel - (earn) How many rewards, in MicroAlgos, have been distributed to each RewardUnit + * of MicroAlgos since genesis. + * @param rewardsPool - (rwd) accepts periodic injections from the fee-sink and continually + * redistributes them as rewards. + * @param rewardsRate - (rate) Number of new MicroAlgos added to the participation stake from rewards at + * the next round. + * @param rewardsResidue - (frac) Number of leftover MicroAlgos after the distribution of + * RewardsRate/rewardUnits MicroAlgos for every reward unit in the next round. + */ + constructor({ + feeSink, + rewardsCalculationRound, + rewardsLevel, + rewardsPool, + rewardsRate, + rewardsResidue, + }: { + feeSink: string; + rewardsCalculationRound: number | bigint; + rewardsLevel: number | bigint; + rewardsPool: string; + rewardsRate: number | bigint; + rewardsResidue: number | bigint; + }) { + this.feeSink = feeSink; + this.rewardsCalculationRound = ensureBigInt(rewardsCalculationRound); + this.rewardsLevel = ensureBigInt(rewardsLevel); + this.rewardsPool = rewardsPool; + this.rewardsRate = ensureBigInt(rewardsRate); + this.rewardsResidue = ensureBigInt(rewardsResidue); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BlockRewards.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['fee-sink', this.feeSink], + ['rewards-calculation-round', this.rewardsCalculationRound], + ['rewards-level', this.rewardsLevel], + ['rewards-pool', this.rewardsPool], + ['rewards-rate', this.rewardsRate], + ['rewards-residue', this.rewardsResidue], + ]); + } + + static fromEncodingData(data: unknown): BlockRewards { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BlockRewards: ${data}`); + } + return new BlockRewards({ + feeSink: data.get('fee-sink'), + rewardsCalculationRound: data.get('rewards-calculation-round'), + rewardsLevel: data.get('rewards-level'), + rewardsPool: data.get('rewards-pool'), + rewardsRate: data.get('rewards-rate'), + rewardsResidue: data.get('rewards-residue'), + }); + } +} + +/** + * Fields relating to a protocol upgrade. + */ +export class BlockUpgradeState implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'current-protocol', + valueSchema: new StringSchema(), + omitEmpty: true, + }, + { + key: 'next-protocol', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'next-protocol-approvals', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'next-protocol-switch-on', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'next-protocol-vote-before', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (proto) The current protocol version. + */ + public currentProtocol: string; + + /** + * (nextproto) The next proposed protocol version. + */ + public nextProtocol?: string; + + /** + * (nextyes) Number of blocks which approved the protocol upgrade. + */ + public nextProtocolApprovals?: number; + + /** + * (nextswitch) Round on which the protocol upgrade will take effect. + */ + public nextProtocolSwitchOn?: bigint; + + /** + * (nextbefore) Deadline round for this protocol upgrade (No votes will be consider + * after this round). + */ + public nextProtocolVoteBefore?: bigint; + + /** + * Creates a new `BlockUpgradeState` object. + * @param currentProtocol - (proto) The current protocol version. + * @param nextProtocol - (nextproto) The next proposed protocol version. + * @param nextProtocolApprovals - (nextyes) Number of blocks which approved the protocol upgrade. + * @param nextProtocolSwitchOn - (nextswitch) Round on which the protocol upgrade will take effect. + * @param nextProtocolVoteBefore - (nextbefore) Deadline round for this protocol upgrade (No votes will be consider + * after this round). + */ + constructor({ + currentProtocol, + nextProtocol, + nextProtocolApprovals, + nextProtocolSwitchOn, + nextProtocolVoteBefore, + }: { + currentProtocol: string; + nextProtocol?: string; + nextProtocolApprovals?: number | bigint; + nextProtocolSwitchOn?: number | bigint; + nextProtocolVoteBefore?: number | bigint; + }) { + this.currentProtocol = currentProtocol; + this.nextProtocol = nextProtocol; + this.nextProtocolApprovals = + typeof nextProtocolApprovals === 'undefined' + ? undefined + : ensureSafeInteger(nextProtocolApprovals); + this.nextProtocolSwitchOn = + typeof nextProtocolSwitchOn === 'undefined' + ? undefined + : ensureBigInt(nextProtocolSwitchOn); + this.nextProtocolVoteBefore = + typeof nextProtocolVoteBefore === 'undefined' + ? undefined + : ensureBigInt(nextProtocolVoteBefore); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BlockUpgradeState.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['current-protocol', this.currentProtocol], + ['next-protocol', this.nextProtocol], + ['next-protocol-approvals', this.nextProtocolApprovals], + ['next-protocol-switch-on', this.nextProtocolSwitchOn], + ['next-protocol-vote-before', this.nextProtocolVoteBefore], + ]); + } + + static fromEncodingData(data: unknown): BlockUpgradeState { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BlockUpgradeState: ${data}`); + } + return new BlockUpgradeState({ + currentProtocol: data.get('current-protocol'), + nextProtocol: data.get('next-protocol'), + nextProtocolApprovals: data.get('next-protocol-approvals'), + nextProtocolSwitchOn: data.get('next-protocol-switch-on'), + nextProtocolVoteBefore: data.get('next-protocol-vote-before'), + }); + } +} + +/** + * Fields relating to voting for a protocol upgrade. + */ +export class BlockUpgradeVote implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'upgrade-approve', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'upgrade-delay', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'upgrade-propose', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (upgradeyes) Indicates a yes vote for the current proposal. + */ + public upgradeApprove?: boolean; + + /** + * (upgradedelay) Indicates the time between acceptance and execution. + */ + public upgradeDelay?: bigint; + + /** + * (upgradeprop) Indicates a proposed upgrade. + */ + public upgradePropose?: string; + + /** + * Creates a new `BlockUpgradeVote` object. + * @param upgradeApprove - (upgradeyes) Indicates a yes vote for the current proposal. + * @param upgradeDelay - (upgradedelay) Indicates the time between acceptance and execution. + * @param upgradePropose - (upgradeprop) Indicates a proposed upgrade. + */ + constructor({ + upgradeApprove, + upgradeDelay, + upgradePropose, + }: { + upgradeApprove?: boolean; + upgradeDelay?: number | bigint; + upgradePropose?: string; + }) { + this.upgradeApprove = upgradeApprove; + this.upgradeDelay = + typeof upgradeDelay === 'undefined' + ? undefined + : ensureBigInt(upgradeDelay); + this.upgradePropose = upgradePropose; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BlockUpgradeVote.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['upgrade-approve', this.upgradeApprove], + ['upgrade-delay', this.upgradeDelay], + ['upgrade-propose', this.upgradePropose], + ]); + } + + static fromEncodingData(data: unknown): BlockUpgradeVote { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BlockUpgradeVote: ${data}`); + } + return new BlockUpgradeVote({ + upgradeApprove: data.get('upgrade-approve'), + upgradeDelay: data.get('upgrade-delay'), + upgradePropose: data.get('upgrade-propose'), + }); + } +} + +/** + * Box name and its content. + */ +export class Box implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'name', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'value', valueSchema: new ByteArraySchema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + + /** + * (name) box name, base64 encoded + */ + public name: Uint8Array; + + /** + * The round for which this information is relevant + */ + public round: bigint; + + /** + * (value) box value, base64 encoded. + */ + public value: Uint8Array; + + /** + * Creates a new `Box` object. + * @param name - (name) box name, base64 encoded + * @param round - The round for which this information is relevant + * @param value - (value) box value, base64 encoded. + */ + constructor({ + name, + round, + value, + }: { + name: string | Uint8Array; + round: number | bigint; + value: string | Uint8Array; + }) { + this.name = typeof name === 'string' ? base64ToBytes(name) : name; + this.round = ensureBigInt(round); + this.value = typeof value === 'string' ? base64ToBytes(value) : value; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Box.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['name', this.name], + ['round', this.round], + ['value', this.value], + ]); + } + + static fromEncodingData(data: unknown): Box { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Box: ${data}`); + } + return new Box({ + name: data.get('name'), + round: data.get('round'), + value: data.get('value'), + }); + } +} + +/** + * Box descriptor describes an app box without a value. + */ +export class BoxDescriptor implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'name', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + + /** + * Base64 encoded box name + */ + public name: Uint8Array; + + /** + * Creates a new `BoxDescriptor` object. + * @param name - Base64 encoded box name + */ + constructor({ name }: { name: string | Uint8Array }) { + this.name = typeof name === 'string' ? base64ToBytes(name) : name; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BoxDescriptor.encodingSchema; + } + + toEncodingData(): Map { + return new Map([['name', this.name]]); + } + + static fromEncodingData(data: unknown): BoxDescriptor { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BoxDescriptor: ${data}`); + } + return new BoxDescriptor({ + name: data.get('name'), + }); + } +} + +/** + * BoxReference names a box by its name and the application ID it belongs to. + */ +export class BoxReference implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'app', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'name', valueSchema: new ByteArraySchema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + + /** + * Application ID to which the box belongs, or zero if referring to the called + * application. + */ + public app: number; + + /** + * Base64 encoded box name + */ + public name: Uint8Array; + + /** + * Creates a new `BoxReference` object. + * @param app - Application ID to which the box belongs, or zero if referring to the called + * application. + * @param name - Base64 encoded box name + */ + constructor({ + app, + name, + }: { + app: number | bigint; + name: string | Uint8Array; + }) { + this.app = ensureSafeInteger(app); + this.name = typeof name === 'string' ? base64ToBytes(name) : name; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BoxReference.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['app', this.app], + ['name', this.name], + ]); + } + + static fromEncodingData(data: unknown): BoxReference { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BoxReference: ${data}`); + } + return new BoxReference({ + app: data.get('app'), + name: data.get('name'), + }); + } +} + +/** + * Box names of an application + */ +export class BoxesResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'application-id', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'boxes', + valueSchema: new ArraySchema(BoxDescriptor.encodingSchema), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (appidx) application index. + */ + public applicationId: bigint; + + public boxes: BoxDescriptor[]; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `BoxesResponse` object. + * @param applicationId - (appidx) application index. + * @param boxes - + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + applicationId, + boxes, + nextToken, + }: { + applicationId: number | bigint; + boxes: BoxDescriptor[]; + nextToken?: string; + }) { + this.applicationId = ensureBigInt(applicationId); + this.boxes = boxes; + this.nextToken = nextToken; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return BoxesResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['application-id', this.applicationId], + ['boxes', this.boxes.map((v) => v.toEncodingData())], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): BoxesResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded BoxesResponse: ${data}`); + } + return new BoxesResponse({ + applicationId: data.get('application-id'), + boxes: (data.get('boxes') ?? []).map((v: unknown) => + BoxDescriptor.fromEncodingData(v) + ), + nextToken: data.get('next-token'), + }); + } +} + +/** + * Response for errors + */ +export class ErrorResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'message', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'data', + valueSchema: new OptionalSchema(UntypedValue.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + public message: string; + + public data?: UntypedValue; + + /** + * Creates a new `ErrorResponse` object. + * @param message - + * @param data - + */ + constructor({ message, data }: { message: string; data?: UntypedValue }) { + this.message = message; + this.data = data; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ErrorResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['message', this.message], + [ + 'data', + typeof this.data !== 'undefined' + ? this.data.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): ErrorResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ErrorResponse: ${data}`); + } + return new ErrorResponse({ + message: data.get('message'), + data: + typeof data.get('data') !== 'undefined' + ? UntypedValue.fromEncodingData(data.get('data')) + : undefined, + }); + } +} + +/** + * Represents a TEAL value delta. + */ +export class EvalDelta implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'action', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'bytes', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'uint', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (at) delta action. + */ + public action: number; + + /** + * (bs) bytes value. + */ + public bytes?: string; + + /** + * (ui) uint value. + */ + public uint?: bigint; + + /** + * Creates a new `EvalDelta` object. + * @param action - (at) delta action. + * @param bytes - (bs) bytes value. + * @param uint - (ui) uint value. + */ + constructor({ + action, + bytes, + uint, + }: { + action: number | bigint; + bytes?: string; + uint?: number | bigint; + }) { + this.action = ensureSafeInteger(action); + this.bytes = bytes; + this.uint = typeof uint === 'undefined' ? undefined : ensureBigInt(uint); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return EvalDelta.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['action', this.action], + ['bytes', this.bytes], + ['uint', this.uint], + ]); + } + + static fromEncodingData(data: unknown): EvalDelta { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded EvalDelta: ${data}`); + } + return new EvalDelta({ + action: data.get('action'), + bytes: data.get('bytes'), + uint: data.get('uint'), + }); + } +} + +/** + * Key-value pairs for StateDelta. + */ +export class EvalDeltaKeyValue implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'key', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'value', valueSchema: EvalDelta.encodingSchema, omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + + public key: string; + + /** + * Represents a TEAL value delta. + */ + public value: EvalDelta; + + /** + * Creates a new `EvalDeltaKeyValue` object. + * @param key - + * @param value - Represents a TEAL value delta. + */ + constructor({ key, value }: { key: string; value: EvalDelta }) { + this.key = key; + this.value = value; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return EvalDeltaKeyValue.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['key', this.key], + ['value', this.value.toEncodingData()], + ]); + } + + static fromEncodingData(data: unknown): EvalDeltaKeyValue { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded EvalDeltaKeyValue: ${data}`); + } + return new EvalDeltaKeyValue({ + key: data.get('key'), + value: EvalDelta.fromEncodingData(data.get('value') ?? new Map()), + }); + } +} + +export class HashFactory implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries({ + key: 'hash-type', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }); + } + return this.encodingSchemaValue; + } + + /** + * (t) + */ + public hashType?: number; + + /** + * Creates a new `HashFactory` object. + * @param hashType - (t) + */ + constructor({ hashType }: { hashType?: number | bigint }) { + this.hashType = + typeof hashType === 'undefined' ? undefined : ensureSafeInteger(hashType); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return HashFactory.encodingSchema; + } + + toEncodingData(): Map { + return new Map([['hash-type', this.hashType]]); + } + + static fromEncodingData(data: unknown): HashFactory { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded HashFactory: ${data}`); + } + return new HashFactory({ + hashType: data.get('hash-type'), + }); + } +} + +/** + * (hbprf) HbProof is a signature using HeartbeatAddress's partkey, thereby showing + * it is online. + */ +export class HbProofFields implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'hb-pk', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'hb-pk1sig', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'hb-pk2', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'hb-pk2sig', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'hb-sig', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (p) Public key of the heartbeat message. + */ + public hbPk?: Uint8Array; + + /** + * (p1s) Signature of OneTimeSignatureSubkeyOffsetID(PK, Batch, Offset) under the + * key PK2. + */ + public hbPk1sig?: Uint8Array; + + /** + * (p2) Key for new-style two-level ephemeral signature. + */ + public hbPk2?: Uint8Array; + + /** + * (p2s) Signature of OneTimeSignatureSubkeyBatchID(PK2, Batch) under the master + * key (OneTimeSignatureVerifier). + */ + public hbPk2sig?: Uint8Array; + + /** + * (s) Signature of the heartbeat message. + */ + public hbSig?: Uint8Array; + + /** + * Creates a new `HbProofFields` object. + * @param hbPk - (p) Public key of the heartbeat message. + * @param hbPk1sig - (p1s) Signature of OneTimeSignatureSubkeyOffsetID(PK, Batch, Offset) under the + * key PK2. + * @param hbPk2 - (p2) Key for new-style two-level ephemeral signature. + * @param hbPk2sig - (p2s) Signature of OneTimeSignatureSubkeyBatchID(PK2, Batch) under the master + * key (OneTimeSignatureVerifier). + * @param hbSig - (s) Signature of the heartbeat message. + */ + constructor({ + hbPk, + hbPk1sig, + hbPk2, + hbPk2sig, + hbSig, + }: { + hbPk?: string | Uint8Array; + hbPk1sig?: string | Uint8Array; + hbPk2?: string | Uint8Array; + hbPk2sig?: string | Uint8Array; + hbSig?: string | Uint8Array; + }) { + this.hbPk = typeof hbPk === 'string' ? base64ToBytes(hbPk) : hbPk; + this.hbPk1sig = + typeof hbPk1sig === 'string' ? base64ToBytes(hbPk1sig) : hbPk1sig; + this.hbPk2 = typeof hbPk2 === 'string' ? base64ToBytes(hbPk2) : hbPk2; + this.hbPk2sig = + typeof hbPk2sig === 'string' ? base64ToBytes(hbPk2sig) : hbPk2sig; + this.hbSig = typeof hbSig === 'string' ? base64ToBytes(hbSig) : hbSig; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return HbProofFields.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['hb-pk', this.hbPk], + ['hb-pk1sig', this.hbPk1sig], + ['hb-pk2', this.hbPk2], + ['hb-pk2sig', this.hbPk2sig], + ['hb-sig', this.hbSig], + ]); + } + + static fromEncodingData(data: unknown): HbProofFields { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded HbProofFields: ${data}`); + } + return new HbProofFields({ + hbPk: data.get('hb-pk'), + hbPk1sig: data.get('hb-pk1sig'), + hbPk2: data.get('hb-pk2'), + hbPk2sig: data.get('hb-pk2sig'), + hbSig: data.get('hb-sig'), + }); + } +} + +/** + * A health check response. + */ +export class HealthCheck implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'db-available', + valueSchema: new BooleanSchema(), + omitEmpty: true, + }, + { + key: 'is-migrating', + valueSchema: new BooleanSchema(), + omitEmpty: true, + }, + { key: 'message', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'round', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'version', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'data', + valueSchema: new OptionalSchema(UntypedValue.encodingSchema), + omitEmpty: true, + }, + { + key: 'errors', + valueSchema: new OptionalSchema(new ArraySchema(new StringSchema())), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + public dbAvailable: boolean; + + public isMigrating: boolean; + + public message: string; + + public round: bigint; + + /** + * Current version. + */ + public version: string; + + public data?: UntypedValue; + + public errors?: string[]; + + /** + * Creates a new `HealthCheck` object. + * @param dbAvailable - + * @param isMigrating - + * @param message - + * @param round - + * @param version - Current version. + * @param data - + * @param errors - + */ + constructor({ + dbAvailable, + isMigrating, + message, + round, + version, + data, + errors, + }: { + dbAvailable: boolean; + isMigrating: boolean; + message: string; + round: number | bigint; + version: string; + data?: UntypedValue; + errors?: string[]; + }) { + this.dbAvailable = dbAvailable; + this.isMigrating = isMigrating; + this.message = message; + this.round = ensureBigInt(round); + this.version = version; + this.data = data; + this.errors = errors; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return HealthCheck.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['db-available', this.dbAvailable], + ['is-migrating', this.isMigrating], + ['message', this.message], + ['round', this.round], + ['version', this.version], + [ + 'data', + typeof this.data !== 'undefined' + ? this.data.toEncodingData() + : undefined, + ], + ['errors', this.errors], + ]); + } + + static fromEncodingData(data: unknown): HealthCheck { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded HealthCheck: ${data}`); + } + return new HealthCheck({ + dbAvailable: data.get('db-available'), + isMigrating: data.get('is-migrating'), + message: data.get('message'), + round: data.get('round'), + version: data.get('version'), + data: + typeof data.get('data') !== 'undefined' + ? UntypedValue.fromEncodingData(data.get('data')) + : undefined, + errors: data.get('errors'), + }); + } +} + +/** + * HoldingRef names a holding by referring to an Address and Asset it belongs to. + */ +export class HoldingRef implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'address', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'asset', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + + /** + * (d) Address in access list, or the sender of the transaction. + */ + public address: Address; + + /** + * (s) Asset ID for asset in access list. + */ + public asset: number; + + /** + * Creates a new `HoldingRef` object. + * @param address - (d) Address in access list, or the sender of the transaction. + * @param asset - (s) Asset ID for asset in access list. + */ + constructor({ + address, + asset, + }: { + address: Address | string; + asset: number | bigint; + }) { + this.address = + typeof address === 'string' ? Address.fromString(address) : address; + this.asset = ensureSafeInteger(asset); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return HoldingRef.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['address', this.address.toString()], + ['asset', this.asset], + ]); + } + + static fromEncodingData(data: unknown): HoldingRef { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded HoldingRef: ${data}`); + } + return new HoldingRef({ + address: data.get('address'), + asset: data.get('asset'), + }); + } +} + +export class IndexerStateProofMessage implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'block-headers-commitment', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'first-attested-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'latest-attested-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'ln-proven-weight', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'voters-commitment', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (b) + */ + public blockHeadersCommitment?: Uint8Array; + + /** + * (f) + */ + public firstAttestedRound?: bigint; + + /** + * (l) + */ + public latestAttestedRound?: bigint; + + /** + * (P) + */ + public lnProvenWeight?: bigint; + + /** + * (v) + */ + public votersCommitment?: Uint8Array; + + /** + * Creates a new `IndexerStateProofMessage` object. + * @param blockHeadersCommitment - (b) + * @param firstAttestedRound - (f) + * @param latestAttestedRound - (l) + * @param lnProvenWeight - (P) + * @param votersCommitment - (v) + */ + constructor({ + blockHeadersCommitment, + firstAttestedRound, + latestAttestedRound, + lnProvenWeight, + votersCommitment, + }: { + blockHeadersCommitment?: string | Uint8Array; + firstAttestedRound?: number | bigint; + latestAttestedRound?: number | bigint; + lnProvenWeight?: number | bigint; + votersCommitment?: string | Uint8Array; + }) { + this.blockHeadersCommitment = + typeof blockHeadersCommitment === 'string' + ? base64ToBytes(blockHeadersCommitment) + : blockHeadersCommitment; + this.firstAttestedRound = + typeof firstAttestedRound === 'undefined' + ? undefined + : ensureBigInt(firstAttestedRound); + this.latestAttestedRound = + typeof latestAttestedRound === 'undefined' + ? undefined + : ensureBigInt(latestAttestedRound); + this.lnProvenWeight = + typeof lnProvenWeight === 'undefined' + ? undefined + : ensureBigInt(lnProvenWeight); + this.votersCommitment = + typeof votersCommitment === 'string' + ? base64ToBytes(votersCommitment) + : votersCommitment; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return IndexerStateProofMessage.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['block-headers-commitment', this.blockHeadersCommitment], + ['first-attested-round', this.firstAttestedRound], + ['latest-attested-round', this.latestAttestedRound], + ['ln-proven-weight', this.lnProvenWeight], + ['voters-commitment', this.votersCommitment], + ]); + } + + static fromEncodingData(data: unknown): IndexerStateProofMessage { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded IndexerStateProofMessage: ${data}`); + } + return new IndexerStateProofMessage({ + blockHeadersCommitment: data.get('block-headers-commitment'), + firstAttestedRound: data.get('first-attested-round'), + latestAttestedRound: data.get('latest-attested-round'), + lnProvenWeight: data.get('ln-proven-weight'), + votersCommitment: data.get('voters-commitment'), + }); + } +} + +/** + * LocalsRef names a local state by referring to an Address and App it belongs to. + */ +export class LocalsRef implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'address', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'app', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + + /** + * (d) Address in access list, or the sender of the transaction. + */ + public address: Address; + + /** + * (p) Application ID for app in access list, or zero if referring to the called + * application. + */ + public app: number; + + /** + * Creates a new `LocalsRef` object. + * @param address - (d) Address in access list, or the sender of the transaction. + * @param app - (p) Application ID for app in access list, or zero if referring to the called + * application. + */ + constructor({ + address, + app, + }: { + address: Address | string; + app: number | bigint; + }) { + this.address = + typeof address === 'string' ? Address.fromString(address) : address; + this.app = ensureSafeInteger(app); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return LocalsRef.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['address', this.address.toString()], + ['app', this.app], + ]); + } + + static fromEncodingData(data: unknown): LocalsRef { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded LocalsRef: ${data}`); + } + return new LocalsRef({ + address: data.get('address'), + app: data.get('app'), + }); + } +} + +export class MerkleArrayProof implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'hash-factory', + valueSchema: new OptionalSchema(HashFactory.encodingSchema), + omitEmpty: true, + }, + { + key: 'path', + valueSchema: new OptionalSchema( + new ArraySchema(new ByteArraySchema()) + ), + omitEmpty: true, + }, + { + key: 'tree-depth', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + public hashFactory?: HashFactory; + + /** + * (pth) + */ + public path?: Uint8Array[]; + + /** + * (td) + */ + public treeDepth?: number; + + /** + * Creates a new `MerkleArrayProof` object. + * @param hashFactory - + * @param path - (pth) + * @param treeDepth - (td) + */ + constructor({ + hashFactory, + path, + treeDepth, + }: { + hashFactory?: HashFactory; + path?: Uint8Array[]; + treeDepth?: number | bigint; + }) { + this.hashFactory = hashFactory; + this.path = path; + this.treeDepth = + typeof treeDepth === 'undefined' + ? undefined + : ensureSafeInteger(treeDepth); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return MerkleArrayProof.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + [ + 'hash-factory', + typeof this.hashFactory !== 'undefined' + ? this.hashFactory.toEncodingData() + : undefined, + ], + ['path', this.path], + ['tree-depth', this.treeDepth], + ]); + } + + static fromEncodingData(data: unknown): MerkleArrayProof { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded MerkleArrayProof: ${data}`); + } + return new MerkleArrayProof({ + hashFactory: + typeof data.get('hash-factory') !== 'undefined' + ? HashFactory.fromEncodingData(data.get('hash-factory')) + : undefined, + path: data.get('path'), + treeDepth: data.get('tree-depth'), + }); + } +} + +/** + * A simplified version of AssetHolding + */ +export class MiniAssetHolding implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'address', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'amount', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'is-frozen', valueSchema: new BooleanSchema(), omitEmpty: true }, + { + key: 'deleted', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'opted-in-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'opted-out-at-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + public address: string; + + public amount: bigint; + + public isFrozen: boolean; + + /** + * Whether or not this asset holding is currently deleted from its account. + */ + public deleted?: boolean; + + /** + * Round during which the account opted into the asset. + */ + public optedInAtRound?: bigint; + + /** + * Round during which the account opted out of the asset. + */ + public optedOutAtRound?: bigint; + + /** + * Creates a new `MiniAssetHolding` object. + * @param address - + * @param amount - + * @param isFrozen - + * @param deleted - Whether or not this asset holding is currently deleted from its account. + * @param optedInAtRound - Round during which the account opted into the asset. + * @param optedOutAtRound - Round during which the account opted out of the asset. + */ + constructor({ + address, + amount, + isFrozen, + deleted, + optedInAtRound, + optedOutAtRound, + }: { + address: string; + amount: number | bigint; + isFrozen: boolean; + deleted?: boolean; + optedInAtRound?: number | bigint; + optedOutAtRound?: number | bigint; + }) { + this.address = address; + this.amount = ensureBigInt(amount); + this.isFrozen = isFrozen; + this.deleted = deleted; + this.optedInAtRound = + typeof optedInAtRound === 'undefined' + ? undefined + : ensureBigInt(optedInAtRound); + this.optedOutAtRound = + typeof optedOutAtRound === 'undefined' + ? undefined + : ensureBigInt(optedOutAtRound); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return MiniAssetHolding.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['address', this.address], + ['amount', this.amount], + ['is-frozen', this.isFrozen], + ['deleted', this.deleted], + ['opted-in-at-round', this.optedInAtRound], + ['opted-out-at-round', this.optedOutAtRound], + ]); + } + + static fromEncodingData(data: unknown): MiniAssetHolding { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded MiniAssetHolding: ${data}`); + } + return new MiniAssetHolding({ + address: data.get('address'), + amount: data.get('amount'), + isFrozen: data.get('is-frozen'), + deleted: data.get('deleted'), + optedInAtRound: data.get('opted-in-at-round'), + optedOutAtRound: data.get('opted-out-at-round'), + }); + } +} + +/** + * Participation account data that needs to be checked/acted on by the network. + */ +export class ParticipationUpdates implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'absent-participation-accounts', + valueSchema: new OptionalSchema(new ArraySchema(new StringSchema())), + omitEmpty: true, + }, + { + key: 'expired-participation-accounts', + valueSchema: new OptionalSchema(new ArraySchema(new StringSchema())), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (partupabs) a list of online accounts that need to be suspended. + */ + public absentParticipationAccounts?: string[]; + + /** + * (partupdrmv) a list of online accounts that needs to be converted to offline + * since their participation key expired. + */ + public expiredParticipationAccounts?: string[]; + + /** + * Creates a new `ParticipationUpdates` object. + * @param absentParticipationAccounts - (partupabs) a list of online accounts that need to be suspended. + * @param expiredParticipationAccounts - (partupdrmv) a list of online accounts that needs to be converted to offline + * since their participation key expired. + */ + constructor({ + absentParticipationAccounts, + expiredParticipationAccounts, + }: { + absentParticipationAccounts?: string[]; + expiredParticipationAccounts?: string[]; + }) { + this.absentParticipationAccounts = absentParticipationAccounts; + this.expiredParticipationAccounts = expiredParticipationAccounts; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ParticipationUpdates.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['absent-participation-accounts', this.absentParticipationAccounts], + ['expired-participation-accounts', this.expiredParticipationAccounts], + ]); + } + + static fromEncodingData(data: unknown): ParticipationUpdates { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ParticipationUpdates: ${data}`); + } + return new ParticipationUpdates({ + absentParticipationAccounts: data.get('absent-participation-accounts'), + expiredParticipationAccounts: data.get('expired-participation-accounts'), + }); + } +} + +/** + * ResourceRef names a single resource. Only one of the fields should be set. + */ +export class ResourceRef implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'address', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'application-id', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'asset-id', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'box', + valueSchema: new OptionalSchema(BoxReference.encodingSchema), + omitEmpty: true, + }, + { + key: 'holding', + valueSchema: new OptionalSchema(HoldingRef.encodingSchema), + omitEmpty: true, + }, + { + key: 'local', + valueSchema: new OptionalSchema(LocalsRef.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (d) Account whose balance record is accessible by the executing ApprovalProgram + * or ClearStateProgram. + */ + public address?: Address; + + /** + * (p) Application id whose GlobalState may be read by the executing + * ApprovalProgram or ClearStateProgram. + */ + public applicationId?: number; + + /** + * (s) Asset whose AssetParams may be read by the executing + * ApprovalProgram or ClearStateProgram. + */ + public assetId?: number; + + /** + * BoxReference names a box by its name and the application ID it belongs to. + */ + public box?: BoxReference; + + /** + * HoldingRef names a holding by referring to an Address and Asset it belongs to. + */ + public holding?: HoldingRef; + + /** + * LocalsRef names a local state by referring to an Address and App it belongs to. + */ + public local?: LocalsRef; + + /** + * Creates a new `ResourceRef` object. + * @param address - (d) Account whose balance record is accessible by the executing ApprovalProgram + * or ClearStateProgram. + * @param applicationId - (p) Application id whose GlobalState may be read by the executing + * ApprovalProgram or ClearStateProgram. + * @param assetId - (s) Asset whose AssetParams may be read by the executing + * ApprovalProgram or ClearStateProgram. + * @param box - BoxReference names a box by its name and the application ID it belongs to. + * @param holding - HoldingRef names a holding by referring to an Address and Asset it belongs to. + * @param local - LocalsRef names a local state by referring to an Address and App it belongs to. + */ + constructor({ + address, + applicationId, + assetId, + box, + holding, + local, + }: { + address?: Address | string; + applicationId?: number | bigint; + assetId?: number | bigint; + box?: BoxReference; + holding?: HoldingRef; + local?: LocalsRef; + }) { + this.address = + typeof address === 'string' ? Address.fromString(address) : address; + this.applicationId = + typeof applicationId === 'undefined' + ? undefined + : ensureSafeInteger(applicationId); + this.assetId = + typeof assetId === 'undefined' ? undefined : ensureSafeInteger(assetId); + this.box = box; + this.holding = holding; + this.local = local; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return ResourceRef.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + [ + 'address', + typeof this.address !== 'undefined' + ? this.address.toString() + : undefined, + ], + ['application-id', this.applicationId], + ['asset-id', this.assetId], + [ + 'box', + typeof this.box !== 'undefined' ? this.box.toEncodingData() : undefined, + ], + [ + 'holding', + typeof this.holding !== 'undefined' + ? this.holding.toEncodingData() + : undefined, + ], + [ + 'local', + typeof this.local !== 'undefined' + ? this.local.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): ResourceRef { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded ResourceRef: ${data}`); + } + return new ResourceRef({ + address: data.get('address'), + applicationId: data.get('application-id'), + assetId: data.get('asset-id'), + box: + typeof data.get('box') !== 'undefined' + ? BoxReference.fromEncodingData(data.get('box')) + : undefined, + holding: + typeof data.get('holding') !== 'undefined' + ? HoldingRef.fromEncodingData(data.get('holding')) + : undefined, + local: + typeof data.get('local') !== 'undefined' + ? LocalsRef.fromEncodingData(data.get('local')) + : undefined, + }); + } +} + +/** + * (sp) represents a state proof. + * Definition: + * crypto/stateproof/structs.go : StateProof + */ +export class StateProofFields implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'part-proofs', + valueSchema: new OptionalSchema(MerkleArrayProof.encodingSchema), + omitEmpty: true, + }, + { + key: 'positions-to-reveal', + valueSchema: new OptionalSchema(new ArraySchema(new Uint64Schema())), + omitEmpty: true, + }, + { + key: 'reveals', + valueSchema: new OptionalSchema( + new ArraySchema(StateProofReveal.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'salt-version', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'sig-commit', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'sig-proofs', + valueSchema: new OptionalSchema(MerkleArrayProof.encodingSchema), + omitEmpty: true, + }, + { + key: 'signed-weight', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (P) + */ + public partProofs?: MerkleArrayProof; + + /** + * (pr) Sequence of reveal positions. + */ + public positionsToReveal?: bigint[]; + + /** + * (r) Note that this is actually stored as a map[uint64] - Reveal in the actual + * msgp + */ + public reveals?: StateProofReveal[]; + + /** + * (v) Salt version of the merkle signature. + */ + public saltVersion?: number; + + /** + * (c) + */ + public sigCommit?: Uint8Array; + + /** + * (S) + */ + public sigProofs?: MerkleArrayProof; + + /** + * (w) + */ + public signedWeight?: bigint; + + /** + * Creates a new `StateProofFields` object. + * @param partProofs - (P) + * @param positionsToReveal - (pr) Sequence of reveal positions. + * @param reveals - (r) Note that this is actually stored as a map[uint64] - Reveal in the actual + * msgp + * @param saltVersion - (v) Salt version of the merkle signature. + * @param sigCommit - (c) + * @param sigProofs - (S) + * @param signedWeight - (w) + */ + constructor({ + partProofs, + positionsToReveal, + reveals, + saltVersion, + sigCommit, + sigProofs, + signedWeight, + }: { + partProofs?: MerkleArrayProof; + positionsToReveal?: (number | bigint)[]; + reveals?: StateProofReveal[]; + saltVersion?: number | bigint; + sigCommit?: string | Uint8Array; + sigProofs?: MerkleArrayProof; + signedWeight?: number | bigint; + }) { + this.partProofs = partProofs; + this.positionsToReveal = + typeof positionsToReveal === 'undefined' + ? undefined + : positionsToReveal.map(ensureBigInt); + this.reveals = reveals; + this.saltVersion = + typeof saltVersion === 'undefined' + ? undefined + : ensureSafeInteger(saltVersion); + this.sigCommit = + typeof sigCommit === 'string' ? base64ToBytes(sigCommit) : sigCommit; + this.sigProofs = sigProofs; + this.signedWeight = + typeof signedWeight === 'undefined' + ? undefined + : ensureBigInt(signedWeight); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofFields.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + [ + 'part-proofs', + typeof this.partProofs !== 'undefined' + ? this.partProofs.toEncodingData() + : undefined, + ], + ['positions-to-reveal', this.positionsToReveal], + [ + 'reveals', + typeof this.reveals !== 'undefined' + ? this.reveals.map((v) => v.toEncodingData()) + : undefined, + ], + ['salt-version', this.saltVersion], + ['sig-commit', this.sigCommit], + [ + 'sig-proofs', + typeof this.sigProofs !== 'undefined' + ? this.sigProofs.toEncodingData() + : undefined, + ], + ['signed-weight', this.signedWeight], + ]); + } + + static fromEncodingData(data: unknown): StateProofFields { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofFields: ${data}`); + } + return new StateProofFields({ + partProofs: + typeof data.get('part-proofs') !== 'undefined' + ? MerkleArrayProof.fromEncodingData(data.get('part-proofs')) + : undefined, + positionsToReveal: data.get('positions-to-reveal'), + reveals: + typeof data.get('reveals') !== 'undefined' + ? data + .get('reveals') + .map((v: unknown) => StateProofReveal.fromEncodingData(v)) + : undefined, + saltVersion: data.get('salt-version'), + sigCommit: data.get('sig-commit'), + sigProofs: + typeof data.get('sig-proofs') !== 'undefined' + ? MerkleArrayProof.fromEncodingData(data.get('sig-proofs')) + : undefined, + signedWeight: data.get('signed-weight'), + }); + } +} + +export class StateProofParticipant implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'verifier', + valueSchema: new OptionalSchema(StateProofVerifier.encodingSchema), + omitEmpty: true, + }, + { + key: 'weight', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (p) + */ + public verifier?: StateProofVerifier; + + /** + * (w) + */ + public weight?: bigint; + + /** + * Creates a new `StateProofParticipant` object. + * @param verifier - (p) + * @param weight - (w) + */ + constructor({ + verifier, + weight, + }: { + verifier?: StateProofVerifier; + weight?: number | bigint; + }) { + this.verifier = verifier; + this.weight = + typeof weight === 'undefined' ? undefined : ensureBigInt(weight); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofParticipant.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + [ + 'verifier', + typeof this.verifier !== 'undefined' + ? this.verifier.toEncodingData() + : undefined, + ], + ['weight', this.weight], + ]); + } + + static fromEncodingData(data: unknown): StateProofParticipant { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofParticipant: ${data}`); + } + return new StateProofParticipant({ + verifier: + typeof data.get('verifier') !== 'undefined' + ? StateProofVerifier.fromEncodingData(data.get('verifier')) + : undefined, + weight: data.get('weight'), + }); + } +} + +export class StateProofReveal implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'participant', + valueSchema: new OptionalSchema(StateProofParticipant.encodingSchema), + omitEmpty: true, + }, + { + key: 'position', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'sig-slot', + valueSchema: new OptionalSchema(StateProofSigSlot.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (p) + */ + public participant?: StateProofParticipant; + + /** + * The position in the signature and participants arrays corresponding to this + * entry. + */ + public position?: bigint; + + /** + * (s) + */ + public sigSlot?: StateProofSigSlot; + + /** + * Creates a new `StateProofReveal` object. + * @param participant - (p) + * @param position - The position in the signature and participants arrays corresponding to this + * entry. + * @param sigSlot - (s) + */ + constructor({ + participant, + position, + sigSlot, + }: { + participant?: StateProofParticipant; + position?: number | bigint; + sigSlot?: StateProofSigSlot; + }) { + this.participant = participant; + this.position = + typeof position === 'undefined' ? undefined : ensureBigInt(position); + this.sigSlot = sigSlot; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofReveal.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + [ + 'participant', + typeof this.participant !== 'undefined' + ? this.participant.toEncodingData() + : undefined, + ], + ['position', this.position], + [ + 'sig-slot', + typeof this.sigSlot !== 'undefined' + ? this.sigSlot.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): StateProofReveal { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofReveal: ${data}`); + } + return new StateProofReveal({ + participant: + typeof data.get('participant') !== 'undefined' + ? StateProofParticipant.fromEncodingData(data.get('participant')) + : undefined, + position: data.get('position'), + sigSlot: + typeof data.get('sig-slot') !== 'undefined' + ? StateProofSigSlot.fromEncodingData(data.get('sig-slot')) + : undefined, + }); + } +} + +export class StateProofSigSlot implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'lower-sig-weight', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'signature', + valueSchema: new OptionalSchema(StateProofSignature.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (l) The total weight of signatures in the lower-numbered slots. + */ + public lowerSigWeight?: bigint; + + public signature?: StateProofSignature; + + /** + * Creates a new `StateProofSigSlot` object. + * @param lowerSigWeight - (l) The total weight of signatures in the lower-numbered slots. + * @param signature - + */ + constructor({ + lowerSigWeight, + signature, + }: { + lowerSigWeight?: number | bigint; + signature?: StateProofSignature; + }) { + this.lowerSigWeight = + typeof lowerSigWeight === 'undefined' + ? undefined + : ensureBigInt(lowerSigWeight); + this.signature = signature; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofSigSlot.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['lower-sig-weight', this.lowerSigWeight], + [ + 'signature', + typeof this.signature !== 'undefined' + ? this.signature.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): StateProofSigSlot { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofSigSlot: ${data}`); + } + return new StateProofSigSlot({ + lowerSigWeight: data.get('lower-sig-weight'), + signature: + typeof data.get('signature') !== 'undefined' + ? StateProofSignature.fromEncodingData(data.get('signature')) + : undefined, + }); + } +} + +export class StateProofSignature implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'falcon-signature', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'merkle-array-index', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'proof', + valueSchema: new OptionalSchema(MerkleArrayProof.encodingSchema), + omitEmpty: true, + }, + { + key: 'verifying-key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + public falconSignature?: Uint8Array; + + public merkleArrayIndex?: number; + + public proof?: MerkleArrayProof; + + /** + * (vkey) + */ + public verifyingKey?: Uint8Array; + + /** + * Creates a new `StateProofSignature` object. + * @param falconSignature - + * @param merkleArrayIndex - + * @param proof - + * @param verifyingKey - (vkey) + */ + constructor({ + falconSignature, + merkleArrayIndex, + proof, + verifyingKey, + }: { + falconSignature?: string | Uint8Array; + merkleArrayIndex?: number | bigint; + proof?: MerkleArrayProof; + verifyingKey?: string | Uint8Array; + }) { + this.falconSignature = + typeof falconSignature === 'string' + ? base64ToBytes(falconSignature) + : falconSignature; + this.merkleArrayIndex = + typeof merkleArrayIndex === 'undefined' + ? undefined + : ensureSafeInteger(merkleArrayIndex); + this.proof = proof; + this.verifyingKey = + typeof verifyingKey === 'string' + ? base64ToBytes(verifyingKey) + : verifyingKey; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofSignature.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['falcon-signature', this.falconSignature], + ['merkle-array-index', this.merkleArrayIndex], + [ + 'proof', + typeof this.proof !== 'undefined' + ? this.proof.toEncodingData() + : undefined, + ], + ['verifying-key', this.verifyingKey], + ]); + } + + static fromEncodingData(data: unknown): StateProofSignature { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofSignature: ${data}`); + } + return new StateProofSignature({ + falconSignature: data.get('falcon-signature'), + merkleArrayIndex: data.get('merkle-array-index'), + proof: + typeof data.get('proof') !== 'undefined' + ? MerkleArrayProof.fromEncodingData(data.get('proof')) + : undefined, + verifyingKey: data.get('verifying-key'), + }); + } +} + +export class StateProofTracking implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'next-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'online-total-weight', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'type', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'voters-commitment', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (n) Next round for which we will accept a state proof transaction. + */ + public nextRound?: bigint; + + /** + * (t) The total number of microalgos held by the online accounts during the + * StateProof round. + */ + public onlineTotalWeight?: bigint; + + /** + * State Proof Type. Note the raw object uses map with this as key. + */ + public type?: number; + + /** + * (v) Root of a vector commitment containing online accounts that will help sign + * the proof. + */ + public votersCommitment?: Uint8Array; + + /** + * Creates a new `StateProofTracking` object. + * @param nextRound - (n) Next round for which we will accept a state proof transaction. + * @param onlineTotalWeight - (t) The total number of microalgos held by the online accounts during the + * StateProof round. + * @param type - State Proof Type. Note the raw object uses map with this as key. + * @param votersCommitment - (v) Root of a vector commitment containing online accounts that will help sign + * the proof. + */ + constructor({ + nextRound, + onlineTotalWeight, + type, + votersCommitment, + }: { + nextRound?: number | bigint; + onlineTotalWeight?: number | bigint; + type?: number | bigint; + votersCommitment?: string | Uint8Array; + }) { + this.nextRound = + typeof nextRound === 'undefined' ? undefined : ensureBigInt(nextRound); + this.onlineTotalWeight = + typeof onlineTotalWeight === 'undefined' + ? undefined + : ensureBigInt(onlineTotalWeight); + this.type = + typeof type === 'undefined' ? undefined : ensureSafeInteger(type); + this.votersCommitment = + typeof votersCommitment === 'string' + ? base64ToBytes(votersCommitment) + : votersCommitment; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofTracking.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['next-round', this.nextRound], + ['online-total-weight', this.onlineTotalWeight], + ['type', this.type], + ['voters-commitment', this.votersCommitment], + ]); + } + + static fromEncodingData(data: unknown): StateProofTracking { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofTracking: ${data}`); + } + return new StateProofTracking({ + nextRound: data.get('next-round'), + onlineTotalWeight: data.get('online-total-weight'), + type: data.get('type'), + votersCommitment: data.get('voters-commitment'), + }); + } +} + +export class StateProofVerifier implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'commitment', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'key-lifetime', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (cmt) Represents the root of the vector commitment tree. + */ + public commitment?: Uint8Array; + + /** + * (lf) Key lifetime. + */ + public keyLifetime?: bigint; + + /** + * Creates a new `StateProofVerifier` object. + * @param commitment - (cmt) Represents the root of the vector commitment tree. + * @param keyLifetime - (lf) Key lifetime. + */ + constructor({ + commitment, + keyLifetime, + }: { + commitment?: string | Uint8Array; + keyLifetime?: number | bigint; + }) { + this.commitment = + typeof commitment === 'string' ? base64ToBytes(commitment) : commitment; + this.keyLifetime = + typeof keyLifetime === 'undefined' + ? undefined + : ensureBigInt(keyLifetime); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateProofVerifier.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['commitment', this.commitment], + ['key-lifetime', this.keyLifetime], + ]); + } + + static fromEncodingData(data: unknown): StateProofVerifier { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofVerifier: ${data}`); + } + return new StateProofVerifier({ + commitment: data.get('commitment'), + keyLifetime: data.get('key-lifetime'), + }); + } +} + +/** + * Represents a (apls) local-state or (apgs) global-state schema. These schemas + * determine how much storage may be used in a local-state or global-state for an + * application. The more space used, the larger minimum balance must be maintained + * in the account holding the data. + */ +export class StateSchema implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'num-byte-slice', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { key: 'num-uint', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + + /** + * Maximum number of TEAL byte slices that may be stored in the key/value store. + */ + public numByteSlice: number; + + /** + * Maximum number of TEAL uints that may be stored in the key/value store. + */ + public numUint: number; + + /** + * Creates a new `StateSchema` object. + * @param numByteSlice - Maximum number of TEAL byte slices that may be stored in the key/value store. + * @param numUint - Maximum number of TEAL uints that may be stored in the key/value store. + */ + constructor({ + numByteSlice, + numUint, + }: { + numByteSlice: number | bigint; + numUint: number | bigint; + }) { + this.numByteSlice = ensureSafeInteger(numByteSlice); + this.numUint = ensureSafeInteger(numUint); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return StateSchema.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['num-byte-slice', this.numByteSlice], + ['num-uint', this.numUint], + ]); + } + + static fromEncodingData(data: unknown): StateSchema { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateSchema: ${data}`); + } + return new StateSchema({ + numByteSlice: data.get('num-byte-slice'), + numUint: data.get('num-uint'), + }); + } +} + +/** + * Represents a key-value pair in an application store. + */ +export class TealKeyValue implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'key', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'value', valueSchema: TealValue.encodingSchema, omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + + public key: Uint8Array; + + /** + * Represents a TEAL value. + */ + public value: TealValue; + + /** + * Creates a new `TealKeyValue` object. + * @param key - + * @param value - Represents a TEAL value. + */ + constructor({ key, value }: { key: string | Uint8Array; value: TealValue }) { + this.key = typeof key === 'string' ? base64ToBytes(key) : key; + this.value = value; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TealKeyValue.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['key', this.key], + ['value', this.value.toEncodingData()], + ]); + } + + static fromEncodingData(data: unknown): TealKeyValue { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TealKeyValue: ${data}`); + } + return new TealKeyValue({ + key: data.get('key'), + value: TealValue.fromEncodingData(data.get('value') ?? new Map()), + }); + } +} + +/** + * Represents a TEAL value. + */ +export class TealValue implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'bytes', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { key: 'type', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'uint', valueSchema: new Uint64Schema(), omitEmpty: true } + ); + } + return this.encodingSchemaValue; + } + + /** + * bytes value. + */ + public bytes: Uint8Array; + + /** + * type of the value. Value `1` refers to **bytes**, value `2` refers to **uint** + */ + public type: number; + + /** + * uint value. + */ + public uint: bigint; + + /** + * Creates a new `TealValue` object. + * @param bytes - bytes value. + * @param type - type of the value. Value `1` refers to **bytes**, value `2` refers to **uint** + * @param uint - uint value. + */ + constructor({ + bytes, + type, + uint, + }: { + bytes: string | Uint8Array; + type: number | bigint; + uint: number | bigint; + }) { + this.bytes = typeof bytes === 'string' ? base64ToBytes(bytes) : bytes; + this.type = ensureSafeInteger(type); + this.uint = ensureBigInt(uint); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TealValue.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['bytes', this.bytes], + ['type', this.type], + ['uint', this.uint], + ]); + } + + static fromEncodingData(data: unknown): TealValue { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TealValue: ${data}`); + } + return new TealValue({ + bytes: data.get('bytes'), + type: data.get('type'), + uint: data.get('uint'), + }); + } +} + +/** + * Contains all fields common to all transactions and serves as an envelope to all + * transactions type. Represents both regular and inner transactions. + * Definition: + * data/transactions/signedtxn.go : SignedTxn + * data/transactions/transaction.go : Transaction + */ +export class Transaction implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'fee', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'first-valid', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { key: 'last-valid', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'sender', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'application-transaction', + valueSchema: new OptionalSchema( + TransactionApplication.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'asset-config-transaction', + valueSchema: new OptionalSchema( + TransactionAssetConfig.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'asset-freeze-transaction', + valueSchema: new OptionalSchema( + TransactionAssetFreeze.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'asset-transfer-transaction', + valueSchema: new OptionalSchema( + TransactionAssetTransfer.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'auth-addr', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'close-rewards', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'closing-amount', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'confirmed-round', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'created-application-index', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'created-asset-index', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'genesis-hash', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'genesis-id', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'global-state-delta', + valueSchema: new OptionalSchema( + new ArraySchema(EvalDeltaKeyValue.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'group', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'heartbeat-transaction', + valueSchema: new OptionalSchema(TransactionHeartbeat.encodingSchema), + omitEmpty: true, + }, + { + key: 'id', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'inner-txns', + valueSchema: new OptionalSchema( + new ArraySchema(Transaction.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'intra-round-offset', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'keyreg-transaction', + valueSchema: new OptionalSchema(TransactionKeyreg.encodingSchema), + omitEmpty: true, + }, + { + key: 'lease', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'local-state-delta', + valueSchema: new OptionalSchema( + new ArraySchema(AccountStateDelta.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'logs', + valueSchema: new OptionalSchema( + new ArraySchema(new ByteArraySchema()) + ), + omitEmpty: true, + }, + { + key: 'note', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'payment-transaction', + valueSchema: new OptionalSchema(TransactionPayment.encodingSchema), + omitEmpty: true, + }, + { + key: 'receiver-rewards', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'rekey-to', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'round-time', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'sender-rewards', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'signature', + valueSchema: new OptionalSchema(TransactionSignature.encodingSchema), + omitEmpty: true, + }, + { + key: 'state-proof-transaction', + valueSchema: new OptionalSchema(TransactionStateProof.encodingSchema), + omitEmpty: true, + }, + { + key: 'tx-type', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (fee) Transaction fee. + */ + public fee: bigint; + + /** + * (fv) First valid round for this transaction. + */ + public firstValid: bigint; + + /** + * (lv) Last valid round for this transaction. + */ + public lastValid: bigint; + + /** + * (snd) Sender's address. + */ + public sender: string; + + /** + * Fields for application transactions. + * Definition: + * data/transactions/application.go : ApplicationCallTxnFields + */ + public applicationTransaction?: TransactionApplication; + + /** + * Fields for asset allocation, re-configuration, and destruction. + * A zero value for asset-id indicates asset creation. + * A zero value for the params indicates asset destruction. + * Definition: + * data/transactions/asset.go : AssetConfigTxnFields + */ + public assetConfigTransaction?: TransactionAssetConfig; + + /** + * Fields for an asset freeze transaction. + * Definition: + * data/transactions/asset.go : AssetFreezeTxnFields + */ + public assetFreezeTransaction?: TransactionAssetFreeze; + + /** + * Fields for an asset transfer transaction. + * Definition: + * data/transactions/asset.go : AssetTransferTxnFields + */ + public assetTransferTransaction?: TransactionAssetTransfer; + + /** + * (sgnr) this is included with signed transactions when the signing address does + * not equal the sender. The backend can use this to ensure that auth addr is equal + * to the accounts auth addr. + */ + public authAddr?: Address; + + /** + * (rc) rewards applied to close-remainder-to account. + */ + public closeRewards?: bigint; + + /** + * (ca) closing amount for transaction. + */ + public closingAmount?: bigint; + + /** + * Round when the transaction was confirmed. + */ + public confirmedRound?: bigint; + + /** + * Specifies an application index (ID) if an application was created with this + * transaction. + */ + public createdApplicationIndex?: bigint; + + /** + * Specifies an asset index (ID) if an asset was created with this transaction. + */ + public createdAssetIndex?: bigint; + + /** + * (gh) Hash of genesis block. + */ + public genesisHash?: Uint8Array; + + /** + * (gen) genesis block ID. + */ + public genesisId?: string; + + /** + * (gd) Global state key/value changes for the application being executed by this + * transaction. + */ + public globalStateDelta?: EvalDeltaKeyValue[]; + + /** + * (grp) Base64 encoded byte array of a sha512/256 digest. When present indicates + * that this transaction is part of a transaction group and the value is the + * sha512/256 hash of the transactions in that group. + */ + public group?: Uint8Array; + + /** + * Fields for a heartbeat transaction. + * Definition: + * data/transactions/heartbeat.go : HeartbeatTxnFields + */ + public heartbeatTransaction?: TransactionHeartbeat; + + /** + * Transaction ID + */ + public id?: string; + + /** + * Inner transactions produced by application execution. + */ + public innerTxns?: Transaction[]; + + /** + * Offset into the round where this transaction was confirmed. + */ + public intraRoundOffset?: number; + + /** + * Fields for a keyreg transaction. + * Definition: + * data/transactions/keyreg.go : KeyregTxnFields + */ + public keyregTransaction?: TransactionKeyreg; + + /** + * (lx) Base64 encoded 32-byte array. Lease enforces mutual exclusion of + * transactions. If this field is nonzero, then once the transaction is confirmed, + * it acquires the lease identified by the (Sender, Lease) pair of the transaction + * until the LastValid round passes. While this transaction possesses the lease, no + * other transaction specifying this lease can be confirmed. + */ + public lease?: Uint8Array; + + /** + * (ld) Local state key/value changes for the application being executed by this + * transaction. + */ + public localStateDelta?: AccountStateDelta[]; + + /** + * (lg) Logs for the application being executed by this transaction. + */ + public logs?: Uint8Array[]; + + /** + * (note) Free form data. + */ + public note?: Uint8Array; + + /** + * Fields for a payment transaction. + * Definition: + * data/transactions/payment.go : PaymentTxnFields + */ + public paymentTransaction?: TransactionPayment; + + /** + * (rr) rewards applied to receiver account. + */ + public receiverRewards?: bigint; + + /** + * (rekey) when included in a valid transaction, the accounts auth addr will be + * updated with this value and future signatures must be signed with the key + * represented by this address. + */ + public rekeyTo?: Address; + + /** + * Time when the block this transaction is in was confirmed. + */ + public roundTime?: number; + + /** + * (rs) rewards applied to sender account. + */ + public senderRewards?: bigint; + + /** + * Validation signature associated with some data. Only one of the signatures + * should be provided. + */ + public signature?: TransactionSignature; + + /** + * Fields for a state proof transaction. + * Definition: + * data/transactions/stateproof.go : StateProofTxnFields + */ + public stateProofTransaction?: TransactionStateProof; + + /** + * (type) Indicates what type of transaction this is. Different types have + * different fields. + * Valid types, and where their fields are stored: + * * (pay) payment-transaction + * * (keyreg) keyreg-transaction + * * (acfg) asset-config-transaction + * * (axfer) asset-transfer-transaction + * * (afrz) asset-freeze-transaction + * * (appl) application-transaction + * * (stpf) state-proof-transaction + * * (hb) heartbeat-transaction + */ + public txType?: string; + + /** + * Creates a new `Transaction` object. + * @param fee - (fee) Transaction fee. + * @param firstValid - (fv) First valid round for this transaction. + * @param lastValid - (lv) Last valid round for this transaction. + * @param sender - (snd) Sender's address. + * @param applicationTransaction - Fields for application transactions. + * Definition: + * data/transactions/application.go : ApplicationCallTxnFields + * @param assetConfigTransaction - Fields for asset allocation, re-configuration, and destruction. + * A zero value for asset-id indicates asset creation. + * A zero value for the params indicates asset destruction. + * Definition: + * data/transactions/asset.go : AssetConfigTxnFields + * @param assetFreezeTransaction - Fields for an asset freeze transaction. + * Definition: + * data/transactions/asset.go : AssetFreezeTxnFields + * @param assetTransferTransaction - Fields for an asset transfer transaction. + * Definition: + * data/transactions/asset.go : AssetTransferTxnFields + * @param authAddr - (sgnr) this is included with signed transactions when the signing address does + * not equal the sender. The backend can use this to ensure that auth addr is equal + * to the accounts auth addr. + * @param closeRewards - (rc) rewards applied to close-remainder-to account. + * @param closingAmount - (ca) closing amount for transaction. + * @param confirmedRound - Round when the transaction was confirmed. + * @param createdApplicationIndex - Specifies an application index (ID) if an application was created with this + * transaction. + * @param createdAssetIndex - Specifies an asset index (ID) if an asset was created with this transaction. + * @param genesisHash - (gh) Hash of genesis block. + * @param genesisId - (gen) genesis block ID. + * @param globalStateDelta - (gd) Global state key/value changes for the application being executed by this + * transaction. + * @param group - (grp) Base64 encoded byte array of a sha512/256 digest. When present indicates + * that this transaction is part of a transaction group and the value is the + * sha512/256 hash of the transactions in that group. + * @param heartbeatTransaction - Fields for a heartbeat transaction. + * Definition: + * data/transactions/heartbeat.go : HeartbeatTxnFields + * @param id - Transaction ID + * @param innerTxns - Inner transactions produced by application execution. + * @param intraRoundOffset - Offset into the round where this transaction was confirmed. + * @param keyregTransaction - Fields for a keyreg transaction. + * Definition: + * data/transactions/keyreg.go : KeyregTxnFields + * @param lease - (lx) Base64 encoded 32-byte array. Lease enforces mutual exclusion of + * transactions. If this field is nonzero, then once the transaction is confirmed, + * it acquires the lease identified by the (Sender, Lease) pair of the transaction + * until the LastValid round passes. While this transaction possesses the lease, no + * other transaction specifying this lease can be confirmed. + * @param localStateDelta - (ld) Local state key/value changes for the application being executed by this + * transaction. + * @param logs - (lg) Logs for the application being executed by this transaction. + * @param note - (note) Free form data. + * @param paymentTransaction - Fields for a payment transaction. + * Definition: + * data/transactions/payment.go : PaymentTxnFields + * @param receiverRewards - (rr) rewards applied to receiver account. + * @param rekeyTo - (rekey) when included in a valid transaction, the accounts auth addr will be + * updated with this value and future signatures must be signed with the key + * represented by this address. + * @param roundTime - Time when the block this transaction is in was confirmed. + * @param senderRewards - (rs) rewards applied to sender account. + * @param signature - Validation signature associated with some data. Only one of the signatures + * should be provided. + * @param stateProofTransaction - Fields for a state proof transaction. + * Definition: + * data/transactions/stateproof.go : StateProofTxnFields + * @param txType - (type) Indicates what type of transaction this is. Different types have + * different fields. + * Valid types, and where their fields are stored: + * * (pay) payment-transaction + * * (keyreg) keyreg-transaction + * * (acfg) asset-config-transaction + * * (axfer) asset-transfer-transaction + * * (afrz) asset-freeze-transaction + * * (appl) application-transaction + * * (stpf) state-proof-transaction + * * (hb) heartbeat-transaction + */ + constructor({ + fee, + firstValid, + lastValid, + sender, + applicationTransaction, + assetConfigTransaction, + assetFreezeTransaction, + assetTransferTransaction, + authAddr, + closeRewards, + closingAmount, + confirmedRound, + createdApplicationIndex, + createdAssetIndex, + genesisHash, + genesisId, + globalStateDelta, + group, + heartbeatTransaction, + id, + innerTxns, + intraRoundOffset, + keyregTransaction, + lease, + localStateDelta, + logs, + note, + paymentTransaction, + receiverRewards, + rekeyTo, + roundTime, + senderRewards, + signature, + stateProofTransaction, + txType, + }: { + fee: number | bigint; + firstValid: number | bigint; + lastValid: number | bigint; + sender: string; + applicationTransaction?: TransactionApplication; + assetConfigTransaction?: TransactionAssetConfig; + assetFreezeTransaction?: TransactionAssetFreeze; + assetTransferTransaction?: TransactionAssetTransfer; + authAddr?: Address | string; + closeRewards?: number | bigint; + closingAmount?: number | bigint; + confirmedRound?: number | bigint; + createdApplicationIndex?: number | bigint; + createdAssetIndex?: number | bigint; + genesisHash?: string | Uint8Array; + genesisId?: string; + globalStateDelta?: EvalDeltaKeyValue[]; + group?: string | Uint8Array; + heartbeatTransaction?: TransactionHeartbeat; + id?: string; + innerTxns?: Transaction[]; + intraRoundOffset?: number | bigint; + keyregTransaction?: TransactionKeyreg; + lease?: string | Uint8Array; + localStateDelta?: AccountStateDelta[]; + logs?: Uint8Array[]; + note?: string | Uint8Array; + paymentTransaction?: TransactionPayment; + receiverRewards?: number | bigint; + rekeyTo?: Address | string; + roundTime?: number | bigint; + senderRewards?: number | bigint; + signature?: TransactionSignature; + stateProofTransaction?: TransactionStateProof; + txType?: string; + }) { + this.fee = ensureBigInt(fee); + this.firstValid = ensureBigInt(firstValid); + this.lastValid = ensureBigInt(lastValid); + this.sender = sender; + this.applicationTransaction = applicationTransaction; + this.assetConfigTransaction = assetConfigTransaction; + this.assetFreezeTransaction = assetFreezeTransaction; + this.assetTransferTransaction = assetTransferTransaction; + this.authAddr = + typeof authAddr === 'string' ? Address.fromString(authAddr) : authAddr; + this.closeRewards = + typeof closeRewards === 'undefined' + ? undefined + : ensureBigInt(closeRewards); + this.closingAmount = + typeof closingAmount === 'undefined' + ? undefined + : ensureBigInt(closingAmount); + this.confirmedRound = + typeof confirmedRound === 'undefined' + ? undefined + : ensureBigInt(confirmedRound); + this.createdApplicationIndex = + typeof createdApplicationIndex === 'undefined' + ? undefined + : ensureBigInt(createdApplicationIndex); + this.createdAssetIndex = + typeof createdAssetIndex === 'undefined' + ? undefined + : ensureBigInt(createdAssetIndex); + this.genesisHash = + typeof genesisHash === 'string' + ? base64ToBytes(genesisHash) + : genesisHash; + this.genesisId = genesisId; + this.globalStateDelta = globalStateDelta; + this.group = typeof group === 'string' ? base64ToBytes(group) : group; + this.heartbeatTransaction = heartbeatTransaction; + this.id = id; + this.innerTxns = innerTxns; + this.intraRoundOffset = + typeof intraRoundOffset === 'undefined' + ? undefined + : ensureSafeInteger(intraRoundOffset); + this.keyregTransaction = keyregTransaction; + this.lease = typeof lease === 'string' ? base64ToBytes(lease) : lease; + this.localStateDelta = localStateDelta; + this.logs = logs; + this.note = typeof note === 'string' ? base64ToBytes(note) : note; + this.paymentTransaction = paymentTransaction; + this.receiverRewards = + typeof receiverRewards === 'undefined' + ? undefined + : ensureBigInt(receiverRewards); + this.rekeyTo = + typeof rekeyTo === 'string' ? Address.fromString(rekeyTo) : rekeyTo; + this.roundTime = + typeof roundTime === 'undefined' + ? undefined + : ensureSafeInteger(roundTime); + this.senderRewards = + typeof senderRewards === 'undefined' + ? undefined + : ensureBigInt(senderRewards); + this.signature = signature; + this.stateProofTransaction = stateProofTransaction; + this.txType = txType; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return Transaction.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['fee', this.fee], + ['first-valid', this.firstValid], + ['last-valid', this.lastValid], + ['sender', this.sender], + [ + 'application-transaction', + typeof this.applicationTransaction !== 'undefined' + ? this.applicationTransaction.toEncodingData() + : undefined, + ], + [ + 'asset-config-transaction', + typeof this.assetConfigTransaction !== 'undefined' + ? this.assetConfigTransaction.toEncodingData() + : undefined, + ], + [ + 'asset-freeze-transaction', + typeof this.assetFreezeTransaction !== 'undefined' + ? this.assetFreezeTransaction.toEncodingData() + : undefined, + ], + [ + 'asset-transfer-transaction', + typeof this.assetTransferTransaction !== 'undefined' + ? this.assetTransferTransaction.toEncodingData() + : undefined, + ], + [ + 'auth-addr', + typeof this.authAddr !== 'undefined' + ? this.authAddr.toString() + : undefined, + ], + ['close-rewards', this.closeRewards], + ['closing-amount', this.closingAmount], + ['confirmed-round', this.confirmedRound], + ['created-application-index', this.createdApplicationIndex], + ['created-asset-index', this.createdAssetIndex], + ['genesis-hash', this.genesisHash], + ['genesis-id', this.genesisId], + [ + 'global-state-delta', + typeof this.globalStateDelta !== 'undefined' + ? this.globalStateDelta.map((v) => v.toEncodingData()) + : undefined, + ], + ['group', this.group], + [ + 'heartbeat-transaction', + typeof this.heartbeatTransaction !== 'undefined' + ? this.heartbeatTransaction.toEncodingData() + : undefined, + ], + ['id', this.id], + [ + 'inner-txns', + typeof this.innerTxns !== 'undefined' + ? this.innerTxns.map((v) => v.toEncodingData()) + : undefined, + ], + ['intra-round-offset', this.intraRoundOffset], + [ + 'keyreg-transaction', + typeof this.keyregTransaction !== 'undefined' + ? this.keyregTransaction.toEncodingData() + : undefined, + ], + ['lease', this.lease], + [ + 'local-state-delta', + typeof this.localStateDelta !== 'undefined' + ? this.localStateDelta.map((v) => v.toEncodingData()) + : undefined, + ], + ['logs', this.logs], + ['note', this.note], + [ + 'payment-transaction', + typeof this.paymentTransaction !== 'undefined' + ? this.paymentTransaction.toEncodingData() + : undefined, + ], + ['receiver-rewards', this.receiverRewards], + [ + 'rekey-to', + typeof this.rekeyTo !== 'undefined' + ? this.rekeyTo.toString() + : undefined, + ], + ['round-time', this.roundTime], + ['sender-rewards', this.senderRewards], + [ + 'signature', + typeof this.signature !== 'undefined' + ? this.signature.toEncodingData() + : undefined, + ], + [ + 'state-proof-transaction', + typeof this.stateProofTransaction !== 'undefined' + ? this.stateProofTransaction.toEncodingData() + : undefined, + ], + ['tx-type', this.txType], + ]); + } + + static fromEncodingData(data: unknown): Transaction { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Transaction: ${data}`); + } + return new Transaction({ + fee: data.get('fee'), + firstValid: data.get('first-valid'), + lastValid: data.get('last-valid'), + sender: data.get('sender'), + applicationTransaction: + typeof data.get('application-transaction') !== 'undefined' + ? TransactionApplication.fromEncodingData( + data.get('application-transaction') + ) + : undefined, + assetConfigTransaction: + typeof data.get('asset-config-transaction') !== 'undefined' + ? TransactionAssetConfig.fromEncodingData( + data.get('asset-config-transaction') + ) + : undefined, + assetFreezeTransaction: + typeof data.get('asset-freeze-transaction') !== 'undefined' + ? TransactionAssetFreeze.fromEncodingData( + data.get('asset-freeze-transaction') + ) + : undefined, + assetTransferTransaction: + typeof data.get('asset-transfer-transaction') !== 'undefined' + ? TransactionAssetTransfer.fromEncodingData( + data.get('asset-transfer-transaction') + ) + : undefined, + authAddr: data.get('auth-addr'), + closeRewards: data.get('close-rewards'), + closingAmount: data.get('closing-amount'), + confirmedRound: data.get('confirmed-round'), + createdApplicationIndex: data.get('created-application-index'), + createdAssetIndex: data.get('created-asset-index'), + genesisHash: data.get('genesis-hash'), + genesisId: data.get('genesis-id'), + globalStateDelta: + typeof data.get('global-state-delta') !== 'undefined' + ? data + .get('global-state-delta') + .map((v: unknown) => EvalDeltaKeyValue.fromEncodingData(v)) + : undefined, + group: data.get('group'), + heartbeatTransaction: + typeof data.get('heartbeat-transaction') !== 'undefined' + ? TransactionHeartbeat.fromEncodingData( + data.get('heartbeat-transaction') + ) + : undefined, + id: data.get('id'), + innerTxns: + typeof data.get('inner-txns') !== 'undefined' + ? data + .get('inner-txns') + .map((v: unknown) => Transaction.fromEncodingData(v)) + : undefined, + intraRoundOffset: data.get('intra-round-offset'), + keyregTransaction: + typeof data.get('keyreg-transaction') !== 'undefined' + ? TransactionKeyreg.fromEncodingData(data.get('keyreg-transaction')) + : undefined, + lease: data.get('lease'), + localStateDelta: + typeof data.get('local-state-delta') !== 'undefined' + ? data + .get('local-state-delta') + .map((v: unknown) => AccountStateDelta.fromEncodingData(v)) + : undefined, + logs: data.get('logs'), + note: data.get('note'), + paymentTransaction: + typeof data.get('payment-transaction') !== 'undefined' + ? TransactionPayment.fromEncodingData(data.get('payment-transaction')) + : undefined, + receiverRewards: data.get('receiver-rewards'), + rekeyTo: data.get('rekey-to'), + roundTime: data.get('round-time'), + senderRewards: data.get('sender-rewards'), + signature: + typeof data.get('signature') !== 'undefined' + ? TransactionSignature.fromEncodingData(data.get('signature')) + : undefined, + stateProofTransaction: + typeof data.get('state-proof-transaction') !== 'undefined' + ? TransactionStateProof.fromEncodingData( + data.get('state-proof-transaction') + ) + : undefined, + txType: data.get('tx-type'), + }); + } +} + +/** + * Fields for application transactions. + * Definition: + * data/transactions/application.go : ApplicationCallTxnFields + */ +export class TransactionApplication implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'application-id', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'access', + valueSchema: new OptionalSchema( + new ArraySchema(ResourceRef.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'accounts', + valueSchema: new OptionalSchema(new ArraySchema(new StringSchema())), + omitEmpty: true, + }, + { + key: 'application-args', + valueSchema: new OptionalSchema( + new ArraySchema(new ByteArraySchema()) + ), + omitEmpty: true, + }, + { + key: 'approval-program', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'box-references', + valueSchema: new OptionalSchema( + new ArraySchema(BoxReference.encodingSchema) + ), + omitEmpty: true, + }, + { + key: 'clear-state-program', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'extra-program-pages', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'foreign-apps', + valueSchema: new OptionalSchema(new ArraySchema(new Uint64Schema())), + omitEmpty: true, + }, + { + key: 'foreign-assets', + valueSchema: new OptionalSchema(new ArraySchema(new Uint64Schema())), + omitEmpty: true, + }, + { + key: 'global-state-schema', + valueSchema: new OptionalSchema(StateSchema.encodingSchema), + omitEmpty: true, + }, + { + key: 'local-state-schema', + valueSchema: new OptionalSchema(StateSchema.encodingSchema), + omitEmpty: true, + }, + { + key: 'on-completion', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'reject-version', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (apid) ID of the application being configured or empty if creating. + */ + public applicationId: bigint; + + /** + * (al) Access unifies `accounts`, `foreign-apps`, `foreign-assets`, and + * `box-references` under a single list. If access is non-empty, these lists must + * be empty. If access is empty, those lists may be non-empty. + */ + public access?: ResourceRef[]; + + /** + * (apat) List of accounts in addition to the sender that may be accessed from the + * application's approval-program and clear-state-program. + */ + public accounts?: Address[]; + + /** + * (apaa) transaction specific arguments accessed from the application's + * approval-program and clear-state-program. + */ + public applicationArgs?: Uint8Array[]; + + /** + * (apap) Logic executed for every application transaction, except when + * on-completion is set to "clear". It can read and write global state for the + * application, as well as account-specific local state. Approval programs may + * reject the transaction. + */ + public approvalProgram?: Uint8Array; + + /** + * (apbx) the boxes that can be accessed by this transaction (and others in the + * same group). + */ + public boxReferences?: BoxReference[]; + + /** + * (apsu) Logic executed for application transactions with on-completion set to + * "clear". It can read and write global state for the application, as well as + * account-specific local state. Clear state programs cannot reject the + * transaction. + */ + public clearStateProgram?: Uint8Array; + + /** + * (epp) specifies the additional app program len requested in pages. + */ + public extraProgramPages?: number; + + /** + * (apfa) Lists the applications in addition to the application-id whose global + * states may be accessed by this application's approval-program and + * clear-state-program. The access is read-only. + */ + public foreignApps?: bigint[]; + + /** + * (apas) lists the assets whose parameters may be accessed by this application's + * ApprovalProgram and ClearStateProgram. The access is read-only. + */ + public foreignAssets?: bigint[]; + + /** + * Represents a (apls) local-state or (apgs) global-state schema. These schemas + * determine how much storage may be used in a local-state or global-state for an + * application. The more space used, the larger minimum balance must be maintained + * in the account holding the data. + */ + public globalStateSchema?: StateSchema; + + /** + * Represents a (apls) local-state or (apgs) global-state schema. These schemas + * determine how much storage may be used in a local-state or global-state for an + * application. The more space used, the larger minimum balance must be maintained + * in the account holding the data. + */ + public localStateSchema?: StateSchema; + + /** + * (apan) defines the what additional actions occur with the transaction. + * Valid types: + * * noop + * * optin + * * closeout + * * clear + * * update + * * update + * * delete + */ + public onCompletion?: string; + + /** + * (aprv) the lowest application version for which this transaction should + * immediately fail. 0 indicates that no version check should be performed. + */ + public rejectVersion?: number; + + /** + * Creates a new `TransactionApplication` object. + * @param applicationId - (apid) ID of the application being configured or empty if creating. + * @param access - (al) Access unifies `accounts`, `foreign-apps`, `foreign-assets`, and + * `box-references` under a single list. If access is non-empty, these lists must + * be empty. If access is empty, those lists may be non-empty. + * @param accounts - (apat) List of accounts in addition to the sender that may be accessed from the + * application's approval-program and clear-state-program. + * @param applicationArgs - (apaa) transaction specific arguments accessed from the application's + * approval-program and clear-state-program. + * @param approvalProgram - (apap) Logic executed for every application transaction, except when + * on-completion is set to "clear". It can read and write global state for the + * application, as well as account-specific local state. Approval programs may + * reject the transaction. + * @param boxReferences - (apbx) the boxes that can be accessed by this transaction (and others in the + * same group). + * @param clearStateProgram - (apsu) Logic executed for application transactions with on-completion set to + * "clear". It can read and write global state for the application, as well as + * account-specific local state. Clear state programs cannot reject the + * transaction. + * @param extraProgramPages - (epp) specifies the additional app program len requested in pages. + * @param foreignApps - (apfa) Lists the applications in addition to the application-id whose global + * states may be accessed by this application's approval-program and + * clear-state-program. The access is read-only. + * @param foreignAssets - (apas) lists the assets whose parameters may be accessed by this application's + * ApprovalProgram and ClearStateProgram. The access is read-only. + * @param globalStateSchema - Represents a (apls) local-state or (apgs) global-state schema. These schemas + * determine how much storage may be used in a local-state or global-state for an + * application. The more space used, the larger minimum balance must be maintained + * in the account holding the data. + * @param localStateSchema - Represents a (apls) local-state or (apgs) global-state schema. These schemas + * determine how much storage may be used in a local-state or global-state for an + * application. The more space used, the larger minimum balance must be maintained + * in the account holding the data. + * @param onCompletion - (apan) defines the what additional actions occur with the transaction. + * Valid types: + * * noop + * * optin + * * closeout + * * clear + * * update + * * update + * * delete + * @param rejectVersion - (aprv) the lowest application version for which this transaction should + * immediately fail. 0 indicates that no version check should be performed. + */ + constructor({ + applicationId, + access, + accounts, + applicationArgs, + approvalProgram, + boxReferences, + clearStateProgram, + extraProgramPages, + foreignApps, + foreignAssets, + globalStateSchema, + localStateSchema, + onCompletion, + rejectVersion, + }: { + applicationId: number | bigint; + access?: ResourceRef[]; + accounts?: (Address | string)[]; + applicationArgs?: Uint8Array[]; + approvalProgram?: string | Uint8Array; + boxReferences?: BoxReference[]; + clearStateProgram?: string | Uint8Array; + extraProgramPages?: number | bigint; + foreignApps?: (number | bigint)[]; + foreignAssets?: (number | bigint)[]; + globalStateSchema?: StateSchema; + localStateSchema?: StateSchema; + onCompletion?: string; + rejectVersion?: number | bigint; + }) { + this.applicationId = ensureBigInt(applicationId); + this.access = access; + this.accounts = + typeof accounts !== 'undefined' + ? accounts.map((addr) => + typeof addr === 'string' ? Address.fromString(addr) : addr + ) + : undefined; + this.applicationArgs = applicationArgs; + this.approvalProgram = + typeof approvalProgram === 'string' + ? base64ToBytes(approvalProgram) + : approvalProgram; + this.boxReferences = boxReferences; + this.clearStateProgram = + typeof clearStateProgram === 'string' + ? base64ToBytes(clearStateProgram) + : clearStateProgram; + this.extraProgramPages = + typeof extraProgramPages === 'undefined' + ? undefined + : ensureSafeInteger(extraProgramPages); + this.foreignApps = + typeof foreignApps === 'undefined' + ? undefined + : foreignApps.map(ensureBigInt); + this.foreignAssets = + typeof foreignAssets === 'undefined' + ? undefined + : foreignAssets.map(ensureBigInt); + this.globalStateSchema = globalStateSchema; + this.localStateSchema = localStateSchema; + this.onCompletion = onCompletion; + this.rejectVersion = + typeof rejectVersion === 'undefined' + ? undefined + : ensureSafeInteger(rejectVersion); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionApplication.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['application-id', this.applicationId], + [ + 'access', + typeof this.access !== 'undefined' + ? this.access.map((v) => v.toEncodingData()) + : undefined, + ], + [ + 'accounts', + typeof this.accounts !== 'undefined' + ? this.accounts.map((v) => v.toString()) + : undefined, + ], + ['application-args', this.applicationArgs], + ['approval-program', this.approvalProgram], + [ + 'box-references', + typeof this.boxReferences !== 'undefined' + ? this.boxReferences.map((v) => v.toEncodingData()) + : undefined, + ], + ['clear-state-program', this.clearStateProgram], + ['extra-program-pages', this.extraProgramPages], + ['foreign-apps', this.foreignApps], + ['foreign-assets', this.foreignAssets], + [ + 'global-state-schema', + typeof this.globalStateSchema !== 'undefined' + ? this.globalStateSchema.toEncodingData() + : undefined, + ], + [ + 'local-state-schema', + typeof this.localStateSchema !== 'undefined' + ? this.localStateSchema.toEncodingData() + : undefined, + ], + ['on-completion', this.onCompletion], + ['reject-version', this.rejectVersion], + ]); + } + + static fromEncodingData(data: unknown): TransactionApplication { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionApplication: ${data}`); + } + return new TransactionApplication({ + applicationId: data.get('application-id'), + access: + typeof data.get('access') !== 'undefined' + ? data + .get('access') + .map((v: unknown) => ResourceRef.fromEncodingData(v)) + : undefined, + accounts: data.get('accounts'), + applicationArgs: data.get('application-args'), + approvalProgram: data.get('approval-program'), + boxReferences: + typeof data.get('box-references') !== 'undefined' + ? data + .get('box-references') + .map((v: unknown) => BoxReference.fromEncodingData(v)) + : undefined, + clearStateProgram: data.get('clear-state-program'), + extraProgramPages: data.get('extra-program-pages'), + foreignApps: data.get('foreign-apps'), + foreignAssets: data.get('foreign-assets'), + globalStateSchema: + typeof data.get('global-state-schema') !== 'undefined' + ? StateSchema.fromEncodingData(data.get('global-state-schema')) + : undefined, + localStateSchema: + typeof data.get('local-state-schema') !== 'undefined' + ? StateSchema.fromEncodingData(data.get('local-state-schema')) + : undefined, + onCompletion: data.get('on-completion'), + rejectVersion: data.get('reject-version'), + }); + } +} + +/** + * Fields for asset allocation, re-configuration, and destruction. + * A zero value for asset-id indicates asset creation. + * A zero value for the params indicates asset destruction. + * Definition: + * data/transactions/asset.go : AssetConfigTxnFields + */ +export class TransactionAssetConfig implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'asset-id', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'params', + valueSchema: new OptionalSchema(AssetParams.encodingSchema), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (xaid) ID of the asset being configured or empty if creating. + */ + public assetId?: bigint; + + /** + * AssetParams specifies the parameters for an asset. + * (apar) when part of an AssetConfig transaction. + * Definition: + * data/transactions/asset.go : AssetParams + */ + public params?: AssetParams; + + /** + * Creates a new `TransactionAssetConfig` object. + * @param assetId - (xaid) ID of the asset being configured or empty if creating. + * @param params - AssetParams specifies the parameters for an asset. + * (apar) when part of an AssetConfig transaction. + * Definition: + * data/transactions/asset.go : AssetParams + */ + constructor({ + assetId, + params, + }: { + assetId?: number | bigint; + params?: AssetParams; + }) { + this.assetId = + typeof assetId === 'undefined' ? undefined : ensureBigInt(assetId); + this.params = params; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionAssetConfig.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['asset-id', this.assetId], + [ + 'params', + typeof this.params !== 'undefined' + ? this.params.toEncodingData() + : undefined, + ], + ]); + } + + static fromEncodingData(data: unknown): TransactionAssetConfig { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionAssetConfig: ${data}`); + } + return new TransactionAssetConfig({ + assetId: data.get('asset-id'), + params: + typeof data.get('params') !== 'undefined' + ? AssetParams.fromEncodingData(data.get('params')) + : undefined, + }); + } +} + +/** + * Fields for an asset freeze transaction. + * Definition: + * data/transactions/asset.go : AssetFreezeTxnFields + */ +export class TransactionAssetFreeze implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'address', valueSchema: new StringSchema(), omitEmpty: true }, + { key: 'asset-id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { + key: 'new-freeze-status', + valueSchema: new BooleanSchema(), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (fadd) Address of the account whose asset is being frozen or thawed. + */ + public address: string; + + /** + * (faid) ID of the asset being frozen or thawed. + */ + public assetId: bigint; + + /** + * (afrz) The new freeze status. + */ + public newFreezeStatus: boolean; + + /** + * Creates a new `TransactionAssetFreeze` object. + * @param address - (fadd) Address of the account whose asset is being frozen or thawed. + * @param assetId - (faid) ID of the asset being frozen or thawed. + * @param newFreezeStatus - (afrz) The new freeze status. + */ + constructor({ + address, + assetId, + newFreezeStatus, + }: { + address: string; + assetId: number | bigint; + newFreezeStatus: boolean; + }) { + this.address = address; + this.assetId = ensureBigInt(assetId); + this.newFreezeStatus = newFreezeStatus; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionAssetFreeze.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['address', this.address], + ['asset-id', this.assetId], + ['new-freeze-status', this.newFreezeStatus], + ]); + } + + static fromEncodingData(data: unknown): TransactionAssetFreeze { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionAssetFreeze: ${data}`); + } + return new TransactionAssetFreeze({ + address: data.get('address'), + assetId: data.get('asset-id'), + newFreezeStatus: data.get('new-freeze-status'), + }); + } +} + +/** + * Fields for an asset transfer transaction. + * Definition: + * data/transactions/asset.go : AssetTransferTxnFields + */ +export class TransactionAssetTransfer implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'amount', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'asset-id', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'receiver', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'close-amount', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'close-to', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + }, + { + key: 'sender', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (aamt) Amount of asset to transfer. A zero amount transferred to self allocates + * that asset in the account's Assets map. + */ + public amount: bigint; + + /** + * (xaid) ID of the asset being transferred. + */ + public assetId: bigint; + + /** + * (arcv) Recipient address of the transfer. + */ + public receiver: string; + + /** + * Number of assets transferred to the close-to account as part of the transaction. + */ + public closeAmount?: bigint; + + /** + * (aclose) Indicates that the asset should be removed from the account's Assets + * map, and specifies where the remaining asset holdings should be transferred. + * It's always valid to transfer remaining asset holdings to the creator account. + */ + public closeTo?: string; + + /** + * (asnd) The effective sender during a clawback transactions. If this is not a + * zero value, the real transaction sender must be the Clawback address from the + * AssetParams. + */ + public sender?: string; + + /** + * Creates a new `TransactionAssetTransfer` object. + * @param amount - (aamt) Amount of asset to transfer. A zero amount transferred to self allocates + * that asset in the account's Assets map. + * @param assetId - (xaid) ID of the asset being transferred. + * @param receiver - (arcv) Recipient address of the transfer. + * @param closeAmount - Number of assets transferred to the close-to account as part of the transaction. + * @param closeTo - (aclose) Indicates that the asset should be removed from the account's Assets + * map, and specifies where the remaining asset holdings should be transferred. + * It's always valid to transfer remaining asset holdings to the creator account. + * @param sender - (asnd) The effective sender during a clawback transactions. If this is not a + * zero value, the real transaction sender must be the Clawback address from the + * AssetParams. + */ + constructor({ + amount, + assetId, + receiver, + closeAmount, + closeTo, + sender, + }: { + amount: number | bigint; + assetId: number | bigint; + receiver: string; + closeAmount?: number | bigint; + closeTo?: string; + sender?: string; + }) { + this.amount = ensureBigInt(amount); + this.assetId = ensureBigInt(assetId); + this.receiver = receiver; + this.closeAmount = + typeof closeAmount === 'undefined' + ? undefined + : ensureBigInt(closeAmount); + this.closeTo = closeTo; + this.sender = sender; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionAssetTransfer.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['amount', this.amount], + ['asset-id', this.assetId], + ['receiver', this.receiver], + ['close-amount', this.closeAmount], + ['close-to', this.closeTo], + ['sender', this.sender], + ]); + } + + static fromEncodingData(data: unknown): TransactionAssetTransfer { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionAssetTransfer: ${data}`); + } + return new TransactionAssetTransfer({ + amount: data.get('amount'), + assetId: data.get('asset-id'), + receiver: data.get('receiver'), + closeAmount: data.get('close-amount'), + closeTo: data.get('close-to'), + sender: data.get('sender'), + }); + } +} + +/** + * Fields for a heartbeat transaction. + * Definition: + * data/transactions/heartbeat.go : HeartbeatTxnFields + */ +export class TransactionHeartbeat implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'hb-address', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'hb-key-dilution', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'hb-proof', + valueSchema: HbProofFields.encodingSchema, + omitEmpty: true, + }, + { key: 'hb-seed', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { + key: 'hb-vote-id', + valueSchema: new ByteArraySchema(), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (hbad) HbAddress is the account this txn is proving onlineness for. + */ + public hbAddress: string; + + /** + * (hbkd) HbKeyDilution must match HbAddress account's current KeyDilution. + */ + public hbKeyDilution: bigint; + + /** + * (hbprf) HbProof is a signature using HeartbeatAddress's partkey, thereby showing + * it is online. + */ + public hbProof: HbProofFields; + + /** + * (hbsd) HbSeed must be the block seed for the this transaction's firstValid + * block. + */ + public hbSeed: Uint8Array; + + /** + * (hbvid) HbVoteID must match the HbAddress account's current VoteID. + */ + public hbVoteId: Uint8Array; + + /** + * Creates a new `TransactionHeartbeat` object. + * @param hbAddress - (hbad) HbAddress is the account this txn is proving onlineness for. + * @param hbKeyDilution - (hbkd) HbKeyDilution must match HbAddress account's current KeyDilution. + * @param hbProof - (hbprf) HbProof is a signature using HeartbeatAddress's partkey, thereby showing + * it is online. + * @param hbSeed - (hbsd) HbSeed must be the block seed for the this transaction's firstValid + * block. + * @param hbVoteId - (hbvid) HbVoteID must match the HbAddress account's current VoteID. + */ + constructor({ + hbAddress, + hbKeyDilution, + hbProof, + hbSeed, + hbVoteId, + }: { + hbAddress: string; + hbKeyDilution: number | bigint; + hbProof: HbProofFields; + hbSeed: string | Uint8Array; + hbVoteId: string | Uint8Array; + }) { + this.hbAddress = hbAddress; + this.hbKeyDilution = ensureBigInt(hbKeyDilution); + this.hbProof = hbProof; + this.hbSeed = typeof hbSeed === 'string' ? base64ToBytes(hbSeed) : hbSeed; + this.hbVoteId = + typeof hbVoteId === 'string' ? base64ToBytes(hbVoteId) : hbVoteId; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionHeartbeat.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['hb-address', this.hbAddress], + ['hb-key-dilution', this.hbKeyDilution], + ['hb-proof', this.hbProof.toEncodingData()], + ['hb-seed', this.hbSeed], + ['hb-vote-id', this.hbVoteId], + ]); + } + + static fromEncodingData(data: unknown): TransactionHeartbeat { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionHeartbeat: ${data}`); + } + return new TransactionHeartbeat({ + hbAddress: data.get('hb-address'), + hbKeyDilution: data.get('hb-key-dilution'), + hbProof: HbProofFields.fromEncodingData( + data.get('hb-proof') ?? new Map() + ), + hbSeed: data.get('hb-seed'), + hbVoteId: data.get('hb-vote-id'), + }); + } +} + +/** + * Fields for a keyreg transaction. + * Definition: + * data/transactions/keyreg.go : KeyregTxnFields + */ +export class TransactionKeyreg implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'non-participation', + valueSchema: new OptionalSchema(new BooleanSchema()), + omitEmpty: true, + }, + { + key: 'selection-participation-key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'state-proof-key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'vote-first-valid', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'vote-key-dilution', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'vote-last-valid', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'vote-participation-key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (nonpart) Mark the account as participating or non-participating. + */ + public nonParticipation?: boolean; + + /** + * (selkey) Public key used with the Verified Random Function (VRF) result during + * committee selection. + */ + public selectionParticipationKey?: Uint8Array; + + /** + * (sprfkey) State proof key used in key registration transactions. + */ + public stateProofKey?: Uint8Array; + + /** + * (votefst) First round this participation key is valid. + */ + public voteFirstValid?: bigint; + + /** + * (votekd) Number of subkeys in each batch of participation keys. + */ + public voteKeyDilution?: bigint; + + /** + * (votelst) Last round this participation key is valid. + */ + public voteLastValid?: bigint; + + /** + * (votekey) Participation public key used in key registration transactions. + */ + public voteParticipationKey?: Uint8Array; + + /** + * Creates a new `TransactionKeyreg` object. + * @param nonParticipation - (nonpart) Mark the account as participating or non-participating. + * @param selectionParticipationKey - (selkey) Public key used with the Verified Random Function (VRF) result during + * committee selection. + * @param stateProofKey - (sprfkey) State proof key used in key registration transactions. + * @param voteFirstValid - (votefst) First round this participation key is valid. + * @param voteKeyDilution - (votekd) Number of subkeys in each batch of participation keys. + * @param voteLastValid - (votelst) Last round this participation key is valid. + * @param voteParticipationKey - (votekey) Participation public key used in key registration transactions. + */ + constructor({ + nonParticipation, + selectionParticipationKey, + stateProofKey, + voteFirstValid, + voteKeyDilution, + voteLastValid, + voteParticipationKey, + }: { + nonParticipation?: boolean; + selectionParticipationKey?: string | Uint8Array; + stateProofKey?: string | Uint8Array; + voteFirstValid?: number | bigint; + voteKeyDilution?: number | bigint; + voteLastValid?: number | bigint; + voteParticipationKey?: string | Uint8Array; + }) { + this.nonParticipation = nonParticipation; + this.selectionParticipationKey = + typeof selectionParticipationKey === 'string' + ? base64ToBytes(selectionParticipationKey) + : selectionParticipationKey; + this.stateProofKey = + typeof stateProofKey === 'string' + ? base64ToBytes(stateProofKey) + : stateProofKey; + this.voteFirstValid = + typeof voteFirstValid === 'undefined' + ? undefined + : ensureBigInt(voteFirstValid); + this.voteKeyDilution = + typeof voteKeyDilution === 'undefined' + ? undefined + : ensureBigInt(voteKeyDilution); + this.voteLastValid = + typeof voteLastValid === 'undefined' + ? undefined + : ensureBigInt(voteLastValid); + this.voteParticipationKey = + typeof voteParticipationKey === 'string' + ? base64ToBytes(voteParticipationKey) + : voteParticipationKey; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionKeyreg.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['non-participation', this.nonParticipation], + ['selection-participation-key', this.selectionParticipationKey], + ['state-proof-key', this.stateProofKey], + ['vote-first-valid', this.voteFirstValid], + ['vote-key-dilution', this.voteKeyDilution], + ['vote-last-valid', this.voteLastValid], + ['vote-participation-key', this.voteParticipationKey], + ]); + } + + static fromEncodingData(data: unknown): TransactionKeyreg { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionKeyreg: ${data}`); + } + return new TransactionKeyreg({ + nonParticipation: data.get('non-participation'), + selectionParticipationKey: data.get('selection-participation-key'), + stateProofKey: data.get('state-proof-key'), + voteFirstValid: data.get('vote-first-valid'), + voteKeyDilution: data.get('vote-key-dilution'), + voteLastValid: data.get('vote-last-valid'), + voteParticipationKey: data.get('vote-participation-key'), + }); + } +} + +/** + * Fields for a payment transaction. + * Definition: + * data/transactions/payment.go : PaymentTxnFields + */ +export class TransactionPayment implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'amount', valueSchema: new Uint64Schema(), omitEmpty: true }, + { key: 'receiver', valueSchema: new StringSchema(), omitEmpty: true }, + { + key: 'close-amount', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'close-remainder-to', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (amt) number of MicroAlgos intended to be transferred. + */ + public amount: bigint; + + /** + * (rcv) receiver's address. + */ + public receiver: string; + + /** + * Number of MicroAlgos that were sent to the close-remainder-to address when + * closing the sender account. + */ + public closeAmount?: bigint; + + /** + * (close) when set, indicates that the sending account should be closed and all + * remaining funds be transferred to this address. + */ + public closeRemainderTo?: string; + + /** + * Creates a new `TransactionPayment` object. + * @param amount - (amt) number of MicroAlgos intended to be transferred. + * @param receiver - (rcv) receiver's address. + * @param closeAmount - Number of MicroAlgos that were sent to the close-remainder-to address when + * closing the sender account. + * @param closeRemainderTo - (close) when set, indicates that the sending account should be closed and all + * remaining funds be transferred to this address. + */ + constructor({ + amount, + receiver, + closeAmount, + closeRemainderTo, + }: { + amount: number | bigint; + receiver: string; + closeAmount?: number | bigint; + closeRemainderTo?: string; + }) { + this.amount = ensureBigInt(amount); + this.receiver = receiver; + this.closeAmount = + typeof closeAmount === 'undefined' + ? undefined + : ensureBigInt(closeAmount); + this.closeRemainderTo = closeRemainderTo; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionPayment.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['amount', this.amount], + ['receiver', this.receiver], + ['close-amount', this.closeAmount], + ['close-remainder-to', this.closeRemainderTo], + ]); + } + + static fromEncodingData(data: unknown): TransactionPayment { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionPayment: ${data}`); + } + return new TransactionPayment({ + amount: data.get('amount'), + receiver: data.get('receiver'), + closeAmount: data.get('close-amount'), + closeRemainderTo: data.get('close-remainder-to'), + }); + } +} + +/** + * + */ +export class TransactionResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'transaction', + valueSchema: Transaction.encodingSchema, + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * Round at which the results were computed. + */ + public currentRound: bigint; + + /** + * Contains all fields common to all transactions and serves as an envelope to all + * transactions type. Represents both regular and inner transactions. + * Definition: + * data/transactions/signedtxn.go : SignedTxn + * data/transactions/transaction.go : Transaction + */ + public transaction: Transaction; + + /** + * Creates a new `TransactionResponse` object. + * @param currentRound - Round at which the results were computed. + * @param transaction - Contains all fields common to all transactions and serves as an envelope to all + * transactions type. Represents both regular and inner transactions. + * Definition: + * data/transactions/signedtxn.go : SignedTxn + * data/transactions/transaction.go : Transaction + */ + constructor({ + currentRound, + transaction, + }: { + currentRound: number | bigint; + transaction: Transaction; + }) { + this.currentRound = ensureBigInt(currentRound); + this.transaction = transaction; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['current-round', this.currentRound], + ['transaction', this.transaction.toEncodingData()], + ]); + } + + static fromEncodingData(data: unknown): TransactionResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionResponse: ${data}`); + } + return new TransactionResponse({ + currentRound: data.get('current-round'), + transaction: Transaction.fromEncodingData( + data.get('transaction') ?? new Map() + ), + }); + } +} + +/** + * Validation signature associated with some data. Only one of the signatures + * should be provided. + */ +export class TransactionSignature implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'logicsig', + valueSchema: new OptionalSchema( + TransactionSignatureLogicsig.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'multisig', + valueSchema: new OptionalSchema( + TransactionSignatureMultisig.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'sig', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (lsig) Programatic transaction signature. + * Definition: + * data/transactions/logicsig.go + */ + public logicsig?: TransactionSignatureLogicsig; + + /** + * structure holding multiple subsignatures. + * Definition: + * crypto/multisig.go : MultisigSig + */ + public multisig?: TransactionSignatureMultisig; + + /** + * (sig) Standard ed25519 signature. + */ + public sig?: Uint8Array; + + /** + * Creates a new `TransactionSignature` object. + * @param logicsig - (lsig) Programatic transaction signature. + * Definition: + * data/transactions/logicsig.go + * @param multisig - structure holding multiple subsignatures. + * Definition: + * crypto/multisig.go : MultisigSig + * @param sig - (sig) Standard ed25519 signature. + */ + constructor({ + logicsig, + multisig, + sig, + }: { + logicsig?: TransactionSignatureLogicsig; + multisig?: TransactionSignatureMultisig; + sig?: string | Uint8Array; + }) { + this.logicsig = logicsig; + this.multisig = multisig; + this.sig = typeof sig === 'string' ? base64ToBytes(sig) : sig; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionSignature.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + [ + 'logicsig', + typeof this.logicsig !== 'undefined' + ? this.logicsig.toEncodingData() + : undefined, + ], + [ + 'multisig', + typeof this.multisig !== 'undefined' + ? this.multisig.toEncodingData() + : undefined, + ], + ['sig', this.sig], + ]); + } + + static fromEncodingData(data: unknown): TransactionSignature { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionSignature: ${data}`); + } + return new TransactionSignature({ + logicsig: + typeof data.get('logicsig') !== 'undefined' + ? TransactionSignatureLogicsig.fromEncodingData(data.get('logicsig')) + : undefined, + multisig: + typeof data.get('multisig') !== 'undefined' + ? TransactionSignatureMultisig.fromEncodingData(data.get('multisig')) + : undefined, + sig: data.get('sig'), + }); + } +} + +/** + * (lsig) Programatic transaction signature. + * Definition: + * data/transactions/logicsig.go + */ +export class TransactionSignatureLogicsig implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { key: 'logic', valueSchema: new ByteArraySchema(), omitEmpty: true }, + { + key: 'args', + valueSchema: new OptionalSchema( + new ArraySchema(new ByteArraySchema()) + ), + omitEmpty: true, + }, + { + key: 'logic-multisig-signature', + valueSchema: new OptionalSchema( + TransactionSignatureMultisig.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'multisig-signature', + valueSchema: new OptionalSchema( + TransactionSignatureMultisig.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'signature', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (l) Program signed by a signature or multi signature, or hashed to be the + * address of ana ccount. Base64 encoded TEAL program. + */ + public logic: Uint8Array; + + /** + * (arg) Logic arguments, base64 encoded. + */ + public args?: Uint8Array[]; + + /** + * structure holding multiple subsignatures. + * Definition: + * crypto/multisig.go : MultisigSig + */ + public logicMultisigSignature?: TransactionSignatureMultisig; + + /** + * structure holding multiple subsignatures. + * Definition: + * crypto/multisig.go : MultisigSig + */ + public multisigSignature?: TransactionSignatureMultisig; + + /** + * (sig) ed25519 signature. + */ + public signature?: Uint8Array; + + /** + * Creates a new `TransactionSignatureLogicsig` object. + * @param logic - (l) Program signed by a signature or multi signature, or hashed to be the + * address of ana ccount. Base64 encoded TEAL program. + * @param args - (arg) Logic arguments, base64 encoded. + * @param logicMultisigSignature - structure holding multiple subsignatures. + * Definition: + * crypto/multisig.go : MultisigSig + * @param multisigSignature - structure holding multiple subsignatures. + * Definition: + * crypto/multisig.go : MultisigSig + * @param signature - (sig) ed25519 signature. + */ + constructor({ + logic, + args, + logicMultisigSignature, + multisigSignature, + signature, + }: { + logic: string | Uint8Array; + args?: Uint8Array[]; + logicMultisigSignature?: TransactionSignatureMultisig; + multisigSignature?: TransactionSignatureMultisig; + signature?: string | Uint8Array; + }) { + this.logic = typeof logic === 'string' ? base64ToBytes(logic) : logic; + this.args = args; + this.logicMultisigSignature = logicMultisigSignature; + this.multisigSignature = multisigSignature; + this.signature = + typeof signature === 'string' ? base64ToBytes(signature) : signature; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionSignatureLogicsig.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['logic', this.logic], + ['args', this.args], + [ + 'logic-multisig-signature', + typeof this.logicMultisigSignature !== 'undefined' + ? this.logicMultisigSignature.toEncodingData() + : undefined, + ], + [ + 'multisig-signature', + typeof this.multisigSignature !== 'undefined' + ? this.multisigSignature.toEncodingData() + : undefined, + ], + ['signature', this.signature], + ]); + } + + static fromEncodingData(data: unknown): TransactionSignatureLogicsig { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionSignatureLogicsig: ${data}`); + } + return new TransactionSignatureLogicsig({ + logic: data.get('logic'), + args: data.get('args'), + logicMultisigSignature: + typeof data.get('logic-multisig-signature') !== 'undefined' + ? TransactionSignatureMultisig.fromEncodingData( + data.get('logic-multisig-signature') + ) + : undefined, + multisigSignature: + typeof data.get('multisig-signature') !== 'undefined' + ? TransactionSignatureMultisig.fromEncodingData( + data.get('multisig-signature') + ) + : undefined, + signature: data.get('signature'), + }); + } +} + +/** + * structure holding multiple subsignatures. + * Definition: + * crypto/multisig.go : MultisigSig + */ +export class TransactionSignatureMultisig implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'subsignature', + valueSchema: new OptionalSchema( + new ArraySchema( + TransactionSignatureMultisigSubsignature.encodingSchema + ) + ), + omitEmpty: true, + }, + { + key: 'threshold', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + }, + { + key: 'version', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (subsig) holds pairs of public key and signatures. + */ + public subsignature?: TransactionSignatureMultisigSubsignature[]; + + /** + * (thr) + */ + public threshold?: number; + + /** + * (v) + */ + public version?: number; + + /** + * Creates a new `TransactionSignatureMultisig` object. + * @param subsignature - (subsig) holds pairs of public key and signatures. + * @param threshold - (thr) + * @param version - (v) + */ + constructor({ + subsignature, + threshold, + version, + }: { + subsignature?: TransactionSignatureMultisigSubsignature[]; + threshold?: number | bigint; + version?: number | bigint; + }) { + this.subsignature = subsignature; + this.threshold = + typeof threshold === 'undefined' + ? undefined + : ensureSafeInteger(threshold); + this.version = + typeof version === 'undefined' ? undefined : ensureSafeInteger(version); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionSignatureMultisig.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + [ + 'subsignature', + typeof this.subsignature !== 'undefined' + ? this.subsignature.map((v) => v.toEncodingData()) + : undefined, + ], + ['threshold', this.threshold], + ['version', this.version], + ]); + } + + static fromEncodingData(data: unknown): TransactionSignatureMultisig { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionSignatureMultisig: ${data}`); + } + return new TransactionSignatureMultisig({ + subsignature: + typeof data.get('subsignature') !== 'undefined' + ? data + .get('subsignature') + .map((v: unknown) => + TransactionSignatureMultisigSubsignature.fromEncodingData(v) + ) + : undefined, + threshold: data.get('threshold'), + version: data.get('version'), + }); + } +} + +export class TransactionSignatureMultisigSubsignature implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'public-key', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + }, + { + key: 'signature', + valueSchema: new OptionalSchema(new ByteArraySchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (pk) + */ + public publicKey?: Uint8Array; + + /** + * (s) + */ + public signature?: Uint8Array; + + /** + * Creates a new `TransactionSignatureMultisigSubsignature` object. + * @param publicKey - (pk) + * @param signature - (s) + */ + constructor({ + publicKey, + signature, + }: { + publicKey?: string | Uint8Array; + signature?: string | Uint8Array; + }) { + this.publicKey = + typeof publicKey === 'string' ? base64ToBytes(publicKey) : publicKey; + this.signature = + typeof signature === 'string' ? base64ToBytes(signature) : signature; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionSignatureMultisigSubsignature.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['public-key', this.publicKey], + ['signature', this.signature], + ]); + } + + static fromEncodingData( + data: unknown + ): TransactionSignatureMultisigSubsignature { + if (!(data instanceof Map)) { + throw new Error( + `Invalid decoded TransactionSignatureMultisigSubsignature: ${data}` + ); + } + return new TransactionSignatureMultisigSubsignature({ + publicKey: data.get('public-key'), + signature: data.get('signature'), + }); + } +} + +/** + * Fields for a state proof transaction. + * Definition: + * data/transactions/stateproof.go : StateProofTxnFields + */ +export class TransactionStateProof implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'message', + valueSchema: new OptionalSchema( + IndexerStateProofMessage.encodingSchema + ), + omitEmpty: true, + }, + { + key: 'state-proof', + valueSchema: new OptionalSchema(StateProofFields.encodingSchema), + omitEmpty: true, + }, + { + key: 'state-proof-type', + valueSchema: new OptionalSchema(new Uint64Schema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * (spmsg) + */ + public message?: IndexerStateProofMessage; + + /** + * (sp) represents a state proof. + * Definition: + * crypto/stateproof/structs.go : StateProof + */ + public stateProof?: StateProofFields; + + /** + * (sptype) Type of the state proof. Integer representing an entry defined in + * protocol/stateproof.go + */ + public stateProofType?: number; + + /** + * Creates a new `TransactionStateProof` object. + * @param message - (spmsg) + * @param stateProof - (sp) represents a state proof. + * Definition: + * crypto/stateproof/structs.go : StateProof + * @param stateProofType - (sptype) Type of the state proof. Integer representing an entry defined in + * protocol/stateproof.go + */ + constructor({ + message, + stateProof, + stateProofType, + }: { + message?: IndexerStateProofMessage; + stateProof?: StateProofFields; + stateProofType?: number | bigint; + }) { + this.message = message; + this.stateProof = stateProof; + this.stateProofType = + typeof stateProofType === 'undefined' + ? undefined + : ensureSafeInteger(stateProofType); + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionStateProof.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + [ + 'message', + typeof this.message !== 'undefined' + ? this.message.toEncodingData() + : undefined, + ], + [ + 'state-proof', + typeof this.stateProof !== 'undefined' + ? this.stateProof.toEncodingData() + : undefined, + ], + ['state-proof-type', this.stateProofType], + ]); + } + + static fromEncodingData(data: unknown): TransactionStateProof { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionStateProof: ${data}`); + } + return new TransactionStateProof({ + message: + typeof data.get('message') !== 'undefined' + ? IndexerStateProofMessage.fromEncodingData(data.get('message')) + : undefined, + stateProof: + typeof data.get('state-proof') !== 'undefined' + ? StateProofFields.fromEncodingData(data.get('state-proof')) + : undefined, + stateProofType: data.get('state-proof-type'), + }); + } +} + +/** + * + */ +export class TransactionsResponse implements Encodable { + private static encodingSchemaValue: Schema | undefined; + + static get encodingSchema(): Schema { + if (!this.encodingSchemaValue) { + this.encodingSchemaValue = new NamedMapSchema([]); + (this.encodingSchemaValue as NamedMapSchema).pushEntries( + { + key: 'current-round', + valueSchema: new Uint64Schema(), + omitEmpty: true, + }, + { + key: 'transactions', + valueSchema: new ArraySchema(Transaction.encodingSchema), + omitEmpty: true, + }, + { + key: 'next-token', + valueSchema: new OptionalSchema(new StringSchema()), + omitEmpty: true, + } + ); + } + return this.encodingSchemaValue; + } + + /** + * Round at which the results were computed. + */ + public currentRound: bigint; + + public transactions: Transaction[]; + + /** + * Used for pagination, when making another request provide this token with the + * next parameter. + */ + public nextToken?: string; + + /** + * Creates a new `TransactionsResponse` object. + * @param currentRound - Round at which the results were computed. + * @param transactions - + * @param nextToken - Used for pagination, when making another request provide this token with the + * next parameter. + */ + constructor({ + currentRound, + transactions, + nextToken, + }: { + currentRound: number | bigint; + transactions: Transaction[]; + nextToken?: string; + }) { + this.currentRound = ensureBigInt(currentRound); + this.transactions = transactions; + this.nextToken = nextToken; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): Schema { + return TransactionsResponse.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['current-round', this.currentRound], + ['transactions', this.transactions.map((v) => v.toEncodingData())], + ['next-token', this.nextToken], + ]); + } + + static fromEncodingData(data: unknown): TransactionsResponse { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded TransactionsResponse: ${data}`); + } + return new TransactionsResponse({ + currentRound: data.get('current-round'), + transactions: (data.get('transactions') ?? []).map((v: unknown) => + Transaction.fromEncodingData(v) + ), + nextToken: data.get('next-token'), + }); + } +} diff --git a/packages/sdk/src/client/v2/indexer/searchAccounts.ts b/packages/sdk/src/client/v2/indexer/searchAccounts.ts new file mode 100644 index 00000000..b385a285 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/searchAccounts.ts @@ -0,0 +1,298 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { AccountsResponse } from './models/types.js'; + +/** + * Returns information about indexed accounts. + * + * #### Example + * ```typescript + * const accounts = await indexerClient.searchAccounts().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2accounts) + * @category GET + */ +export default class SearchAccounts extends JSONRequest { + /** + * @returns `/v2/accounts` + */ + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/accounts'; + } + + /** + * Filtered results should have an amount greater than this value, as int, representing microAlgos, unless an asset-id is provided, in which case units are in the asset's units. + * + * #### Example 1 + * ```typescript + * const minBalance = 300000; + * const accounts = await indexerClient + * .searchAccounts() + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetID = 163650; + * const minBalance = 300000; + * const accounts = await indexerClient + * .searchAccounts() + * .assetID(assetID) + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * @remarks + * If you are looking for accounts with the currency amount greater than 0, simply construct the query without `currencyGreaterThan` because it doesn't accept `-1`, and passing the `0` `currency-greater-than` value would exclude accounts with a 0 amount. + * + * @param greater + * @category query + */ + currencyGreaterThan(greater: number | bigint) { + // We convert the following to a string for now to correctly include zero values in request parameters. + this.query['currency-greater-than'] = greater.toString(); + return this; + } + + /** + * Filtered results should have an amount less than this value, as int, representing microAlgos, unless an asset-id is provided, in which case units are in the asset's units. + * + * #### Example 1 + * ```typescript + * const maxBalance = 500000; + * const accounts = await indexerClient + * .searchAccounts() + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetID = 163650; + * const maxBalance = 500000; + * const accounts = await indexerClient + * .searchAccounts() + * .assetID(assetID) + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * @param lesser + * @category query + */ + currencyLessThan(lesser: number | bigint) { + this.query['currency-less-than'] = lesser; + return this; + } + + /** + * Maximum number of results to return. + * + * #### Example + * ```typescript + * const maxResults = 25; + * const accounts = await indexerClient + * .searchAccounts() + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Asset ID to filter with. + * + * #### Example + * ```typescript + * const assetID = 163650; + * const accounts = await indexerClient + * .searchAccounts() + * .assetID(assetID) + * .do(); + * ``` + * + * @param id + * @category query + */ + assetID(id: number | bigint) { + this.query['asset-id'] = id; + return this; + } + + /** + * The next page of results. + * + * #### Example + * ```typescript + * const maxResults = 25; + * + * const accountsPage1 = await indexerClient + * .searchAccounts() + * .limit(maxResults) + * .do(); + * + * const accountsPage2 = await indexerClient + * .searchAccounts() + * .limit(maxResults) + * .nextToken(accountsPage1["next-token"]) + * .do(); + * ``` + * + * @param nextToken - provided by the previous results + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Include results for the specified round. + * + * #### Example + * ```typescript + * const targetBlock = 18309917; + * const accounts = await indexerClient + * .searchAccounts() + * .round(targetBlock) + * .do(); + * ``` + * @remarks For performance reasons, this parameter may be disabled on some configurations. + * @param round + * @category query + */ + round(round: number | bigint) { + this.query.round = round; + return this; + } + + /** + * Include accounts that use this spending key. + * + * #### Example + * ```typescript + * const authAddr = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const accounts = await indexerClient + * .searchAccounts() + * .authAddr(authAddr) + * .do(); + * ``` + * + * @param authAddr + */ + authAddr(authAddr: string | Address) { + this.query['auth-addr'] = authAddr.toString(); + return this; + } + + /** + * Filter for this application. + * + * #### Example + * ```typescript + * const appId = 60553466; + * const accounts = await indexerClient + * .searchAccounts() + * .applicationID(appId) + * .do(); + * ``` + * + * @param applicationID + * @category query + */ + applicationID(applicationID: number | bigint) { + this.query['application-id'] = applicationID; + return this; + } + + /** + * Includes all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example 1 + * ```typescript + * const assetId = 163650; + * const accounts = await indexerClient + * .searchAccounts() + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetId = 163650; + * const accounts = await indexerClient + * .searchAccounts() + * .includeAll() + * .do(); + * ``` + * + * @param value - default true when called without passing a value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + /** + * Exclude additional items such as asset holdings, application local data stored for this account, asset parameters created by this account, and application parameters created by this account. + * + * #### Example 1 + * ```typescript + * const accounts = await indexerClient + * .searchAccounts() + * .exclude("all") + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const accounts = await indexerClient + * .searchAccounts() + * .exclude("assets,created-assets") + * .do(); + * ``` + * @remarks By default, it behaves as exclude=none + * @param exclude - Array of `all`, `assets`, `created-assets`, `apps-local-state`, `created-apps`, `none` + * @category query + */ + exclude(exclude: string) { + this.query.exclude = exclude; + return this; + } + + /** + * If true, only online accounts will be returned in the response. + * + * #### Example + * ```typescript + * const onlineOnly = true; + * const accounts = await indexerClient + * .searchAccounts + * .onlineOnly(onlineOnly) + * .do(); + * ``` + * + * @param onlineOnly - if true, only online accounts will be returned in the response + * @category query + */ + onlineOnly(onlineOnly: boolean) { + this.query['online-only'] = onlineOnly; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AccountsResponse { + return decodeJSON(response.getJSONText(), AccountsResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/searchForApplicationBoxes.ts b/packages/sdk/src/client/v2/indexer/searchForApplicationBoxes.ts new file mode 100644 index 00000000..ea75b22a --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/searchForApplicationBoxes.ts @@ -0,0 +1,100 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClient, HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { BoxesResponse } from './models/types.js'; + +export default class SearchForApplicationBoxes extends JSONRequest { + private index: bigint; + + /** + * Returns information about indexed application boxes. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const appID = 1234; + * + * const responsePage1 = await indexerClient + * .searchForApplicationBoxes(appID) + * .limit(maxResults) + * .do(); + * const boxNamesPage1 = responsePage1.boxes.map(box => box.name); + * + * const responsePage2 = await indexerClient + * .searchForApplicationBoxes(appID) + * .limit(maxResults) + * .nextToken(responsePage1.nextToken) + * .do(); + * const boxNamesPage2 = responsePage2.boxes.map(box => box.name); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applicationsapplication-idboxes) + * @oaram index - application index. + * @category GET + */ + constructor(c: HTTPClient, index: number | bigint) { + super(c); + this.index = BigInt(index); + } + + /** + * @returns `/v2/applications/${index}/boxes` + */ + path() { + return `/v2/applications/${this.index}/boxes`; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const appID = 1234; + * + * const responsePage1 = await indexerClient + * .searchForApplicationBoxes(appID) + * .limit(maxResults) + * .do(); + * const boxNamesPage1 = responsePage1.boxes.map(box => box.name); + * + * const responsePage2 = await indexerClient + * .searchForApplicationBoxes(appID) + * .limit(maxResults) + * .nextToken(responsePage1.nextToken) + * .do(); + * const boxNamesPage2 = responsePage2.boxes.map(box => box.name); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(next: string) { + this.query.next = next; + return this; + } + + /** + * Limit results for pagination. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const boxesResponse = await indexerClient + * .searchForApplicationBoxes(1234) + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): BoxesResponse { + return decodeJSON(response.getJSONText(), BoxesResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/searchForApplications.ts b/packages/sdk/src/client/v2/indexer/searchForApplications.ts new file mode 100644 index 00000000..c0c51a51 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/searchForApplications.ts @@ -0,0 +1,143 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { ApplicationsResponse } from './models/types.js'; + +/** + * Returns information about indexed applications. + * + * #### Example + * ```typescript + * const apps = await indexerClient.searchForApplications().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2applications) + * @category GET + */ +export default class SearchForApplications extends JSONRequest { + /** + * @returns `/v2/applications` + */ + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/applications'; + } + + /** + * Application ID for filter, as int + * + * #### Example + * ```typescript + * const appId = 60553466; + * const apps = await indexerClient + * .searchForApplications() + * .index(appId) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupApplications(appId).do()` + * @param index + * @category query + */ + index(index: number | bigint) { + this.query['application-id'] = index; + return this; + } + + /** + * Creator for filter, as string + * + * #### Example + * ```typescript + * const creator = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const apps = await indexerClient + * .searchForApplications() + * .creator(creator) + * .do(); + * ``` + * @param creator + * @category query + */ + creator(creator: string | Address) { + this.query.creator = creator.toString(); + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const maxResults = 20; + * + * const appsPage1 = await indexerClient + * .searchForApplications() + * .limit(maxResults) + * .do(); + * + * const appsPage2 = await indexerClient + * .searchForApplications() + * .limit(maxResults) + * .nextToken(appsPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(next: string) { + this.query.next = next; + return this; + } + + /** + * Limit results for pagination. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const apps = await indexerClient + * .searchForApplications() + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Includes all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example 1 + * ```typescript + * const apps = await indexerClient + * .searchForApplications() + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const apps = await indexerClient + * .searchForApplications() + * .includeAll() + * .do(); + * ``` + * + * @param value - default true when called without passing a value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): ApplicationsResponse { + return decodeJSON(response.getJSONText(), ApplicationsResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/searchForAssets.ts b/packages/sdk/src/client/v2/indexer/searchForAssets.ts new file mode 100644 index 00000000..674055a3 --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/searchForAssets.ts @@ -0,0 +1,184 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { AssetsResponse } from './models/types.js'; + +/** + * Returns information about indexed assets. + * + * #### Example + * ```typescript + * const assets = await indexerClient.searchForAssets().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2assets) + * @category GET + */ +export default class SearchForAssets extends JSONRequest { + /** + * @returns `/v2/assets` + */ + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/assets'; + } + + /** + * Limit results for pagination. + * + * #### Example + * ```typescript + * const maxResults = 20; + * const assets = await indexerClient + * .searchForAssets() + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit - maximum number of results to return. + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Filter just assets with the given creator address. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const assets = await indexerClient + * .searchForAssets() + * .creator(address) + * .do(); + * ``` + * + * @param creator + * @category query + */ + creator(creator: string | Address) { + this.query.creator = creator.toString(); + return this; + } + + /** + * Filter just assets with the given name. + * + * #### Example + * ```typescript + * const name = "Test Token"; + * const assets = await indexerClient + * .searchForAssets() + * .name(name) + * .do(); + * ``` + * + * @param name + * @category query + */ + name(name: string) { + this.query.name = name; + return this; + } + + /** + * Filter just assets with the given unit. + * + * #### Example + * ```typescript + * const unit = "test"; + * const assets = await indexerClient + * .searchForAssets() + * .unit(unit) + * .do(); + * ``` + * + * @param unit + * @category query + */ + unit(unit: string) { + this.query.unit = unit; + return this; + } + + /** + * Asset ID for filter, as int. + * + * #### Example + * ```typescript + * const assetId = 163650; + * const assets = await indexerClient + * .searchForAssets() + * .index(assetId) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupAssetByID(assetId).do();` + * @param index + * @category query + */ + index(index: number | bigint) { + this.query['asset-id'] = index; + return this; + } + + /** + * Specify the next page of results. + * + * #### Example + * ```typescript + * const maxResults = 20; + * + * const assetsPage1 = await indexerClient + * .searchForAssets() + * .limit(maxResults) + * .do(); + * + * const assetsPage2 = await indexerClient + * .searchForAssets() + * .limit(maxResults) + * .nextToken(assetsPage1["next-token"]) + * .do(); + * ``` + * @param nextToken - provided by the previous results. + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Includes all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates + * + * #### Example 1 + * ```typescript + * const assets = await indexerClient + * .searchForAssets() + * .includeAll(false) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assets = await indexerClient + * .searchForAssets() + * .includeAll() + * .do(); + * ``` + * + * @param value - default true when called without passing a value + * @category query + */ + includeAll(value = true) { + this.query['include-all'] = value; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): AssetsResponse { + return decodeJSON(response.getJSONText(), AssetsResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/searchForBlockHeaders.ts b/packages/sdk/src/client/v2/indexer/searchForBlockHeaders.ts new file mode 100644 index 00000000..72a7b52b --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/searchForBlockHeaders.ts @@ -0,0 +1,223 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { Address } from '../../../encoding/address.js'; +import { BlockHeadersResponse } from './models/types.js'; + +/** + * Returns information about indexed block headers. + * + * #### Example + * ```typescript + * const bhs = await indexerClient.searchForBlockHeaders().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2block-headers) + * @category GET + */ +export default class SearchForBlockHeaders extends JSONRequest { + /** + * @returns `/v2/block-headers` + */ + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/block-headers'; + } + + /** + * Accounts marked as absent in the block header's participation updates. + * + * #### Example + * ```typescript + * const address1 = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const address2 = "4H5UNRBJ2Q6JENAXQ6HNTGKLKINP4J4VTQBEPK5F3I6RDICMZBPGNH6KD4"; + * const bhs = await indexerClient + * .searchForBlockHeaders() + * .absent([address1,address2]) + * .do(); + * ``` + * + * @param absent - a comma separated list of addresses + * @category query + */ + absent(absent: (string | Address)[]) { + this.query.absent = absent; + return this; + } + + /** + * Include results after the given time. + * + * #### Example + * ```typescript + * const afterTime = "2022-10-21T00:00:11.55Z"; + * const bhs = await indexerClient + * .searchForBlockHeaders() + * .afterTime(afterTime) + * .do(); + * ``` + * + * @param after - rfc3339 string or Date object + * @category query + */ + afterTime(after: string | Date) { + this.query['after-time'] = + after instanceof Date ? after.toISOString() : after; + return this; + } + + /** + * Include results before the given time. + * + * #### Example + * ```typescript + * const beforeTime = "2022-02-02T20:20:22.02Z"; + * const bhs = await indexerClient + * .searchForBlockHeaders() + * .beforeTime(beforeTime) + * .do(); + * ``` + * + * @param before - rfc3339 string or Date object + * @category query + */ + beforeTime(before: string | Date) { + this.query['before-time'] = + before instanceof Date ? before.toISOString() : before; + return this; + } + + /** + * Accounts marked as expired in the block header's participation updates. + * + * #### Example + * ```typescript + * const address1 = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const address2 = "4H5UNRBJ2Q6JENAXQ6HNTGKLKINP4J4VTQBEPK5F3I6RDICMZBPGNH6KD4"; + * const bhs = await indexerClient + * .searchForBlockHeaders() + * .expired([address1,address2]) + * .do(); + * ``` + * + * @param expired - - a comma separated list of addresses + * @category query + */ + expired(expired: (string | Address)[]) { + this.query.expired = expired; + return this; + } + + /** + * Maximum number of results to return. + * + * #### Example + * ```typescript + * const maxResults = 25; + * const bhs = await indexerClient + * .searchForBlockHeaders() + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Include results at or before the specified max-round. + * + * #### Example + * ```typescript + * const maxRound = 18309917; + * const bhs = await indexerClient + * .searchForBlockHeaders() + * .maxRound(maxRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + maxRound(round: number | bigint) { + this.query['max-round'] = round; + return this; + } + + /** + * Include results at or after the specified min-round. + * + * #### Example + * ```typescript + * const minRound = 18309917; + * const bhs = await indexerClient + * .searchForBlockHeaders() + * .minRound(minRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + minRound(round: number | bigint) { + this.query['min-round'] = round; + return this; + } + + /** + * The next page of results. + * + * #### Example + * ```typescript + * const maxResults = 25; + * + * const bh1 = await indexerClient + * .searchForBlockHeaders() + * .limit(maxResults) + * .do(); + * + * const bh2 = await indexerClient + * .searchForBlockHeaders() + * .limit(maxResults) + * .nextToken(bh1["next-token"]) + * .do(); + * ``` + * + * @param nextToken - provided by the previous results + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Accounts marked as proposer in the block header's participation updates. + * + * #### Example + * ```typescript + * const address1 = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const address2 = "4H5UNRBJ2Q6JENAXQ6HNTGKLKINP4J4VTQBEPK5F3I6RDICMZBPGNH6KD4"; + * const bhs = await indexerClient + * .searchForBlockHeaders() + * .proposers([address1,address2]) + * .do(); + * ``` + * + * @param proposers - a comma separated list of addresses + * @category query + */ + proposers(proposers: (string | Address)[]) { + this.query.proposers = proposers; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): BlockHeadersResponse { + return decodeJSON(response.getJSONText(), BlockHeadersResponse); + } +} diff --git a/packages/sdk/src/client/v2/indexer/searchForTransactions.ts b/packages/sdk/src/client/v2/indexer/searchForTransactions.ts new file mode 100644 index 00000000..50825b4e --- /dev/null +++ b/packages/sdk/src/client/v2/indexer/searchForTransactions.ts @@ -0,0 +1,465 @@ +import JSONRequest from '../jsonrequest.js'; +import { HTTPClientResponse } from '../../client.js'; +import { decodeJSON } from '../../../encoding/encoding.js'; +import { base64StringFunnel } from './lookupAccountTransactions.js'; +import { Address } from '../../../encoding/address.js'; +import { TransactionsResponse } from './models/types.js'; + +/** + * Returns information about indexed transactions. + * + * #### Example + * ```typescript + * const txns = await indexerClient.searchForTransactions().do(); + * ``` + * + * [Response data schema details](https://developer.algorand.org/docs/rest-apis/indexer/#get-v2transactions) + * @category GET + */ +export default class SearchForTransactions extends JSONRequest { + /** + * @returns `/v2/transactions` + */ + // eslint-disable-next-line class-methods-use-this + path() { + return '/v2/transactions'; + } + + /** + * Specifies a prefix which must be contained in the note field. + * + * #### Example + * ```typescript + * const notePrefixBase64Encoded = "Y3JlYXRl"; + * const txns = await indexerClient + * .searchForTransactions() + * .notePrefix(notePrefixBase64Encoded) + * .do(); + * ``` + * + * @param prefix - base64 string or uint8array + * @category query + */ + notePrefix(prefix: Uint8Array | string) { + this.query['note-prefix'] = base64StringFunnel(prefix); + return this; + } + + /** + * Type of transaction to filter with. + * + * #### Example + * ```typescript + * const txns = await indexerClient + * .searchForTransactions() + * .txType("keyreg") + * .do(); + * ``` + * + * @param type - one of `pay`, `keyreg`, `acfg`, `axfer`, `afrz`, `appl`, `stpf` + * @category query + */ + txType(type: string) { + this.query['tx-type'] = type; + return this; + } + + /** + * Type of signature to filter with. + * - sig: Standard + * - msig: MultiSig + * - lsig: LogicSig + * + * #### Example + * ```typescript + * const txns = await indexerClient + * .searchForTransactions() + * .sigType("sig") + * .do(); + * ``` + * + * @param type - one of `sig`, `msig`, `lsig` + * @category query + */ + sigType(type: string) { + this.query['sig-type'] = type; + return this; + } + + /** + * Lookup the specific transaction by ID. + * + * #### Example + * ```typescript + * const txId = "MEUOC4RQJB23CQZRFRKYEI6WBO73VTTPST5A7B3S5OKBUY6LFUDA"; + * const txns = await indexerClient + * .searchForTransactions() + * .txid(txId) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupTransactionByID(txnId).do()` + * @param txid + * @category query + */ + txid(txid: string) { + this.query.txid = txid; + return this; + } + + /** + * Lookup transactions by group ID. + * + * #### Example + * ```typescript + * const groupIdBase64Encoded = "A62qVigWtWo0laUzcE1iZY8+KXWzK1vSkgwN/eKgvjc="; + * const txns = await indexerClient + * .searchForTransactions() + * .groupid(groupIdBase64Encoded) + * .do(); + * ``` + * + * @param groupid - base64 string or uint8array + * @category query + */ + groupid(groupid: Uint8Array | string) { + this.query['group-id'] = base64StringFunnel(groupid); + return this; + } + + /** + * Include results for the specified round. + * + * #### Example + * ```typescript + * const targetBlock = 18309917; + * const txns = await indexerClient + * .searchForTransactions() + * .round(targetBlock) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupBlock(targetBlock).do()` + * @param round + * @category query + */ + round(round: number | bigint) { + this.query.round = round; + return this; + } + + /** + * Include results at or after the specified min-round. + * + * #### Example + * ```typescript + * const minRound = 18309917; + * const txns = await indexerClient + * .searchForTransactions() + * .minRound(minRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + minRound(round: number | bigint) { + this.query['min-round'] = round; + return this; + } + + /** + * Include results at or before the specified max-round. + * + * #### Example + * ```typescript + * const maxRound = 18309917; + * const txns = await indexerClient + * .searchForTransactions() + * .maxRound(maxRound) + * .do(); + * ``` + * + * @param round + * @category query + */ + maxRound(round: number | bigint) { + this.query['max-round'] = round; + return this; + } + + /** + * Asset ID to filter with. + * + * #### Example + * ```typescript + * const assetID = 163650; + * const txns = await indexerClient + * .searchForTransactions() + * .assetID(assetID) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupAssetTransactions(assetId).do()` + * @param id + * @category query + */ + assetID(id: number | bigint) { + this.query['asset-id'] = id; + return this; + } + + /** + * Maximum number of results to return. + * + * #### Example + * ```typescript + * const maxResults = 25; + * const txns = await indexerClient + * .searchForTransactions() + * .limit(maxResults) + * .do(); + * ``` + * + * @param limit + * @category query + */ + limit(limit: number) { + this.query.limit = limit; + return this; + } + + /** + * Include results before the given time. + * + * #### Example + * ```typescript + * const beforeTime = "2022-02-02T20:20:22.02Z"; + * const txns = await indexerClient + * .searchForTransactions() + * .beforeTime(beforeTime) + * .do(); + * ``` + * + * @param before - rfc3339 string or Date object + * @category query + */ + beforeTime(before: string | Date) { + this.query['before-time'] = + before instanceof Date ? before.toISOString() : before; + return this; + } + + /** + * Include results after the given time. + * + * #### Example + * ```typescript + * const afterTime = "2022-10-21T00:00:11.55Z"; + * const txns = await indexerClient + * .searchForTransactions() + * .afterTime(afterTime) + * .do(); + * ``` + * + * @param after - rfc3339 string or Date object + * @category query + */ + afterTime(after: string | Date) { + this.query['after-time'] = + after instanceof Date ? after.toISOString() : after; + return this; + } + + /** + * Combined with address, defines what address to filter on, as string. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const role = "freeze-target"; + * const txns = await indexerClient + * .searchForTransactions() + * .address(address) + * .addressRole(role) + * .do(); + * ``` + * + * @param role - one of `sender`, `receiver`, `freeze-target` + * @category query + */ + addressRole(role: string) { + this.query['address-role'] = role; + return this; + } + + /** + * Only include transactions with this address in one of the transaction fields. + * + * #### Example + * ```typescript + * const address = "XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA"; + * const txns = await indexerClient + * .searchForTransactions() + * .address(address) + * .do(); + * ``` + * @remarks Alternatively, use `indexerClient.lookupAccountTransactions(address).do()` + * @param address + * @category query + */ + address(address: string | Address) { + this.query.address = address.toString(); + return this; + } + + /** + * Whether or not to consider the `close-to` field as a receiver when filtering transactions, as bool. Set to `true` to ignore `close-to`. + * + * #### Example + * ```typescript + * const txns = await indexerClient + * .searchForTransactions() + * .excludeCloseTo(true) + * .do(); + * ``` + * + * @param exclude + * @category query + */ + excludeCloseTo(exclude: boolean) { + this.query['exclude-close-to'] = exclude; + return this; + } + + /** + * The next page of results. + * + * #### Example + * ```typescript + * const maxResults = 25; + * + * const txnsPage1 = await indexerClient + * .searchForTransactions() + * .limit(maxResults) + * .do(); + * + * const txnsPage2 = await indexerClient + * .searchForTransactions() + * .limit(maxResults) + * .nextToken(txnsPage1["next-token"]) + * .do(); + * ``` + * + * @param nextToken - provided by the previous results + * @category query + */ + nextToken(nextToken: string) { + this.query.next = nextToken; + return this; + } + + /** + * Whether or not to include rekeying transactions. + * + * #### Example + * ```typescript + * const txns = await indexerClient + * .searchForTransactions() + * .rekeyTo(false) + * .do(); + * ``` + * + * @param rekeyTo + * @category query + */ + rekeyTo(rekeyTo: boolean) { + this.query['rekey-to'] = rekeyTo; + return this; + } + + /** + * Filter for this application. + * + * #### Example + * ```typescript + * const appId = 60553466; + * const txns = await indexerClient + * .searchForTransactions() + * .applicationID(appId) + * .do(); + * ``` + * + * @param applicationID + * @category query + */ + applicationID(applicationID: number | bigint) { + this.query['application-id'] = applicationID; + return this; + } + + /** + * Filtered results should have an amount greater than this value, as int, representing microAlgos, unless an asset-id is provided, in which case units are in the asset's units. + * + * #### Example 1 + * ```typescript + * const minBalance = 300000; + * const txns = await indexerClient + * .searchForTransactions() + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetID = 163650; + * const minBalance = 300000; + * const txns = await indexerClient + * .searchForTransactions() + * .assetID(assetID) + * .currencyGreaterThan(minBalance - 1) + * .do(); + * ``` + * + * @param greater + * @category query + */ + currencyGreaterThan(greater: number | bigint) { + // We convert the following to a string for now to correctly include zero values in request parameters. + this.query['currency-greater-than'] = greater.toString(); + return this; + } + + /** + * Filtered results should have an amount less than this value, as int, representing microAlgos, unless an asset-id is provided, in which case units are in the asset's units. + * + * #### Example 1 + * ```typescript + * const maxBalance = 500000; + * const txns = await indexerClient + * .searchForTransactions() + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * #### Example 2 + * ```typescript + * const assetID = 163650; + * const maxBalance = 500000; + * const txns = await indexerClient + * .searchForTransactions() + * .assetID(assetID) + * .currencyLessThan(maxBalance + 1) + * .do(); + * ``` + * + * @param lesser + * @category query + */ + currencyLessThan(lesser: number | bigint) { + this.query['currency-less-than'] = lesser; + return this; + } + + // eslint-disable-next-line class-methods-use-this + prepare(response: HTTPClientResponse): TransactionsResponse { + return decodeJSON(response.getJSONText(), TransactionsResponse); + } +} diff --git a/packages/sdk/src/client/v2/jsonrequest.ts b/packages/sdk/src/client/v2/jsonrequest.ts new file mode 100644 index 00000000..c29914e7 --- /dev/null +++ b/packages/sdk/src/client/v2/jsonrequest.ts @@ -0,0 +1,85 @@ +import { HTTPClient, HTTPClientResponse } from '../client.js'; + +/** + * Base abstract class for JSON requests. + * + * Data: The type returned from the `do()` method + * + * Body: The structure of the response's body + */ +export default abstract class JSONRequest { + c: HTTPClient; + query: Record; + + /** + * @param client - HTTPClient object. + */ + constructor(client: HTTPClient) { + this.c = client; + this.query = {}; + } + + /** + * @returns The path of this request. + * @category JSONRequest + */ + abstract path(): string; + + /** + * Prepare a JSON response before returning it. + * + * Use this method to unpack response ata as needed after receiving it from the `do()` method. + * @param response - Response body received + * @category JSONRequest + */ + abstract prepare(response: HTTPClientResponse): Data; + + /** + * Execute the request + */ + protected executeRequest( + headers?: Record, + customOptions?: Record + ): Promise { + return this.c.get({ + relativePath: this.path(), + query: this.query, + requestHeaders: headers, + customOptions, + }); + } + + /** + * Execute the request and decode the response. + * @param headers - Additional headers to send in the request. Optional. + * @param customOptions - Additional options to pass to the underlying BaseHTTPClient. For + * {@link URLTokenBaseHTTPClient}, which is the default client, this will be treated as + * additional options to pass to the network `fetch` method. + * @returns A promise which resolves to the parsed response object. + * @category JSONRequest + */ + async do( + headers?: Record, + customOptions?: Record + ): Promise { + const res = await this.executeRequest(headers, customOptions); + return this.prepare(res); + } + + /** + * Execute the request, but do not process the response data in any way. + * @param headers - Additional headers to send in the request. Optional. + * @param customOptions - Additional options to pass to the underlying BaseHTTPClient. For + * {@link URLTokenBaseHTTPClient}, which is the default client, this will be treated as + * additional options to pass to the network `fetch` method. + * @returns A promise which resolves to the raw response data, exactly as returned by the server. + * @category JSONRequest + */ + async doRaw( + headers?: Record, + customOptions?: Record + ): Promise { + const res = await this.executeRequest(headers, customOptions); + return res.body; + } +} diff --git a/packages/sdk/src/client/v2/serviceClient.ts b/packages/sdk/src/client/v2/serviceClient.ts new file mode 100644 index 00000000..504d74a0 --- /dev/null +++ b/packages/sdk/src/client/v2/serviceClient.ts @@ -0,0 +1,67 @@ +import { HTTPClient } from '../client.js'; +import { BaseHTTPClient } from '../baseHTTPClient.js'; +import { TokenHeader } from '../urlTokenBaseHTTPClient.js'; + +export type TokenHeaderIdentifier = + | 'X-Indexer-API-Token' + | 'X-KMD-API-Token' + | 'X-Algo-API-Token' + | string; + +/** + * Convert a token string to a token header + * @param token - The token string + * @param headerIdentifier - An identifier for the token header + */ +function convertTokenStringToTokenHeader( + headerIdentifier: TokenHeaderIdentifier, + token: string = '' +): TokenHeader { + const tokenHeader: TokenHeader = {}; + if (token === '') { + return tokenHeader; + } + tokenHeader[headerIdentifier] = token; + return tokenHeader; +} + +function isBaseHTTPClient( + tbc: string | TokenHeader | BaseHTTPClient +): tbc is BaseHTTPClient { + return typeof (tbc as BaseHTTPClient).get === 'function'; +} + +/** + * Abstract service client to encapsulate shared AlgodClient and IndexerClient logic + */ +export default abstract class ServiceClient { + /** @ignore */ + c: HTTPClient; + + constructor( + tokenHeaderIdentifier: TokenHeaderIdentifier, + tokenHeaderOrStrOrBaseClient: string | TokenHeader | BaseHTTPClient, + baseServer: string, + port?: string | number, + defaultHeaders: Record = {} + ) { + if (isBaseHTTPClient(tokenHeaderOrStrOrBaseClient)) { + // we are using a base client + this.c = new HTTPClient(tokenHeaderOrStrOrBaseClient); + } else { + // Accept token header as string or object + // - workaround to allow backwards compatibility for multiple headers + let tokenHeader: TokenHeader; + if (typeof tokenHeaderOrStrOrBaseClient === 'string') { + tokenHeader = convertTokenStringToTokenHeader( + tokenHeaderIdentifier, + tokenHeaderOrStrOrBaseClient + ); + } else { + tokenHeader = tokenHeaderOrStrOrBaseClient; + } + + this.c = new HTTPClient(tokenHeader, baseServer, port, defaultHeaders); + } + } +} diff --git a/packages/sdk/src/client/v2/untypedmodel.ts b/packages/sdk/src/client/v2/untypedmodel.ts new file mode 100644 index 00000000..e7b19b5e --- /dev/null +++ b/packages/sdk/src/client/v2/untypedmodel.ts @@ -0,0 +1,25 @@ +import { Encodable, MsgpackEncodingData } from '../../encoding/encoding.js'; +import { UntypedSchema } from '../../encoding/schema/index.js'; + +export class UntypedValue implements Encodable { + static readonly encodingSchema = new UntypedSchema(); + + public readonly data: MsgpackEncodingData; + + constructor(data: MsgpackEncodingData) { + this.data = data; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): UntypedSchema { + return UntypedValue.encodingSchema; + } + + public toEncodingData(): MsgpackEncodingData { + return this.data; + } + + public static fromEncodingData(data: unknown): UntypedValue { + return new UntypedValue(data as MsgpackEncodingData); + } +} diff --git a/packages/sdk/src/composer.ts b/packages/sdk/src/composer.ts new file mode 100644 index 00000000..7fcc5a90 --- /dev/null +++ b/packages/sdk/src/composer.ts @@ -0,0 +1,742 @@ +import type { + AlgodClient, + PendingTransactionResponse, + SimulateRequest, + SimulateTransaction, +} from '@algorandfoundation/algokit-algod-client' +import type { AccessReference, BoxReference, SignedTransaction } from '@algorandfoundation/algokit-transact' +import { OnApplicationComplete, decodeSignedTransaction, getTransactionId } from '@algorandfoundation/algokit-transact' +import { + ABIAddressType, + ABIMethod, + ABIReferenceType, + ABITupleType, + ABIType, + ABIUintType, + ABIValue, + abiCheckTransactionType, + abiTypeIsReference, + abiTypeIsTransaction, +} from './abi/index.js' +import { Address } from './encoding/address.js' +import { assignGroupID } from './group.js' +import { SdkTransactionParams, makeApplicationCallTxnFromObject } from './makeTxn.js' +import { TransactionSigner, TransactionWithSigner, isTransactionWithSigner } from './signer.js' +import { arrayEqual, ensureUint64, stringifyJSON } from './utils/utils.js' +import { waitForConfirmation } from './wait.js' + +// First 4 bytes of SHA-512/256 hash of "return" +const RETURN_PREFIX = new Uint8Array([21, 31, 124, 117]) + +// The maximum number of arguments for an application call transaction +const MAX_APP_ARGS = 16 + +export type ABIArgument = ABIValue | TransactionWithSigner + +/** Represents the output from a successful ABI method call. */ +export interface ABIResult { + /** The TxID of the transaction that invoked the ABI method call. */ + txID: string + /** + * The raw bytes of the return value from the ABI method call. This will be empty if the method + * does not return a value (return type "void"). + */ + rawReturnValue: Uint8Array + /** + * The method that was called for this result + */ + method: ABIMethod + /** + * The return value from the ABI method call. This will be undefined if the method does not return + * a value (return type "void"), or if the SDK was unable to decode the returned value. + */ + returnValue?: ABIValue + /** If the SDK was unable to decode a return value, the error will be here. */ + decodeError?: Error + /** The pending transaction information from the method transaction */ + txInfo?: PendingTransactionResponse +} + +export enum AtomicTransactionComposerStatus { + /** The atomic group is still under construction. */ + BUILDING, + + /** The atomic group has been finalized, but not yet signed. */ + BUILT, + + /** The atomic group has been finalized and signed, but not yet submitted to the network. */ + SIGNED, + + /** The atomic group has been finalized, signed, and submitted to the network. */ + SUBMITTED, + + /** The atomic group has been finalized, signed, submitted, and successfully committed to a block. */ + COMMITTED, +} + +/** + * Add a value to an application call's foreign array. The addition will be as compact as possible, + * and this function will return an index that can be used to reference `valueToAdd` in `array`. + * + * @param valueToAdd - The value to add to the array. If this value is already present in the array, + * it will not be added again. Instead, the existing index will be returned. + * @param array - The existing foreign array. This input may be modified to append `valueToAdd`. + * @param zeroValue - If provided, this value indicated two things: the 0 value is special for this + * array, so all indexes into `array` must start at 1; additionally, if `valueToAdd` equals + * `zeroValue`, then `valueToAdd` will not be added to the array, and instead the 0 indexes will + * be returned. + * @returns An index that can be used to reference `valueToAdd` in `array`. + */ +function populateForeignArray(valueToAdd: Type, array: Type[], zeroValue?: Type): number { + if (zeroValue != null && valueToAdd === zeroValue) { + return 0 + } + + const offset = zeroValue == null ? 0 : 1 + + for (let i = 0; i < array.length; i++) { + if (valueToAdd === array[i]) { + return i + offset + } + } + + array.push(valueToAdd) + return array.length - 1 + offset +} + +/** A class used to construct and execute atomic transaction groups */ +export class AtomicTransactionComposer { + /** The maximum size of an atomic transaction group. */ + static MAX_GROUP_SIZE: number = 16 + + private status = AtomicTransactionComposerStatus.BUILDING + private transactions: TransactionWithSigner[] = [] + private methodCalls: Map = new Map() + private signedTxns: Uint8Array[] = [] + private txIDs: string[] = [] + + /** + * Get the status of this composer's transaction group. + */ + getStatus(): AtomicTransactionComposerStatus { + return this.status + } + + /** + * Get the number of transactions currently in this atomic group. + */ + count(): number { + return this.transactions.length + } + + /** + * Create a new composer with the same underlying transactions. The new composer's status will be + * BUILDING, so additional transactions may be added to it. + */ + clone(): AtomicTransactionComposer { + const theClone = new AtomicTransactionComposer() + + theClone.transactions = this.transactions.map(({ txn, signer }) => { + // Create a new transaction without the group ID + const txnCopy = { ...txn, group: undefined } + return { + txn: txnCopy, + signer, + } + }) + theClone.methodCalls = new Map(this.methodCalls) + + return theClone + } + + /** + * Add a transaction to this atomic group. + * + * An error will be thrown if the transaction has a nonzero group ID, the composer's status is + * not BUILDING, or if adding this transaction causes the current group to exceed MAX_GROUP_SIZE. + */ + addTransaction(txnAndSigner: TransactionWithSigner): void { + if (this.status !== AtomicTransactionComposerStatus.BUILDING) { + throw new Error('Cannot add transactions when composer status is not BUILDING') + } + + if (this.transactions.length === AtomicTransactionComposer.MAX_GROUP_SIZE) { + throw new Error( + `Adding an additional transaction exceeds the maximum atomic group size of ${AtomicTransactionComposer.MAX_GROUP_SIZE}`, + ) + } + + if (txnAndSigner.txn.group && txnAndSigner.txn.group.some((v) => v !== 0)) { + throw new Error('Cannot add a transaction with nonzero group ID') + } + + this.transactions.push(txnAndSigner) + } + + /** + * Add a smart contract method call to this atomic group. + * + * An error will be thrown if the composer's status is not BUILDING, if adding this transaction + * causes the current group to exceed MAX_GROUP_SIZE, or if the provided arguments are invalid + * for the given method. + */ + addMethodCall({ + appID, + method, + methodArgs, + sender, + suggestedParams, + onComplete, + approvalProgram, + clearProgram, + numGlobalInts, + numGlobalByteSlices, + numLocalInts, + numLocalByteSlices, + extraPages, + appAccounts, + appForeignApps, + appForeignAssets, + boxes, + access, + note, + lease, + rekeyTo, + rejectVersion, + signer, + }: { + /** The ID of the smart contract to call. Set this to 0 to indicate an application creation call. */ + appID: number | bigint + /** The method to call on the smart contract */ + method: ABIMethod + /** The arguments to include in the method call. If omitted, no arguments will be passed to the method. */ + methodArgs?: ABIArgument[] + /** The address of the sender of this application call */ + sender: string | Address + /** Transactions params to use for this application call */ + suggestedParams: SdkTransactionParams + /** The OnComplete action to take for this application call. If omitted, OnApplicationComplete.NoOp will be used. */ + onComplete?: OnApplicationComplete + /** The approval program for this application call. Only set this if this is an application creation call, or if onComplete is OnApplicationComplete.UpdateApplication */ + approvalProgram?: Uint8Array + /** The clear program for this application call. Only set this if this is an application creation call, or if onComplete is OnApplicationComplete.UpdateApplication */ + clearProgram?: Uint8Array + /** The global integer schema size. Only set this if this is an application creation call. */ + numGlobalInts?: number + /** The global byte slice schema size. Only set this if this is an application creation call. */ + numGlobalByteSlices?: number + /** The local integer schema size. Only set this if this is an application creation call. */ + numLocalInts?: number + /** The local byte slice schema size. Only set this if this is an application creation call. */ + numLocalByteSlices?: number + /** The number of extra pages to allocate for the application's programs. Only set this if this is an application creation call. If omitted, defaults to 0. */ + extraPages?: number + /** Array of Address strings that represent external accounts supplied to this application. If accounts are provided here, the accounts specified in the method args will appear after these. */ + appAccounts?: Array + /** Array of App ID numbers that represent external apps supplied to this application. If apps are provided here, the apps specified in the method args will appear after these. */ + appForeignApps?: Array + /** Array of Asset ID numbers that represent external assets supplied to this application. If assets are provided here, the assets specified in the method args will appear after these. */ + appForeignAssets?: Array + /** The box references for this application call */ + boxes?: BoxReference[] + /** The resource references for this application call */ + access?: AccessReference[] + /** The note value for this application call */ + note?: Uint8Array + /** The lease value for this application call */ + lease?: Uint8Array + /** If provided, the address that the sender will be rekeyed to at the conclusion of this application call */ + rekeyTo?: string | Address + /** The lowest application version for which this transaction should immediately fail. 0 indicates that no version check should be performed. */ + rejectVersion?: number | bigint + /** A transaction signer that can authorize this application call from sender */ + signer: TransactionSigner + }): void { + if (this.status !== AtomicTransactionComposerStatus.BUILDING) { + throw new Error('Cannot add transactions when composer status is not BUILDING') + } + + if (this.transactions.length + method.txnCount() > AtomicTransactionComposer.MAX_GROUP_SIZE) { + throw new Error(`Adding additional transactions exceeds the maximum atomic group size of ${AtomicTransactionComposer.MAX_GROUP_SIZE}`) + } + + if (BigInt(appID) === BigInt(0)) { + if ( + approvalProgram == null || + clearProgram == null || + numGlobalInts == null || + numGlobalByteSlices == null || + numLocalInts == null || + numLocalByteSlices == null + ) { + throw new Error( + 'One of the following required parameters for application creation is missing: approvalProgram, clearProgram, numGlobalInts, numGlobalByteSlices, numLocalInts, numLocalByteSlices', + ) + } + } else if (onComplete === OnApplicationComplete.UpdateApplication) { + if (approvalProgram == null || clearProgram == null) { + throw new Error( + 'One of the following required parameters for OnApplicationComplete.UpdateApplication is missing: approvalProgram, clearProgram', + ) + } + if ( + numGlobalInts != null || + numGlobalByteSlices != null || + numLocalInts != null || + numLocalByteSlices != null || + extraPages != null + ) { + throw new Error( + 'One of the following application creation parameters were set on a non-creation call: numGlobalInts, numGlobalByteSlices, numLocalInts, numLocalByteSlices, extraPages', + ) + } + } else if ( + approvalProgram != null || + clearProgram != null || + numGlobalInts != null || + numGlobalByteSlices != null || + numLocalInts != null || + numLocalByteSlices != null || + extraPages != null + ) { + throw new Error( + 'One of the following application creation parameters were set on a non-creation call: approvalProgram, clearProgram, numGlobalInts, numGlobalByteSlices, numLocalInts, numLocalByteSlices, extraPages', + ) + } + + // Validate that access and legacy foreign arrays are not both specified + if (access && (appAccounts || appForeignApps || appForeignAssets || boxes)) { + throw new Error('Cannot specify both access and legacy foreign arrays (appAccounts, appForeignApps, appForeignAssets, boxes)') + } + + if (methodArgs == null) { + methodArgs = [] + } + + if (methodArgs.length !== method.args.length) { + throw new Error(`Incorrect number of method arguments. Expected ${method.args.length}, got ${methodArgs.length}`) + } + + let basicArgTypes: ABIType[] = [] + let basicArgValues: ABIValue[] = [] + const txnArgs: TransactionWithSigner[] = [] + const refArgTypes: ABIReferenceType[] = [] + const refArgValues: ABIValue[] = [] + const refArgIndexToBasicArgIndex: Map = new Map() + // TODO: Box encoding for ABI + const boxReferences: BoxReference[] = !boxes ? [] : boxes + + for (let i = 0; i < methodArgs.length; i++) { + let argType = method.args[i].type + const argValue = methodArgs[i] + + if (abiTypeIsTransaction(argType)) { + if (!isTransactionWithSigner(argValue) || !abiCheckTransactionType(argType, argValue.txn)) { + throw new Error(`Expected ${argType} TransactionWithSigner for argument at index ${i}`) + } + if (argValue.txn.group && argValue.txn.group.some((v) => v !== 0)) { + throw new Error('Cannot add a transaction with nonzero group ID') + } + txnArgs.push(argValue) + continue + } + + if (isTransactionWithSigner(argValue)) { + throw new Error(`Expected non-transaction value for argument at index ${i}`) + } + + if (abiTypeIsReference(argType)) { + refArgIndexToBasicArgIndex.set(refArgTypes.length, basicArgTypes.length) + refArgTypes.push(argType) + refArgValues.push(argValue) + // treat the reference as a uint8 for encoding purposes + argType = new ABIUintType(8) + } + + if (typeof argType === 'string') { + throw new Error(`Unknown ABI type: ${argType}`) + } + + basicArgTypes.push(argType) + basicArgValues.push(argValue) + } + + const resolvedRefIndexes: number[] = [] + // Converting addresses to string form for easier comparison + const foreignAccounts: string[] = appAccounts == null ? [] : appAccounts.map((addr) => addr.toString()) + const foreignApps: bigint[] = appForeignApps == null ? [] : appForeignApps.map(ensureUint64) + const foreignAssets: bigint[] = appForeignAssets == null ? [] : appForeignAssets.map(ensureUint64) + for (let i = 0; i < refArgTypes.length; i++) { + const refType = refArgTypes[i] + const refValue = refArgValues[i] + let resolved = 0 + + switch (refType) { + case ABIReferenceType.account: { + const addressType = new ABIAddressType() + const address = addressType.decode(addressType.encode(refValue)) + resolved = populateForeignArray(address, foreignAccounts, sender.toString()) + break + } + case ABIReferenceType.application: { + const uint64Type = new ABIUintType(64) + const refAppID = uint64Type.decode(uint64Type.encode(refValue)) + if (refAppID > Number.MAX_SAFE_INTEGER) { + throw new Error(`Expected safe integer for application value, got ${refAppID}`) + } + resolved = populateForeignArray(refAppID, foreignApps, ensureUint64(appID)) + break + } + case ABIReferenceType.asset: { + const uint64Type = new ABIUintType(64) + const refAssetID = uint64Type.decode(uint64Type.encode(refValue)) + if (refAssetID > Number.MAX_SAFE_INTEGER) { + throw new Error(`Expected safe integer for asset value, got ${refAssetID}`) + } + resolved = populateForeignArray(refAssetID, foreignAssets) + break + } + default: + throw new Error(`Unknown reference type: ${refType}`) + } + + resolvedRefIndexes.push(resolved) + } + + for (let i = 0; i < resolvedRefIndexes.length; i++) { + const basicArgIndex = refArgIndexToBasicArgIndex.get(i)! + basicArgValues[basicArgIndex] = resolvedRefIndexes[i] + } + + if (basicArgTypes.length > MAX_APP_ARGS - 1) { + const lastArgTupleTypes = basicArgTypes.slice(MAX_APP_ARGS - 2) + const lastArgTupleValues = basicArgValues.slice(MAX_APP_ARGS - 2) + + basicArgTypes = basicArgTypes.slice(0, MAX_APP_ARGS - 2) + basicArgValues = basicArgValues.slice(0, MAX_APP_ARGS - 2) + + basicArgTypes.push(new ABITupleType(lastArgTupleTypes)) + basicArgValues.push(lastArgTupleValues) + } + + const appArgsEncoded: Uint8Array[] = [method.getSelector()] + for (let i = 0; i < basicArgTypes.length; i++) { + appArgsEncoded.push(basicArgTypes[i].encode(basicArgValues[i])) + } + + const appCall = { + txn: makeApplicationCallTxnFromObject({ + sender, + appIndex: appID, + appArgs: appArgsEncoded, + // Only pass legacy foreign arrays if access is not provided + accounts: access ? undefined : foreignAccounts, + foreignApps: access ? undefined : foreignApps, + foreignAssets: access ? undefined : foreignAssets, + boxes: access ? undefined : boxReferences, + access, + onComplete: onComplete == null ? OnApplicationComplete.NoOp : onComplete, + approvalProgram, + clearProgram, + numGlobalInts, + numGlobalByteSlices, + numLocalInts, + numLocalByteSlices, + extraPages, + rejectVersion, + lease, + note, + rekeyTo, + suggestedParams, + }), + signer, + } + + this.transactions.push(...txnArgs, appCall) + this.methodCalls.set(this.transactions.length - 1, method) + } + + /** + * Finalize the transaction group and returned the finalized transactions. + * + * The composer's status will be at least BUILT after executing this method. + */ + buildGroup(): TransactionWithSigner[] { + if (this.status === AtomicTransactionComposerStatus.BUILDING) { + if (this.transactions.length === 0) { + throw new Error('Cannot build a group with 0 transactions') + } + if (this.transactions.length > 1) { + const groupedTxns = assignGroupID(this.transactions.map((txnWithSigner) => txnWithSigner.txn)) + this.transactions = this.transactions.map((txnWithSigner, index) => ({ + signer: txnWithSigner.signer, + txn: groupedTxns[index], + })) + } + this.status = AtomicTransactionComposerStatus.BUILT + } + return this.transactions + } + + /** + * Obtain signatures for each transaction in this group. If signatures have already been obtained, + * this method will return cached versions of the signatures. + * + * The composer's status will be at least SIGNED after executing this method. + * + * An error will be thrown if signing any of the transactions fails. + * + * @returns A promise that resolves to an array of signed transactions. + */ + async gatherSignatures(): Promise { + if (this.status >= AtomicTransactionComposerStatus.SIGNED) { + return this.signedTxns + } + + // retrieve built transactions and verify status is BUILT + const txnsWithSigners = this.buildGroup() + const txnGroup = txnsWithSigners.map((txnWithSigner) => txnWithSigner.txn) + + const indexesPerSigner: Map = new Map() + + for (let i = 0; i < txnsWithSigners.length; i++) { + const { signer } = txnsWithSigners[i] + + if (!indexesPerSigner.has(signer)) { + indexesPerSigner.set(signer, []) + } + + indexesPerSigner.get(signer)!.push(i) + } + + const orderedSigners = Array.from(indexesPerSigner) + + const batchedSigs = await Promise.all(orderedSigners.map(([signer, indexes]) => signer(txnGroup, indexes))) + + const signedTxns: Array = txnsWithSigners.map(() => null) + + for (let signerIndex = 0; signerIndex < orderedSigners.length; signerIndex++) { + const indexes = orderedSigners[signerIndex][1] + const sigs = batchedSigs[signerIndex] + + for (let i = 0; i < indexes.length; i++) { + signedTxns[indexes[i]] = sigs[i] + } + } + + function fullyPopulated(a: Array): a is Uint8Array[] { + return a.every((v) => v != null) + } + + if (!fullyPopulated(signedTxns)) { + throw new Error(`Missing signatures. Got ${signedTxns}`) + } + + const txIDs = signedTxns.map((stxn, index) => { + try { + return getTransactionId(decodeSignedTransaction(stxn).txn) + } catch (err) { + throw new Error(`Cannot decode signed transaction at index ${index}. ${err}`) + } + }) + + this.signedTxns = signedTxns + this.txIDs = txIDs + this.status = AtomicTransactionComposerStatus.SIGNED + + return signedTxns + } + + /** + * Send the transaction group to the network, but don't wait for it to be committed to a block. An + * error will be thrown if submission fails. + * + * The composer's status must be SUBMITTED or lower before calling this method. If submission is + * successful, this composer's status will update to SUBMITTED. + * + * Note: a group can only be submitted again if it fails. + * + * @param client - An Algodv2 client + * + * @returns A promise that, upon success, resolves to a list of TxIDs of the submitted transactions. + */ + async submit(client: AlgodClient): Promise { + if (this.status > AtomicTransactionComposerStatus.SUBMITTED) { + throw new Error('Transaction group cannot be resubmitted') + } + + const stxns = await this.gatherSignatures() + + const totalLength = stxns.reduce((sum, stxn) => sum + stxn.length, 0) + const merged = new Uint8Array(totalLength) + let offset = 0 + for (const stxn of stxns) { + merged.set(stxn, offset) + offset += stxn.length + } + + await client.rawTransaction({ body: merged }) + + this.status = AtomicTransactionComposerStatus.SUBMITTED + + return this.txIDs + } + + /** + * Simulates the transaction group in the network. + * + * The composer will try to sign any transactions in the group, then simulate + * the results. + * Simulating the group will not change the composer's status. + * + * @param client - An Algodv2 client + * @param request - SimulateRequest with options in simulation. + * If provided, the request's transaction group will be overrwritten by the composer's group, + * only simulation related options will be used. + * + * @returns A promise that, upon success, resolves to an object containing an + * array of results containing one element for each method call transaction + * in this group (ABIResult[]) and the SimulateTransaction object. + */ + async simulate( + client: AlgodClient, + request?: SimulateRequest, + ): Promise<{ + methodResults: ABIResult[] + simulateResponse: SimulateTransaction + }> { + if (this.status > AtomicTransactionComposerStatus.SUBMITTED) { + throw new Error('Simulated Transaction group has already been submitted to the network') + } + + const stxns = await this.gatherSignatures() + const txnObjects: SignedTransaction[] = stxns.map((stxn) => decodeSignedTransaction(stxn)) + + const currentRequest: SimulateRequest = request ?? { txnGroups: [] } + + currentRequest.txnGroups = [ + { + txns: txnObjects, + }, + ] + + const simulateResponse = await client.simulateTransaction({ body: currentRequest }) + + // Parse method response + const methodResults: ABIResult[] = [] + for (const [txnIndex, method] of this.methodCalls) { + const txID = this.txIDs[txnIndex] + const pendingInfo = simulateResponse.txnGroups[0].txnResults[txnIndex].txnResult + + const methodResult: ABIResult = { + txID, + rawReturnValue: new Uint8Array(), + method, + } + + methodResults.push(AtomicTransactionComposer.parseMethodResponse(method, methodResult, pendingInfo)) + } + + return { methodResults, simulateResponse } + } + + /** + * Send the transaction group to the network and wait until it's committed to a block. An error + * will be thrown if submission or execution fails. + * + * The composer's status must be SUBMITTED or lower before calling this method, since execution is + * only allowed once. If submission is successful, this composer's status will update to SUBMITTED. + * If the execution is also successful, this composer's status will update to COMMITTED. + * + * Note: a group can only be submitted again if it fails. + * + * @param client - An Algodv2 client + * @param waitRounds - The maximum number of rounds to wait for transaction confirmation + * + * @returns A promise that, upon success, resolves to an object containing the confirmed round for + * this transaction, the txIDs of the submitted transactions, and an array of results containing + * one element for each method call transaction in this group. + */ + async execute( + client: AlgodClient, + waitRounds: number, + ): Promise<{ + confirmedRound: bigint + txIDs: string[] + methodResults: ABIResult[] + }> { + if (this.status === AtomicTransactionComposerStatus.COMMITTED) { + throw new Error('Transaction group has already been executed successfully') + } + + const txIDs = await this.submit(client) + this.status = AtomicTransactionComposerStatus.SUBMITTED + + const firstMethodCallIndex = this.transactions.findIndex((_, index) => this.methodCalls.has(index)) + const indexToWaitFor = firstMethodCallIndex === -1 ? 0 : firstMethodCallIndex + const confirmedTxnInfo = await waitForConfirmation(client, txIDs[indexToWaitFor], waitRounds) + this.status = AtomicTransactionComposerStatus.COMMITTED + + const confirmedRound = confirmedTxnInfo.confirmedRound! + + const methodResults: ABIResult[] = [] + + for (const [txnIndex, method] of this.methodCalls) { + const txID = txIDs[txnIndex] + + let methodResult: ABIResult = { + txID, + rawReturnValue: new Uint8Array(), + method, + } + + try { + const pendingInfo = txnIndex === firstMethodCallIndex ? confirmedTxnInfo : await client.pendingTransactionInformation(txID) + + methodResult = AtomicTransactionComposer.parseMethodResponse(method, methodResult, pendingInfo) + } catch (err) { + methodResult.decodeError = err as Error + } + + methodResults.push(methodResult) + } + + return { + confirmedRound, + txIDs, + methodResults, + } + } + + /** + * Parses a single ABI Method transaction log into a ABI result object. + * + * @param method + * @param methodResult + * @param pendingInfo + * @returns An ABIResult object + */ + static parseMethodResponse(method: ABIMethod, methodResult: ABIResult, pendingInfo: PendingTransactionResponse): ABIResult { + const returnedResult: ABIResult = methodResult + try { + returnedResult.txInfo = pendingInfo + if (method.returns.type !== 'void') { + const logs = pendingInfo.logs || [] + if (logs.length === 0) { + throw new Error(`App call transaction did not log a return value ${stringifyJSON(pendingInfo)}`) + } + const lastLog = logs[logs.length - 1] + if (lastLog.byteLength < 4 || !arrayEqual(lastLog.slice(0, 4), RETURN_PREFIX)) { + throw new Error(`App call transaction did not log a ABI return value ${stringifyJSON(pendingInfo)}`) + } + + returnedResult.rawReturnValue = new Uint8Array(lastLog.slice(4)) + returnedResult.returnValue = method.returns.type.decode(methodResult.rawReturnValue) + } + } catch (err) { + returnedResult.decodeError = err as Error + } + + return returnedResult + } +} diff --git a/packages/sdk/src/convert.ts b/packages/sdk/src/convert.ts new file mode 100644 index 00000000..54f9b056 --- /dev/null +++ b/packages/sdk/src/convert.ts @@ -0,0 +1,25 @@ +const MICROALGOS_TO_ALGOS_RATIO = 1e6; +export const INVALID_MICROALGOS_ERROR_MSG = + 'Microalgos should be positive and less than 2^53 - 1.'; + +/** + * microalgosToAlgos converts microalgos to algos + * @param microalgos - number + * @returns number + */ +export function microalgosToAlgos(microalgos: number) { + if (microalgos < 0 || !Number.isSafeInteger(microalgos)) { + throw new Error(INVALID_MICROALGOS_ERROR_MSG); + } + return microalgos / MICROALGOS_TO_ALGOS_RATIO; +} + +/** + * algosToMicroalgos converts algos to microalgos + * @param algos - number + * @returns number + */ +export function algosToMicroalgos(algos: number) { + const microalgos = algos * MICROALGOS_TO_ALGOS_RATIO; + return Math.round(microalgos); +} diff --git a/packages/sdk/src/encoding/address.ts b/packages/sdk/src/encoding/address.ts new file mode 100644 index 00000000..77a2f750 --- /dev/null +++ b/packages/sdk/src/encoding/address.ts @@ -0,0 +1,180 @@ +import base32 from 'hi-base32'; +import * as nacl from '../nacl/naclWrappers.js'; +import * as utils from '../utils/utils.js'; +import { encodeUint64 } from './uint64.js'; +import { bytesToHex } from './binarydata.js'; + +export const ALGORAND_ADDRESS_BYTE_LENGTH = 36; +export const ALGORAND_CHECKSUM_BYTE_LENGTH = 4; +export const ALGORAND_ADDRESS_LENGTH = 58; +export const ALGORAND_ZERO_ADDRESS_STRING = + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ'; + +export const MALFORMED_ADDRESS_ERROR_MSG = 'address seems to be malformed'; +export const CHECKSUM_ADDRESS_ERROR_MSG = 'wrong checksum for address'; + +function checksumFromPublicKey(pk: Uint8Array): Uint8Array { + return Uint8Array.from( + nacl + .genericHash(pk) + .slice( + nacl.HASH_BYTES_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH, + nacl.HASH_BYTES_LENGTH + ) + ); +} + +/** + * Represents an Algorand address + */ +export class Address { + /** + * The binary form of the address. For standard accounts, this is the public key. + */ + public readonly publicKey: Uint8Array; + + /** + * Create a new Address object from its binary form. + * @param publicKey - The binary form of the address. Must be 32 bytes. + */ + constructor(publicKey: Uint8Array) { + if (!(publicKey instanceof Uint8Array)) { + throw new Error( + `${MALFORMED_ADDRESS_ERROR_MSG}: ${publicKey} is not Uint8Array, type ${typeof publicKey}` + ); + } + if ( + publicKey.length !== + ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH + ) + throw new Error( + `${MALFORMED_ADDRESS_ERROR_MSG}: 0x${bytesToHex(publicKey)}, length ${publicKey.length}` + ); + this.publicKey = publicKey; + } + + /** + * Check if the address is equal to another address. + */ + equals(other: Address): boolean { + return ( + other instanceof Address && + utils.arrayEqual(this.publicKey, other.publicKey) + ); + } + + /** + * Compute the 4 byte checksum of the address. + */ + checksum(): Uint8Array { + return checksumFromPublicKey(this.publicKey); + } + + /** + * Encode the address into a string form. + */ + toString(): string { + const addr = base32.encode( + utils.concatArrays(this.publicKey, this.checksum()) + ); + return addr.slice(0, ALGORAND_ADDRESS_LENGTH); // removing the extra '====' + } + + /** + * Decode an address from a string. + * @param address - The address to decode. Must be 58 bytes long. + * @returns An Address object corresponding to the input string. + */ + static fromString(address: string): Address { + if (typeof address !== 'string') + throw new Error( + `${MALFORMED_ADDRESS_ERROR_MSG}: expected string, got ${typeof address}, ${address}` + ); + if (address.length !== ALGORAND_ADDRESS_LENGTH) + throw new Error( + `${MALFORMED_ADDRESS_ERROR_MSG}: expected length ${ALGORAND_ADDRESS_LENGTH}, got ${address.length}: ${address}` + ); + + // try to decode + const decoded = base32.decode.asBytes(address); + // Sanity check + if (decoded.length !== ALGORAND_ADDRESS_BYTE_LENGTH) + throw new Error( + `${MALFORMED_ADDRESS_ERROR_MSG}: expected byte length ${ALGORAND_ADDRESS_BYTE_LENGTH}, got ${decoded.length}` + ); + + // Find publickey and checksum + const pk = new Uint8Array( + decoded.slice( + 0, + ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH + ) + ); + const cs = new Uint8Array( + decoded.slice(nacl.PUBLIC_KEY_LENGTH, ALGORAND_ADDRESS_BYTE_LENGTH) + ); + const checksum = checksumFromPublicKey(pk); + // Check if the checksum and the address are equal + if (!utils.arrayEqual(checksum, cs)) + throw new Error(CHECKSUM_ADDRESS_ERROR_MSG); + + return new Address(pk); + } + + /** + * Get the zero address. + */ + static zeroAddress(): Address { + return new Address( + new Uint8Array( + ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH + ) + ); + } +} + +/** + * decodeAddress takes an Algorand address in string form and decodes it into a Uint8Array. + * @param address - an Algorand address with checksum. + * @returns the decoded form of the address's public key and checksum + */ +export function decodeAddress(address: string): Address { + return Address.fromString(address); +} + +/** + * isValidAddress checks if a string is a valid Algorand address. + * @param address - an Algorand address with checksum. + * @returns true if valid, false otherwise + */ +export function isValidAddress(address: string): boolean { + // Try to decode + try { + Address.fromString(address); + } catch (e) { + return false; + } + return true; +} + +/** + * encodeAddress takes an Algorand address as a Uint8Array and encodes it into a string with checksum. + * @param address - a raw Algorand address + * @returns the address and checksum encoded as a string. + */ +export function encodeAddress(address: Uint8Array): string { + return new Address(address).toString(); +} + +const APP_ID_PREFIX = new TextEncoder().encode('appID'); + +/** + * Get the escrow address of an application. + * @param appID - The ID of the application. + * @returns The address corresponding to that application's escrow account. + */ +export function getApplicationAddress(appID: number | bigint): Address { + const toBeSigned = utils.concatArrays(APP_ID_PREFIX, encodeUint64(appID)); + const hash = nacl.genericHash(toBeSigned); + return new Address(Uint8Array.from(hash)); +} diff --git a/packages/sdk/src/encoding/bigint.ts b/packages/sdk/src/encoding/bigint.ts new file mode 100644 index 00000000..b29301a4 --- /dev/null +++ b/packages/sdk/src/encoding/bigint.ts @@ -0,0 +1,33 @@ +/** + * bigIntToBytes converts a BigInt to a big-endian Uint8Array for encoding. + * @param bi - The bigint to convert. + * @param size - The size of the resulting byte array. + * @returns A byte array containing the big-endian encoding of the input bigint + */ +export function bigIntToBytes(bi: bigint | number, size: number) { + let hex = bi.toString(16); + // Pad the hex with zeros so it matches the size in bytes + if (hex.length !== size * 2) { + hex = hex.padStart(size * 2, '0'); + } + const byteArray = new Uint8Array(hex.length / 2); + for (let i = 0, j = 0; i < hex.length / 2; i++, j += 2) { + byteArray[i] = parseInt(hex.slice(j, j + 2), 16); + } + return byteArray; +} + +/** + * bytesToBigInt produces a bigint from a binary representation. + * + * @param bytes - The Uint8Array to convert. + * @returns The bigint that was encoded in the input data. + */ +export function bytesToBigInt(bytes: Uint8Array) { + let res = BigInt(0); + const buf = new DataView(bytes.buffer, bytes.byteOffset); + for (let i = 0; i < bytes.length; i++) { + res = BigInt(Number(buf.getUint8(i))) + res * BigInt(256); + } + return res; +} diff --git a/packages/sdk/src/encoding/binarydata.ts b/packages/sdk/src/encoding/binarydata.ts new file mode 100644 index 00000000..1a6e726f --- /dev/null +++ b/packages/sdk/src/encoding/binarydata.ts @@ -0,0 +1,80 @@ +import { isNode } from '../utils/utils.js'; + +/** + * Convert a base64 string to a Uint8Array for Node.js and browser environments. + * @returns A Uint8Array + */ +export function base64ToBytes(base64String: string): Uint8Array { + if (isNode()) { + return new Uint8Array(Buffer.from(base64String, 'base64')); + } + /* eslint-env browser */ + const binString = atob(base64String); + return Uint8Array.from(binString, (m) => m.codePointAt(0)!); +} + +/** + * Convert a Uint8Array to a base64 string for Node.js and browser environments. + * @returns A base64 string + */ +export function bytesToBase64(byteArray: Uint8Array): string { + if (isNode()) { + return Buffer.from(byteArray).toString('base64'); + } + /* eslint-env browser */ + const binString = Array.from(byteArray, (x) => String.fromCodePoint(x)).join( + '' + ); + return btoa(binString); +} + +/** + * Convert a byte array to a UTF-8 string. Warning: not all byte arrays are valid UTF-8. + * @returns A decoded string + */ +export function bytesToString(byteArray: Uint8Array): string { + return new TextDecoder().decode(byteArray); +} + +/** + * Returns a Uint8Array given an input string or Uint8Array. + * @returns A base64 string + */ +export function coerceToBytes(input: Uint8Array | string): Uint8Array { + if (typeof input === 'string') { + return new TextEncoder().encode(input); + } + return input; +} + +/** + * Convert a Uint8Array to a hex string for Node.js and browser environments. + * @returns A hex string + */ +export function bytesToHex(byteArray: Uint8Array): string { + if (isNode()) { + return Buffer.from(byteArray).toString('hex'); + } + return Array.from(byteArray) + .map((i) => i.toString(16).padStart(2, '0')) + .join(''); +} + +/** + * Convert a hex string to Uint8Array for Node.js and browser environments. + * @returns A Uint8Array + */ +export function hexToBytes(hexString: string): Uint8Array { + if (isNode()) { + return Buffer.from(hexString, 'hex'); + } + let hex = hexString; + if (hexString.length % 2 !== 0) { + hex = hexString.padStart(1, '0'); + } + const byteArray = new Uint8Array(hex.length / 2); + for (let i = 0; i < hex.length / 2; i++) { + byteArray[i] = parseInt(hex.slice(2 * i, 2 * i + 2), 16); + } + return byteArray; +} diff --git a/packages/sdk/src/encoding/encoding.ts b/packages/sdk/src/encoding/encoding.ts new file mode 100644 index 00000000..e5406ec8 --- /dev/null +++ b/packages/sdk/src/encoding/encoding.ts @@ -0,0 +1,546 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/** + * This file is a wrapper of msgpack.js. + * The wrapper was written in order to ensure correct encoding of Algorand Transaction and other formats. + * In particular, it matches go-algorand blockchain client, written in go (https://www.github.com/algorand/go-algorand. + * Algorand's msgpack encoding follows to following rules - + * 1. Every integer must be encoded to the smallest type possible (0-255-\>8bit, 256-65535-\>16bit, etx) + * 2. All fields names must be sorted + * 3. All empty and 0 fields should be omitted + * 4. Every positive number must be encoded as uint + * 5. Binary blob should be used for binary data and string for strings + * */ + +import { + DecoderOptions, + EncoderOptions, + IntMode, + RawBinaryString, + decode as msgpackDecode, + encode as msgpackEncode, +} from 'algorand-msgpack' +import IntDecoding from '../types/intDecoding.js' +import { arrayEqual, parseJSON, stringifyJSON } from '../utils/utils.js' +import { bytesToBase64, coerceToBytes } from './binarydata.js' + +// Errors +export const ERROR_CONTAINS_EMPTY_STRING = 'The object contains empty or 0 values. First empty or 0 value encountered during encoding: ' + +/** + * containsEmpty returns true if any of the object's values are empty, false otherwise. + * Empty arrays considered empty + * @param obj - The object to check + * @returns \{true, empty key\} if contains empty, \{false, undefined\} otherwise + */ +function containsEmpty(obj: Record) { + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + if (!obj[key] || obj[key].length === 0) { + return { containsEmpty: true, firstEmptyKey: key } + } + } + } + return { containsEmpty: false, firstEmptyKey: undefined } +} + +/** + * msgpackRawEncode encodes objects using msgpack, regardless of whether there are + * empty or 0 value fields. + * @param obj - a dictionary to be encoded. May or may not contain empty or 0 values. + * @returns msgpack representation of the object + */ +export function msgpackRawEncode(obj: unknown) { + // enable the canonical option + const options: EncoderOptions = { sortKeys: true } + return msgpackEncode(obj, options) +} + +/** + * encodeObj takes a javascript object and returns its msgpack encoding + * Note that the encoding sorts the fields alphabetically + * @param o - js object to be encoded. Must not contain empty or 0 values. + * @returns Uint8Array binary representation + * @throws Error containing ERROR_CONTAINS_EMPTY_STRING if the object contains empty or zero values + * + * @deprecated Use {@link msgpackRawEncode} instead. Note that function does not + * check for empty values like this one does. + */ +export function encodeObj(obj: Record) { + // Check for empty values + const emptyCheck = containsEmpty(obj) + if (emptyCheck.containsEmpty) { + throw new Error(ERROR_CONTAINS_EMPTY_STRING + emptyCheck.firstEmptyKey) + } + return msgpackRawEncode(obj) +} + +function intDecodingToIntMode(intDecoding: IntDecoding): IntMode { + switch (intDecoding) { + case IntDecoding.UNSAFE: + return IntMode.UNSAFE_NUMBER + case IntDecoding.SAFE: + return IntMode.SAFE_NUMBER + case IntDecoding.MIXED: + return IntMode.MIXED + case IntDecoding.BIGINT: + return IntMode.BIGINT + default: + throw new Error(`Invalid intDecoding: ${intDecoding}`) + } +} + +/** + * Decodes msgpack bytes into a plain JavaScript object. + * @param buffer - The msgpack bytes to decode + * @param options - Options for decoding, including int decoding mode. See {@link IntDecoding} for more information. + * @returns The decoded object + */ +export function msgpackRawDecode(buffer: ArrayLike, options?: { intDecoding: IntDecoding }) { + const decoderOptions: DecoderOptions = { + intMode: options?.intDecoding ? intDecodingToIntMode(options?.intDecoding) : IntMode.BIGINT, + } + return msgpackDecode(buffer, decoderOptions) +} + +/** + * decodeObj takes a Uint8Array and returns its javascript obj + * @param o - Uint8Array to decode + * @returns object + * + * @deprecated Use {@link msgpackRawDecode} instead. Note that this function uses `IntDecoding.MIXED` + * while `msgpackRawDecode` defaults to `IntDecoding.BIGINT` for int decoding, though it is + * configurable. + */ +export function decodeObj(o: ArrayLike) { + return msgpackRawDecode(o, { intDecoding: IntDecoding.MIXED }) +} + +/** + * Decodes msgpack bytes into a Map object. This supports decoding non-string map keys. + * @param encoded - The msgpack bytes to decode + * @param options - Options for decoding, including int decoding mode. See {@link IntDecoding} for more information. + * @returns The decoded Map object + */ +export function msgpackRawDecodeAsMap(encoded: ArrayLike, options?: { intDecoding: IntDecoding }) { + const decoderOptions: DecoderOptions = { + intMode: options?.intDecoding ? intDecodingToIntMode(options?.intDecoding) : IntMode.BIGINT, + useMap: true, + } + return msgpackDecode(encoded, decoderOptions) +} + +function msgpackRawDecodeAsMapWithRawStrings(encoded: ArrayLike, options?: { intDecoding: IntDecoding }) { + const decoderOptions: DecoderOptions = { + intMode: options?.intDecoding ? intDecodingToIntMode(options?.intDecoding) : IntMode.BIGINT, + useMap: true, + rawBinaryStringKeys: true, + rawBinaryStringValues: true, + useRawBinaryStringClass: true, + } + return msgpackDecode(encoded, decoderOptions) +} + +export type MsgpackEncodingData = + | null + | undefined + | string + | number + | bigint + | boolean + | Uint8Array + | MsgpackEncodingData[] + | Map + +export type JSONEncodingData = + | null + | undefined + | string + | number + | bigint + | boolean + | JSONEncodingData[] + | { [key: string]: JSONEncodingData } + +export function msgpackEncodingDataToJSONEncodingData(e: MsgpackEncodingData): JSONEncodingData { + if (e === null || e === undefined) { + return e as JSONEncodingData + } + if (e instanceof Uint8Array) { + return bytesToBase64(e) + } + if (Array.isArray(e)) { + return e.map(msgpackEncodingDataToJSONEncodingData) + } + if (e instanceof Map) { + const obj: { [key: string]: JSONEncodingData } = {} + for (const [k, v] of e) { + if (typeof k !== 'string') { + throw new Error(`JSON map key must be a string: ${k}`) + } + obj[k] = msgpackEncodingDataToJSONEncodingData(v) + } + return obj + } + return e +} + +export function jsonEncodingDataToMsgpackEncodingData(e: JSONEncodingData): MsgpackEncodingData { + if (e === null || e === undefined) { + return e as MsgpackEncodingData + } + if ( + typeof e === 'string' || // Note, this will not convert base64 to Uint8Array + typeof e === 'number' || + typeof e === 'bigint' || + typeof e === 'boolean' + ) { + return e + } + if (Array.isArray(e)) { + return e.map(jsonEncodingDataToMsgpackEncodingData) + } + if (typeof e === 'object') { + const obj = new Map() + for (const [key, value] of Object.entries(e)) { + obj.set(key, jsonEncodingDataToMsgpackEncodingData(value)) + } + return obj + } + throw new Error(`Invalid JSON encoding data: ${e}`) +} + +enum MsgpackObjectPathSegmentKind { + MAP_VALUE, + ARRAY_ELEMENT, +} + +interface MsgpackObjectPathSegment { + kind: MsgpackObjectPathSegmentKind + key: string | number | bigint | Uint8Array | RawBinaryString +} + +/** + * This class is used to index into an encoded msgpack object and extract raw strings. + */ +export class MsgpackRawStringProvider { + private readonly parent?: MsgpackRawStringProvider + + private readonly baseObjectBytes?: ArrayLike + + private readonly segment?: MsgpackObjectPathSegment + + private resolvedCache: MsgpackEncodingData = null + private resolvedCachePresent = false + + public constructor({ + parent, + segment, + baseObjectBytes, + }: + | { + parent: MsgpackRawStringProvider + segment: MsgpackObjectPathSegment + baseObjectBytes?: undefined + } + | { + parent?: undefined + segment?: undefined + baseObjectBytes: ArrayLike + }) { + this.parent = parent + this.segment = segment + this.baseObjectBytes = baseObjectBytes + } + + /** + * Create a new provider that resolves to the current provider's map value at the given key. + */ + public withMapValue(key: string | number | bigint | Uint8Array | RawBinaryString): MsgpackRawStringProvider { + return new MsgpackRawStringProvider({ + parent: this, + segment: { + kind: MsgpackObjectPathSegmentKind.MAP_VALUE, + key, + }, + }) + } + + /** + * Create a new provider that resolves to the current provider's array element at the given index. + */ + public withArrayElement(index: number): MsgpackRawStringProvider { + return new MsgpackRawStringProvider({ + parent: this, + segment: { + kind: MsgpackObjectPathSegmentKind.ARRAY_ELEMENT, + key: index, + }, + }) + } + + /** + * Get the raw string at the current location. If the current location is not a raw string, an error is thrown. + */ + public getRawStringAtCurrentLocation(): Uint8Array { + const resolved = this.resolve() + if (resolved instanceof RawBinaryString) { + // Decoded rawBinaryValue will always be a Uint8Array + return resolved.rawBinaryValue as Uint8Array + } + throw new Error(`Invalid type. Expected RawBinaryString, got ${resolved} (${typeof resolved})`) + } + + /** + * Get the raw string map keys and values at the current location. If the current location is not a map, an error is thrown. + */ + public getRawStringKeysAndValuesAtCurrentLocation(): Map { + const resolved = this.resolve() + if (!(resolved instanceof Map)) { + throw new Error(`Invalid type. Expected Map, got ${resolved} (${typeof resolved})`) + } + const keysAndValues = new Map() + for (const [key, value] of resolved) { + if (key instanceof RawBinaryString) { + // Decoded rawBinaryValue will always be a Uint8Array + keysAndValues.set(key.rawBinaryValue as Uint8Array, value) + } else { + throw new Error(`Invalid type for map key. Expected RawBinaryString, got ${key} (${typeof key})`) + } + } + return keysAndValues + } + + /** + * Resolve the provider by extracting the value it indicates from the base msgpack object. + */ + private resolve(): MsgpackEncodingData { + if (this.resolvedCachePresent) { + return this.resolvedCache + } + let parentResolved: MsgpackEncodingData + if (this.parent) { + parentResolved = this.parent.resolve() + } else { + // Need to parse baseObjectBytes + parentResolved = msgpackRawDecodeAsMapWithRawStrings(this.baseObjectBytes!) as MsgpackEncodingData + } + if (!this.segment) { + this.resolvedCache = parentResolved + this.resolvedCachePresent = true + return parentResolved + } + if (this.segment.kind === MsgpackObjectPathSegmentKind.MAP_VALUE) { + if (!(parentResolved instanceof Map)) { + throw new Error(`Invalid type. Expected Map, got ${parentResolved} (${typeof parentResolved})`) + } + // All decoded map keys will be raw strings, and Map objects compare complex values by reference, + // so we must check all the values for value-equality. + if (typeof this.segment.key === 'string' || this.segment.key instanceof Uint8Array || this.segment.key instanceof RawBinaryString) { + const targetBytes = + this.segment.key instanceof RawBinaryString + ? // Decoded rawBinaryValue will always be a Uint8Array + (this.segment.key.rawBinaryValue as Uint8Array) + : coerceToBytes(this.segment.key) + const targetIsRawString = typeof this.segment.key === 'string' || this.segment.key instanceof RawBinaryString + for (const [key, value] of parentResolved) { + let potentialKeyBytes: Uint8Array | undefined + if (targetIsRawString) { + if (key instanceof RawBinaryString) { + // Decoded rawBinaryValue will always be a Uint8Array + potentialKeyBytes = key.rawBinaryValue as Uint8Array + } + } else if (key instanceof Uint8Array) { + potentialKeyBytes = key + } + if (potentialKeyBytes && arrayEqual(targetBytes, potentialKeyBytes)) { + this.resolvedCache = value + break + } + } + } else { + this.resolvedCache = parentResolved.get(this.segment.key) + } + this.resolvedCachePresent = true + return this.resolvedCache + } + if (this.segment.kind === MsgpackObjectPathSegmentKind.ARRAY_ELEMENT) { + if (!Array.isArray(parentResolved)) { + throw new Error(`Invalid type. Expected Array, got ${parentResolved} (${typeof parentResolved})`) + } + this.resolvedCache = parentResolved[this.segment.key as number] + this.resolvedCachePresent = true + return this.resolvedCache + } + throw new Error(`Invalid segment kind: ${this.segment.kind}`) + } + + /** + * Get the path string of the current location indicated by the provider. Useful for debugging. + */ + public getPathString(): string { + const parentPathString = this.parent ? this.parent.getPathString() : 'root' + if (!this.segment) { + return parentPathString + } + if (this.segment.kind === MsgpackObjectPathSegmentKind.MAP_VALUE) { + return `${parentPathString} -> map key "${this.segment.key}" (${typeof this.segment.key})` + } + if (this.segment.kind === MsgpackObjectPathSegmentKind.ARRAY_ELEMENT) { + return `${parentPathString} -> array index ${this.segment.key} (${typeof this.segment.key})` + } + return `${parentPathString} -> unknown segment kind ${this.segment.kind}` + } +} + +/** + * Options for {@link Schema.prepareJSON} + */ +export interface PrepareJSONOptions { + /** + * If true, allows invalid UTF-8 binary strings to be converted to JSON strings. + * + * Otherwise, an error will be thrown if encoding a binary string to a JSON cannot be done losslessly. + */ + lossyBinaryStringConversion?: boolean +} + +/** + * A Schema is used to prepare objects for encoding and decoding from msgpack and JSON. + * + * Schemas represent a specific type. + */ +export abstract class Schema { + /** + * Get the default value for this type. + */ + public abstract defaultValue(): unknown + + /** + * Checks if the value is the default value for this type. + * @param data - The value to check + * @returns True if the value is the default value, false otherwise + */ + public abstract isDefaultValue(data: unknown): boolean + + /** + * Prepares the encoding data for encoding to msgpack. + * @param data - Encoding data to be prepared. + * @returns A value ready to be msgpack encoded. + */ + public abstract prepareMsgpack(data: unknown): MsgpackEncodingData + + /** + * Restores the encoding data from a msgpack encoding object. + * @param encoded - The msgpack encoding object to restore. + * @param rawStringProvider - A provider for raw strings. + * @returns The original encoding data. + */ + public abstract fromPreparedMsgpack(encoded: MsgpackEncodingData, rawStringProvider: MsgpackRawStringProvider): unknown + + /** + * Prepares the encoding data for encoding to JSON. + * @param data - The JSON encoding data to be prepared. + * @returns A value ready to be JSON encoded. + */ + public abstract prepareJSON(data: unknown, options: PrepareJSONOptions): JSONEncodingData + + /** + * Restores the encoding data from a JSON encoding object. + * @param encoded - The JSON encoding object to restore. + * @returns The original encoding data. + */ + public abstract fromPreparedJSON(encoded: JSONEncodingData): unknown +} + +/** + * An interface for objects that can be encoded and decoded to/from msgpack and JSON. + */ +export interface Encodable { + /** + * Extract the encoding data for this object. This data, after being prepared by the encoding + * Schema, can be encoded to msgpack or JSON. + */ + toEncodingData(): unknown + /** + * Get the encoding Schema for this object, used to prepare the encoding data for msgpack and JSON. + */ + getEncodingSchema(): Schema +} + +/** + * A type that represents the class of an Encodable object. + */ +export interface EncodableClass { + /** + * Create a new instance of this class from the given encoding data. + * @param data - The encoding data to create the object from + */ + fromEncodingData(data: unknown): T + /** + * The encoding Schema for this class, used to prepare encoding data from msgpack and JSON. + */ + readonly encodingSchema: Schema +} + +/** + * Decode a msgpack byte array to an Encodable object. + * @param encoded - The msgpack bytes to decode + * @param c - The class of the object to decode. This class must match the object that was encoded. + * @returns An instance of the class with the decoded data + */ +export function decodeMsgpack(encoded: ArrayLike, c: EncodableClass): T { + const decoded = msgpackRawDecodeAsMap(encoded) as MsgpackEncodingData + const rawStringProvider = new MsgpackRawStringProvider({ + baseObjectBytes: encoded, + }) + return c.fromEncodingData(c.encodingSchema.fromPreparedMsgpack(decoded, rawStringProvider)) +} + +/** + * Encode an Encodable object to a msgpack byte array. + * @param e - The object to encode + * @returns A msgpack byte array encoding of the object + */ +export function encodeMsgpack(e: Encodable): Uint8Array { + return msgpackRawEncode(e.getEncodingSchema().prepareMsgpack(e.toEncodingData())) +} + +/** + * Decode a JSON string to an Encodable object. + * @param encoded - The JSON string to decode + * @param c - The class of the object to decode. This class must match the object that was encoded. + * @returns An instance of the class with the decoded data + */ +export function decodeJSON(encoded: string, c: EncodableClass): T { + const decoded: JSONEncodingData = parseJSON(encoded, { + intDecoding: IntDecoding.BIGINT, + }) + return c.fromEncodingData(c.encodingSchema.fromPreparedJSON(decoded) as JSONEncodingData) +} + +export interface EncodeJSONOptions { + /** + * Adds indentation, white space, and line break characters to the return-value JSON text to make + * it easier to read. + */ + space?: string | number + + /** + * If true, allows invalid UTF-8 binary strings to be converted to JSON strings. + * + * Otherwise, an error will be thrown if encoding a binary string to a JSON cannot be done losslessly. + */ + lossyBinaryStringConversion?: boolean +} + +/** + * Encode an Encodable object to a JSON string. + * @param e - The object to encode + * @param options - Optional encoding options. See {@link EncodeJSONOptions} for more information. + * @returns A JSON string encoding of the object + */ +export function encodeJSON(e: Encodable, options?: EncodeJSONOptions): string { + const { space, ...prepareJSONOptions } = options ?? {} + const prepared = e.getEncodingSchema().prepareJSON(e.toEncodingData(), prepareJSONOptions) + return stringifyJSON(prepared, undefined, space) +} diff --git a/packages/sdk/src/encoding/schema/address.ts b/packages/sdk/src/encoding/schema/address.ts new file mode 100644 index 00000000..3eb7f5c7 --- /dev/null +++ b/packages/sdk/src/encoding/schema/address.ts @@ -0,0 +1,53 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; +import { Address } from '../address.js'; + +/* eslint-disable class-methods-use-this */ + +export class AddressSchema extends Schema { + public defaultValue(): Address { + return Address.zeroAddress(); + } + + public isDefaultValue(data: unknown): boolean { + // The equals method checks if the input is an Address + return Address.zeroAddress().equals(data as Address); + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (data instanceof Address) { + return data.publicKey; + } + throw new Error(`Invalid address: (${typeof data}) ${data}`); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): Address { + // The Address constructor checks that the input is a Uint8Array + return new Address(encoded as Uint8Array); + } + + public prepareJSON( + data: unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + if (data instanceof Address) { + return data.toString(); + } + throw new Error(`Invalid address: (${typeof data}) ${data}`); + } + + public fromPreparedJSON(encoded: JSONEncodingData): Address { + // The Address.fromString method checks that the input is a string + return Address.fromString(encoded as string); + } +} diff --git a/packages/sdk/src/encoding/schema/array.ts b/packages/sdk/src/encoding/schema/array.ts new file mode 100644 index 00000000..1a90571b --- /dev/null +++ b/packages/sdk/src/encoding/schema/array.ts @@ -0,0 +1,66 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; + +/* eslint-disable class-methods-use-this */ + +export class ArraySchema extends Schema { + constructor(public readonly itemSchema: Schema) { + super(); + } + + public defaultValue(): unknown[] { + return []; + } + + public isDefaultValue(data: unknown): boolean { + return Array.isArray(data) && data.length === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (Array.isArray(data)) { + return data.map((item) => this.itemSchema.prepareMsgpack(item)); + } + throw new Error('ArraySchema data must be an array'); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): unknown[] { + if (Array.isArray(encoded)) { + return encoded.map((item, index) => + this.itemSchema.fromPreparedMsgpack( + item, + rawStringProvider.withArrayElement(index) + ) + ); + } + throw new Error( + `ArraySchema encoded data must be an array: ${encoded} (${typeof encoded})` + ); + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (Array.isArray(data)) { + return data.map((item) => this.itemSchema.prepareJSON(item, options)); + } + throw new Error('ArraySchema data must be an array'); + } + + public fromPreparedJSON(encoded: JSONEncodingData): unknown[] { + if (Array.isArray(encoded)) { + return encoded.map((item) => this.itemSchema.fromPreparedJSON(item)); + } + throw new Error( + `ArraySchema encoded data must be an array: ${encoded} (${typeof encoded})` + ); + } +} diff --git a/packages/sdk/src/encoding/schema/binarystring.ts b/packages/sdk/src/encoding/schema/binarystring.ts new file mode 100644 index 00000000..01308732 --- /dev/null +++ b/packages/sdk/src/encoding/schema/binarystring.ts @@ -0,0 +1,73 @@ +import { RawBinaryString } from 'algorand-msgpack'; +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; +import { coerceToBytes, bytesToString, bytesToBase64 } from '../binarydata.js'; +import { arrayEqual } from '../../utils/utils.js'; + +/* eslint-disable class-methods-use-this */ + +/** + * SpecialCaseBinaryStringSchema is a schema for byte arrays which are encoded + * as strings in msgpack and JSON. + * + * This schema allows lossless conversion between the in memory representation + * and the msgpack encoded representation, but NOT between the in memory and + * JSON encoded representations if the byte array contains invalid UTF-8 + * sequences. + */ +export class SpecialCaseBinaryStringSchema extends Schema { + public defaultValue(): Uint8Array { + return new Uint8Array(); + } + + public isDefaultValue(data: unknown): boolean { + return data instanceof Uint8Array && data.byteLength === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (data instanceof Uint8Array) { + // Cast is needed because RawBinaryString is not part of the standard MsgpackEncodingData + return new RawBinaryString(data) as unknown as MsgpackEncodingData; + } + throw new Error(`Invalid byte array: (${typeof data}) ${data}`); + } + + public fromPreparedMsgpack( + _encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): Uint8Array { + return rawStringProvider.getRawStringAtCurrentLocation(); + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (data instanceof Uint8Array) { + // Not safe to convert to string for all binary data + const stringValue = bytesToString(data); + if ( + !options.lossyBinaryStringConversion && + !arrayEqual(coerceToBytes(stringValue), data) + ) { + throw new Error( + `Invalid UTF-8 byte array encountered. Encode with lossyBinaryStringConversion enabled to bypass this check. Base64 value: ${bytesToBase64(data)}` + ); + } + return stringValue; + } + throw new Error(`Invalid byte array: (${typeof data}) ${data}`); + } + + public fromPreparedJSON(encoded: JSONEncodingData): Uint8Array { + if (typeof encoded === 'string') { + return coerceToBytes(encoded); + } + throw new Error(`Invalid byte array: (${typeof encoded}) ${encoded}`); + } +} diff --git a/packages/sdk/src/encoding/schema/blockhash.ts b/packages/sdk/src/encoding/schema/blockhash.ts new file mode 100644 index 00000000..5c82dd4f --- /dev/null +++ b/packages/sdk/src/encoding/schema/blockhash.ts @@ -0,0 +1,84 @@ +import base32 from 'hi-base32'; +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; + +/** + * Length of a block hash in bytes + */ +const blockHashByteLength = 32; + +/* eslint-disable class-methods-use-this */ + +/** + * Length of a 32-byte encoded in base32 without padding + */ +const base32Length = 52; + +/** + * BlockHashSchema is a schema for block hashes. + * + * In msgapck, these types are encoded as 32-byte binary strings. In JSON, they + * are encoded as strings prefixed with "blk-" followed by the base32 encoding + * of the 32-byte block hash without any padding. + */ +export class BlockHashSchema extends Schema { + public defaultValue(): Uint8Array { + return new Uint8Array(blockHashByteLength); + } + + public isDefaultValue(data: unknown): boolean { + return ( + data instanceof Uint8Array && + data.byteLength === blockHashByteLength && + data.every((byte) => byte === 0) + ); + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (data instanceof Uint8Array && data.byteLength === blockHashByteLength) { + return data; + } + throw new Error(`Invalid block hash: (${typeof data}) ${data}`); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): Uint8Array { + if ( + encoded instanceof Uint8Array && + encoded.byteLength === blockHashByteLength + ) { + return encoded; + } + throw new Error(`Invalid block hash: (${typeof encoded}) ${encoded}`); + } + + public prepareJSON( + data: unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + if (data instanceof Uint8Array && data.byteLength === blockHashByteLength) { + return `blk-${base32.encode(data).slice(0, base32Length)}`; + } + throw new Error(`Invalid block hash: (${typeof data}) ${data}`); + } + + public fromPreparedJSON(encoded: JSONEncodingData): Uint8Array { + if ( + typeof encoded === 'string' && + encoded.length === base32Length + 4 && + encoded.startsWith('blk-') + ) { + return Uint8Array.from(base32.decode.asBytes(encoded.slice(4))); + } + throw new Error(`Invalid block hash: (${typeof encoded}) ${encoded}`); + } +} diff --git a/packages/sdk/src/encoding/schema/boolean.ts b/packages/sdk/src/encoding/schema/boolean.ts new file mode 100644 index 00000000..090a6754 --- /dev/null +++ b/packages/sdk/src/encoding/schema/boolean.ts @@ -0,0 +1,54 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; + +/* eslint-disable class-methods-use-this */ + +export class BooleanSchema extends Schema { + public defaultValue(): boolean { + return false; + } + + public isDefaultValue(data: unknown): boolean { + return data === false; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (typeof data === 'boolean') { + return data; + } + throw new Error('Invalid boolean'); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): boolean { + if (typeof encoded === 'boolean') { + return encoded; + } + throw new Error('Invalid boolean'); + } + + public prepareJSON( + data: unknown, // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + if (typeof data === 'boolean') { + return data; + } + throw new Error('Invalid boolean'); + } + + public fromPreparedJSON(encoded: JSONEncodingData): boolean { + if (typeof encoded === 'boolean') { + return encoded; + } + throw new Error('Invalid boolean'); + } +} diff --git a/packages/sdk/src/encoding/schema/bytearray.ts b/packages/sdk/src/encoding/schema/bytearray.ts new file mode 100644 index 00000000..11555aea --- /dev/null +++ b/packages/sdk/src/encoding/schema/bytearray.ts @@ -0,0 +1,130 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; +import { base64ToBytes, bytesToBase64 } from '../binarydata.js'; + +/* eslint-disable class-methods-use-this */ + +export class ByteArraySchema extends Schema { + public defaultValue(): Uint8Array { + return new Uint8Array(); + } + + public isDefaultValue(data: unknown): boolean { + return data instanceof Uint8Array && data.byteLength === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (data instanceof Uint8Array) { + return data; + } + throw new Error(`Invalid byte array: (${typeof data}) ${data}`); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): Uint8Array { + if (encoded instanceof Uint8Array) { + return encoded; + } + throw new Error(`Invalid byte array: (${typeof encoded}) ${encoded}`); + } + + public prepareJSON( + data: unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + if (data instanceof Uint8Array) { + return bytesToBase64(data); + } + throw new Error(`Invalid byte array: (${typeof data}) ${data}`); + } + + public fromPreparedJSON(encoded: JSONEncodingData): Uint8Array { + if (encoded === null || encoded === undefined) { + return this.defaultValue(); + } + if (typeof encoded === 'string') { + return base64ToBytes(encoded); + } + throw new Error(`Invalid byte array: (${typeof encoded}) ${encoded}`); + } +} + +export class FixedLengthByteArraySchema extends Schema { + constructor(public readonly length: number) { + super(); + } + + public defaultValue(): Uint8Array { + return new Uint8Array(this.length); + } + + public isDefaultValue(data: unknown): boolean { + return ( + data instanceof Uint8Array && + data.byteLength === this.length && + data.every((byte) => byte === 0) + ); + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (data instanceof Uint8Array) { + if (data.byteLength === this.length) { + return data; + } + throw new Error( + `Invalid byte array length: wanted ${this.length}, got ${data.byteLength}` + ); + } + throw new Error('Invalid byte array'); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): Uint8Array { + if (encoded instanceof Uint8Array) { + if (encoded.byteLength === this.length) { + return encoded; + } + throw new Error( + `Invalid byte array length: wanted ${this.length}, got ${encoded.byteLength}` + ); + } + throw new Error('Invalid byte array'); + } + + public prepareJSON(data: unknown): JSONEncodingData { + if (data instanceof Uint8Array) { + if (data.byteLength === this.length) { + return bytesToBase64(data); + } + throw new Error( + `Invalid byte array length: wanted ${this.length}, got ${data.byteLength}` + ); + } + throw new Error('Invalid byte array'); + } + + public fromPreparedJSON(encoded: JSONEncodingData): Uint8Array { + if (typeof encoded === 'string') { + const bytes = base64ToBytes(encoded); + if (bytes.byteLength === this.length) { + return bytes; + } + throw new Error( + `Invalid byte array length: wanted ${this.length}, got ${bytes.byteLength}` + ); + } + throw new Error('Invalid base64 byte array'); + } +} diff --git a/packages/sdk/src/encoding/schema/index.ts b/packages/sdk/src/encoding/schema/index.ts new file mode 100644 index 00000000..04e5476b --- /dev/null +++ b/packages/sdk/src/encoding/schema/index.ts @@ -0,0 +1,16 @@ +export { BooleanSchema } from './boolean.js' +export { StringSchema } from './string.js' +export { Uint64Schema } from './uint64.js' + +export { AddressSchema } from './address.js' +export { ByteArraySchema, FixedLengthByteArraySchema } from './bytearray.js' + +export { BlockHashSchema } from './blockhash.js' + +export { SpecialCaseBinaryStringSchema } from './binarystring.js' + +export { ArraySchema } from './array.js' +export * from './map.js' +export { OptionalSchema } from './optional.js' + +export { UntypedSchema } from './untyped.js' diff --git a/packages/sdk/src/encoding/schema/map.ts b/packages/sdk/src/encoding/schema/map.ts new file mode 100644 index 00000000..5c57dd6f --- /dev/null +++ b/packages/sdk/src/encoding/schema/map.ts @@ -0,0 +1,713 @@ +import { RawBinaryString } from 'algorand-msgpack'; +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; +import { ensureUint64, arrayEqual } from '../../utils/utils.js'; +import { + bytesToString, + coerceToBytes, + bytesToBase64, + base64ToBytes, +} from '../binarydata.js'; + +/* eslint-disable class-methods-use-this */ + +/** + * Describes a key-value entry in a NamedMapSchema. + */ +export interface NamedMapEntry { + /** + * Key of the entry. Must be unique for this map. + */ + key: string; + /** + * The Schema for the entry's value. + */ + valueSchema: Schema; + /** + * If true, the entry will be omitted from the encoding if the value is the default value. + */ + omitEmpty: boolean; + /** + * If true, valueSchema must be a NamedMapSchema and key must be the empty string. The fields of + * valueSchema will be embedded directly in the parent map. + * + * omitEmpty is ignored for embedded entries. Instead, the individual omitEmpty values of the + * embedded fields are used. + */ + embedded?: boolean; +} + +/** + * Applies the omitEmpty flag to all entries in the array. + * @param entries - The entries to apply the flag to. + * @returns A new array with the omitEmpty flag applied to all entries. + */ +export function allOmitEmpty( + entries: Array> +): NamedMapEntry[] { + return entries.map((entry) => ({ ...entry, omitEmpty: true })); +} + +/** + * Schema for a map/struct with a fixed set of known string fields. + */ +export class NamedMapSchema extends Schema { + private readonly entries: NamedMapEntry[]; + + constructor(entries: NamedMapEntry[]) { + super(); + this.entries = entries; + this.checkEntries(); + } + + /** + * Adds new entries to the map schema. WARNING: this is a mutable operation, and you should be very + * careful when using it. Any error that happens here is non-recoverable and will corrupt the + * NamedMapSchema object; + * @param entries - The entries to add. + */ + public pushEntries(...entries: NamedMapEntry[]) { + this.entries.push(...entries); + this.checkEntries(); + } + + private checkEntries() { + for (const entry of this.entries) { + if (entry.embedded) { + if (entry.key !== '') { + throw new Error('Embedded entries must have an empty key'); + } + if (!(entry.valueSchema instanceof NamedMapSchema)) { + throw new Error( + 'Embedded entry valueSchema must be a NamedMapSchema' + ); + } + } + } + + const keys = new Set(); + for (const entry of this.getEntries()) { + if (keys.has(entry.key)) { + throw new Error(`Duplicate key: ${entry.key}`); + } + keys.add(entry.key); + } + } + + /** + * Returns all top-level entries, properly accounting for fields from embedded entries. + * @returns An array of all top-level entries for this map. + */ + public getEntries(): NamedMapEntry[] { + const entries: NamedMapEntry[] = []; + for (const entry of this.entries) { + if (entry.embedded) { + const embeddedMapSchema = entry.valueSchema as NamedMapSchema; + entries.push(...embeddedMapSchema.getEntries()); + } else { + entries.push(entry); + } + } + return entries; + } + + public defaultValue(): Map { + const map = new Map(); + for (const entry of this.getEntries()) { + map.set(entry.key, entry.valueSchema.defaultValue()); + } + return map; + } + + public isDefaultValue(data: unknown): boolean { + if (!(data instanceof Map)) return false; + for (const entry of this.getEntries()) { + if (!entry.valueSchema.isDefaultValue(data.get(entry.key))) { + return false; + } + } + return true; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `NamedMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const map = new Map(); + for (const entry of this.getEntries()) { + const value = data.get(entry.key); + if (entry.omitEmpty && entry.valueSchema.isDefaultValue(value)) { + continue; + } + map.set(entry.key, entry.valueSchema.prepareMsgpack(value)); + } + return map; + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): Map { + if (!(encoded instanceof Map)) { + throw new Error('NamedMapSchema data must be a Map'); + } + const map = new Map(); + for (const entry of this.getEntries()) { + if (encoded.has(entry.key)) { + map.set( + entry.key, + entry.valueSchema.fromPreparedMsgpack( + encoded.get(entry.key), + rawStringProvider.withMapValue(entry.key) + ) + ); + } else if (entry.omitEmpty) { + map.set(entry.key, entry.valueSchema.defaultValue()); + } else { + throw new Error(`Missing key: ${entry.key}`); + } + } + return map; + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (!(data instanceof Map)) { + throw new Error('NamedMapSchema data must be a Map'); + } + const obj: { [key: string]: JSONEncodingData } = {}; + for (const entry of this.getEntries()) { + const value = data.get(entry.key); + if (entry.omitEmpty && entry.valueSchema.isDefaultValue(value)) { + continue; + } + obj[entry.key] = entry.valueSchema.prepareJSON(value, options); + } + return obj; + } + + public fromPreparedJSON(encoded: JSONEncodingData): Map { + if ( + encoded == null || + typeof encoded !== 'object' || + Array.isArray(encoded) + ) { + throw new Error('NamedMapSchema data must be an object'); + } + const map = new Map(); + for (const entry of this.getEntries()) { + if (Object.prototype.hasOwnProperty.call(encoded, entry.key)) { + map.set( + entry.key, + entry.valueSchema.fromPreparedJSON(encoded[entry.key]) + ); + } else if (entry.omitEmpty) { + map.set(entry.key, entry.valueSchema.defaultValue()); + } else { + throw new Error(`Missing key: ${entry.key}`); + } + } + return map; + } +} + +/** + * Combines multiple maps into a single map. Throws an error if any of the maps have duplicate keys. + * @param maps - The maps to combine. + * @returns A new map with all the entries from the input maps. + */ +export function combineMaps(...maps: Array>): Map { + const combined = new Map(); + for (const map of maps) { + for (const [key, value] of map) { + if (combined.has(key)) { + throw new Error(`Duplicate key: ${key}`); + } + combined.set(key, value); + } + } + return combined; +} + +/** + * Converts a map to a new map with different keys and values. + * @param map - The map to convert. + * @param func - The function to convert each entry. + * @returns A new map with the converted entries. + */ +export function convertMap( + map: Map, + func: (k: K1, v: V1) => [K2, V2] +): Map { + const mapped = new Map(); + for (const [key, value] of map) { + const [newKey, newValue] = func(key, value); + mapped.set(newKey, newValue); + } + return mapped; +} + +/** + * Schema for a map with a variable number of uint64 keys. + */ +export class Uint64MapSchema extends Schema { + constructor(public readonly valueSchema: Schema) { + super(); + } + + public defaultValue(): Map { + return new Map(); + } + + public isDefaultValue(data: unknown): boolean { + return data instanceof Map && data.size === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `Uint64MapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + const bigintKey = ensureUint64(key); + if (prepared.has(bigintKey)) { + throw new Error(`Duplicate key: ${bigintKey}`); + } + prepared.set(bigintKey, this.valueSchema.prepareMsgpack(value)); + } + return prepared; + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): Map { + if (!(encoded instanceof Map)) { + throw new Error('Uint64MapSchema data must be a Map'); + } + const map = new Map(); + for (const [key, value] of encoded) { + const bigintKey = ensureUint64(key); + if (map.has(bigintKey)) { + throw new Error(`Duplicate key: ${bigintKey}`); + } + map.set( + bigintKey, + this.valueSchema.fromPreparedMsgpack( + value, + rawStringProvider.withMapValue(key) + ) + ); + } + return map; + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `Uint64MapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + const bigintKey = ensureUint64(key); + if (prepared.has(bigintKey)) { + throw new Error(`Duplicate key: ${bigintKey}`); + } + prepared.set(bigintKey, this.valueSchema.prepareJSON(value, options)); + } + // Convert map to object + const obj: { [key: string]: JSONEncodingData } = {}; + for (const [key, value] of prepared) { + obj[key.toString()] = value; + } + return obj; + } + + public fromPreparedJSON(encoded: JSONEncodingData): Map { + if ( + encoded == null || + typeof encoded !== 'object' || + Array.isArray(encoded) + ) { + throw new Error('Uint64MapSchema data must be an object'); + } + const map = new Map(); + for (const [key, value] of Object.entries(encoded)) { + const bigintKey = BigInt(key); + if (map.has(bigintKey)) { + throw new Error(`Duplicate key: ${bigintKey}`); + } + map.set(bigintKey, this.valueSchema.fromPreparedJSON(value)); + } + return map; + } +} + +/** + * Schema for a map with a variable number of string keys. + */ +export class StringMapSchema extends Schema { + constructor(public readonly valueSchema: Schema) { + super(); + } + + public defaultValue(): Map { + return new Map(); + } + + public isDefaultValue(data: unknown): boolean { + return data instanceof Map && data.size === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `StringMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + if (typeof key !== 'string') { + throw new Error(`Invalid key: ${key}`); + } + if (prepared.has(key)) { + throw new Error(`Duplicate key: ${key}`); + } + prepared.set(key, this.valueSchema.prepareMsgpack(value)); + } + return prepared; + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): Map { + if (!(encoded instanceof Map)) { + throw new Error('StringMapSchema data must be a Map'); + } + const map = new Map(); + for (const [key, value] of encoded) { + if (typeof key !== 'string') { + throw new Error(`Invalid key: ${key}`); + } + if (map.has(key)) { + throw new Error(`Duplicate key: ${key}`); + } + map.set( + key, + this.valueSchema.fromPreparedMsgpack( + value, + rawStringProvider.withMapValue(key) + ) + ); + } + return map; + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `StringMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + if (typeof key !== 'string') { + throw new Error(`Invalid key: ${key}`); + } + if (prepared.has(key)) { + throw new Error(`Duplicate key: ${key}`); + } + prepared.set(key, this.valueSchema.prepareJSON(value, options)); + } + // Convert map to object + const obj: { [key: string]: JSONEncodingData } = {}; + for (const [key, value] of prepared) { + obj[key] = value; + } + return obj; + } + + public fromPreparedJSON(encoded: JSONEncodingData): Map { + if ( + encoded == null || + typeof encoded !== 'object' || + Array.isArray(encoded) + ) { + throw new Error('StringMapSchema data must be an object'); + } + const map = new Map(); + for (const [key, value] of Object.entries(encoded)) { + if (map.has(key)) { + throw new Error(`Duplicate key: ${key}`); + } + map.set(key, this.valueSchema.fromPreparedJSON(value)); + } + return map; + } +} + +/** + * Schema for a map with a variable number of byte array keys. + */ +export class ByteArrayMapSchema extends Schema { + constructor(public readonly valueSchema: Schema) { + super(); + } + + public defaultValue(): Map { + return new Map(); + } + + public isDefaultValue(data: unknown): boolean { + return data instanceof Map && data.size === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `ByteArrayMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + if (!(key instanceof Uint8Array)) { + throw new Error(`Invalid key: ${key} (${typeof key})`); + } + prepared.set(key, this.valueSchema.prepareMsgpack(value)); + } + return prepared; + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): Map { + if (!(encoded instanceof Map)) { + throw new Error('ByteArrayMapSchema data must be a Map'); + } + const map = new Map(); + for (const [key, value] of encoded) { + if (!(key instanceof Uint8Array)) { + throw new Error(`Invalid key: ${key} (${typeof key})`); + } + map.set( + key, + this.valueSchema.fromPreparedMsgpack( + value, + rawStringProvider.withMapValue(key) + ) + ); + } + return map; + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `ByteArrayMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + if (!(key instanceof Uint8Array)) { + throw new Error(`Invalid key: ${key} (${typeof key})`); + } + const b64Encoded = bytesToBase64(key); + if (prepared.has(b64Encoded)) { + throw new Error(`Duplicate key (base64): ${b64Encoded}`); + } + prepared.set(b64Encoded, this.valueSchema.prepareJSON(value, options)); + } + // Convert map to object + const obj: { [key: string]: JSONEncodingData } = {}; + for (const [key, value] of prepared) { + obj[key] = value; + } + return obj; + } + + public fromPreparedJSON(encoded: JSONEncodingData): Map { + if ( + encoded == null || + typeof encoded !== 'object' || + Array.isArray(encoded) + ) { + throw new Error('ByteArrayMapSchema data must be an object'); + } + const map = new Map(); + for (const [key, value] of Object.entries(encoded)) { + map.set(base64ToBytes(key), this.valueSchema.fromPreparedJSON(value)); + } + return map; + } +} + +/** + * Converts any RawBinaryString values to regular strings in a MsgpackEncodingData object. + * + * Note this conversion may be lossy if the binary data is not valid UTF-8. + * + * @returns A new object with RawBinaryString values converted to strings. + */ +function convertRawStringsInMsgpackValue( + value: MsgpackEncodingData +): MsgpackEncodingData { + if (value instanceof RawBinaryString) { + return bytesToString(value.rawBinaryValue as Uint8Array); + } + if (value instanceof Map) { + const newMap = new Map< + string | number | bigint | Uint8Array, + MsgpackEncodingData + >(); + for (const [key, val] of value) { + newMap.set( + convertRawStringsInMsgpackValue(key) as + | string + | number + | bigint + | Uint8Array, + convertRawStringsInMsgpackValue(val) + ); + } + return newMap; + } + if (Array.isArray(value)) { + return value.map(convertRawStringsInMsgpackValue); + } + return value; +} + +/** + * Schema for a map with a variable number of binary string keys. + * + * See SpecialCaseBinaryStringSchema for more information about the key type. + */ +export class SpecialCaseBinaryStringMapSchema extends Schema { + constructor(public readonly valueSchema: Schema) { + super(); + } + + public defaultValue(): Map { + return new Map(); + } + + public isDefaultValue(data: unknown): boolean { + return data instanceof Map && data.size === 0; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `SpecialCaseBinaryStringMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + if (!(key instanceof Uint8Array)) { + throw new Error(`Invalid key: ${key} (${typeof key})`); + } + prepared.set( + new RawBinaryString(key), + this.valueSchema.prepareMsgpack(value) + ); + } + // Cast is needed because RawBinaryString is not part of the standard MsgpackEncodingData + return prepared as unknown as Map; + } + + public fromPreparedMsgpack( + _encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): Map { + const map = new Map(); + const keysAndValues = + rawStringProvider.getRawStringKeysAndValuesAtCurrentLocation(); + for (const [key, value] of keysAndValues) { + map.set( + key, + this.valueSchema.fromPreparedMsgpack( + convertRawStringsInMsgpackValue(value), + rawStringProvider.withMapValue(new RawBinaryString(key)) + ) + ); + } + return map; + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (!(data instanceof Map)) { + throw new Error( + `SpecialCaseBinaryStringMapSchema data must be a Map. Got (${typeof data}) ${data}` + ); + } + const prepared = new Map(); + for (const [key, value] of data) { + if (!(key instanceof Uint8Array)) { + throw new Error(`Invalid key: ${key}`); + } + // Not safe to convert to string for all binary data + const keyStringValue = bytesToString(key); + if ( + !options.lossyBinaryStringConversion && + !arrayEqual(coerceToBytes(keyStringValue), key) + ) { + throw new Error( + `Invalid UTF-8 byte array encountered. Encode with lossyBinaryStringConversion enabled to bypass this check. Base64 value: ${bytesToBase64(key)}` + ); + } + prepared.set( + keyStringValue, + this.valueSchema.prepareJSON(value, options) + ); + } + // Convert map to object + const obj: { [key: string]: JSONEncodingData } = {}; + for (const [key, value] of prepared) { + obj[key] = value; + } + return obj; + } + + public fromPreparedJSON(encoded: JSONEncodingData): Map { + if ( + encoded == null || + typeof encoded !== 'object' || + Array.isArray(encoded) + ) { + throw new Error( + 'SpecialCaseBinaryStringMapSchema data must be an object' + ); + } + const map = new Map(); + for (const [key, value] of Object.entries(encoded)) { + map.set(coerceToBytes(key), this.valueSchema.fromPreparedJSON(value)); + } + return map; + } +} diff --git a/packages/sdk/src/encoding/schema/optional.ts b/packages/sdk/src/encoding/schema/optional.ts new file mode 100644 index 00000000..ae6100e1 --- /dev/null +++ b/packages/sdk/src/encoding/schema/optional.ts @@ -0,0 +1,72 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; + +/* eslint-disable class-methods-use-this */ + +/** + * OptionalSchema allows for another schema-defined value to be optional. + * + * This expands the set of values which can be represented by the given schema to include `undefined`. + * + * Note that this schema considers `undefined` _and_ any default values from the underlying schema + * to all be default values. This means that when using NamedMapSchema to omit default values, an + * `undefined` value is indistinguishable from the given schema's default value; in this respect, + * OptionalSchema does not affect the encoding of NamedMapSchema values, but rather allows the + * application to restore omitted values as `undefined` instead of their default value. + * + * Upon decoding, this schema also allows null/undefined values to be acceptable as values. + */ +export class OptionalSchema extends Schema { + constructor(public readonly valueSchema: Schema) { + super(); + } + + public defaultValue(): undefined { + return undefined; + } + + public isDefaultValue(data: unknown): boolean { + return data === undefined || this.valueSchema.isDefaultValue(data); + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (data === undefined) { + return undefined; + } + return this.valueSchema.prepareMsgpack(data); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + rawStringProvider: MsgpackRawStringProvider + ): unknown { + // JS undefined is encoded as msgpack nil, which may be decoded as JS null + if (encoded === undefined || encoded === null) { + return undefined; + } + return this.valueSchema.fromPreparedMsgpack(encoded, rawStringProvider); + } + + public prepareJSON( + data: unknown, + options: PrepareJSONOptions + ): JSONEncodingData { + if (data === undefined) { + // JSON representation does not have undefined, only null + return null; + } + return this.valueSchema.prepareJSON(data, options); + } + + public fromPreparedJSON(encoded: JSONEncodingData): unknown { + if (encoded === undefined || encoded === null) { + return undefined; + } + return this.valueSchema.fromPreparedJSON(encoded); + } +} diff --git a/packages/sdk/src/encoding/schema/string.ts b/packages/sdk/src/encoding/schema/string.ts new file mode 100644 index 00000000..4f0081fe --- /dev/null +++ b/packages/sdk/src/encoding/schema/string.ts @@ -0,0 +1,55 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; + +/* eslint-disable class-methods-use-this */ + +export class StringSchema extends Schema { + public defaultValue(): string { + return ''; + } + + public isDefaultValue(data: unknown): boolean { + return data === ''; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + if (typeof data === 'string') { + return data; + } + throw new Error(`Invalid string: (${typeof data}) ${data}`); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): string { + if (typeof encoded === 'string') { + return encoded; + } + throw new Error(`Invalid string: (${typeof encoded}) ${encoded}`); + } + + public prepareJSON( + data: unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + if (typeof data === 'string') { + return data; + } + throw new Error(`Invalid string: (${typeof data}) ${data}`); + } + + public fromPreparedJSON(encoded: JSONEncodingData): string { + if (typeof encoded === 'string') { + return encoded; + } + throw new Error(`Invalid string: (${typeof encoded}) ${encoded}`); + } +} diff --git a/packages/sdk/src/encoding/schema/uint64.ts b/packages/sdk/src/encoding/schema/uint64.ts new file mode 100644 index 00000000..81f1dbbc --- /dev/null +++ b/packages/sdk/src/encoding/schema/uint64.ts @@ -0,0 +1,46 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, +} from '../encoding.js'; +import { ensureUint64 } from '../../utils/utils.js'; + +/* eslint-disable class-methods-use-this */ + +export class Uint64Schema extends Schema { + public defaultValue(): bigint { + return BigInt(0); + } + + public isDefaultValue(data: unknown): boolean { + if (typeof data === 'bigint') return data === BigInt(0); + if (typeof data === 'number') return data === 0; + return false; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + return ensureUint64(data); + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): bigint { + return ensureUint64(encoded); + } + + public prepareJSON( + data: unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + return ensureUint64(data); + } + + public fromPreparedJSON(encoded: JSONEncodingData): bigint { + return ensureUint64(encoded); + } +} diff --git a/packages/sdk/src/encoding/schema/untyped.ts b/packages/sdk/src/encoding/schema/untyped.ts new file mode 100644 index 00000000..6551d615 --- /dev/null +++ b/packages/sdk/src/encoding/schema/untyped.ts @@ -0,0 +1,47 @@ +import { + Schema, + MsgpackEncodingData, + MsgpackRawStringProvider, + JSONEncodingData, + PrepareJSONOptions, + msgpackEncodingDataToJSONEncodingData, + jsonEncodingDataToMsgpackEncodingData, +} from '../encoding.js'; + +/* eslint-disable class-methods-use-this */ + +export class UntypedSchema extends Schema { + public defaultValue(): undefined { + return undefined; + } + + public isDefaultValue(data: unknown): boolean { + return data === undefined; + } + + public prepareMsgpack(data: unknown): MsgpackEncodingData { + // Value is already MsgpackEncodingData, since it is returned as such from + // fromPreparedMsgpack and fromPreparedJSON + return data as MsgpackEncodingData; + } + + public fromPreparedMsgpack( + encoded: MsgpackEncodingData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _rawStringProvider: MsgpackRawStringProvider + ): MsgpackEncodingData { + return encoded; + } + + public prepareJSON( + data: unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _options: PrepareJSONOptions + ): JSONEncodingData { + return msgpackEncodingDataToJSONEncodingData(data as MsgpackEncodingData); + } + + public fromPreparedJSON(encoded: JSONEncodingData): MsgpackEncodingData { + return jsonEncodingDataToMsgpackEncodingData(encoded); + } +} diff --git a/packages/sdk/src/encoding/uint64.ts b/packages/sdk/src/encoding/uint64.ts new file mode 100644 index 00000000..3dc0a441 --- /dev/null +++ b/packages/sdk/src/encoding/uint64.ts @@ -0,0 +1,91 @@ +import { concatArrays } from '../utils/utils.js'; + +// NOTE: at the moment we specifically do not use Buffer.writeBigUInt64BE and +// Buffer.readBigUInt64BE. This is because projects using webpack v4 +// automatically include an old version of the npm `buffer` package (v4.9.2 at +// the time of writing), and this old version does not have these methods. + +/** + * encodeUint64 converts an integer to its binary representation. + * @param num - The number to convert. This must be an unsigned integer less than + * 2^64. + * @returns An 8-byte typed array containing the big-endian encoding of the input + * integer. + */ +export function encodeUint64(num: number | bigint) { + const isInteger = typeof num === 'bigint' || Number.isInteger(num); + + if (!isInteger || num < 0 || num > BigInt('0xffffffffffffffff')) { + throw new Error('Input is not a 64-bit unsigned integer'); + } + + const encoding = new Uint8Array(8); + const view = new DataView(encoding.buffer); + view.setBigUint64(0, BigInt(num)); + + return encoding; +} + +/** + * decodeUint64 produces an integer from a binary representation. + * @param data - An typed array containing the big-endian encoding of an unsigned integer + * less than 2^64. This array must be at most 8 bytes long. + * @param decodingMode - Configure how the integer will be + * decoded. + * + * The options are: + * * "safe": The integer will be decoded as a Number, but if it is greater than + * Number.MAX_SAFE_INTEGER an error will be thrown. + * * "mixed": The integer will be decoded as a Number if it is less than or equal to + * Number.MAX_SAFE_INTEGER, otherwise it will be decoded as a BigInt. + * * "bigint": The integer will always be decoded as a BigInt. + * + * Defaults to "safe" if not included. + * @returns The integer that was encoded in the input data. The return type will + * be determined by the parameter decodingMode. + */ +export function decodeUint64(data: Uint8Array, decodingMode: 'safe'): number; +export function decodeUint64( + data: Uint8Array, + decodingMode: 'mixed' +): number | bigint; +export function decodeUint64(data: Uint8Array, decodingMode: 'bigint'): bigint; +export function decodeUint64(data: Uint8Array): number; +export function decodeUint64(data: any, decodingMode: any = 'safe') { + if ( + decodingMode !== 'safe' && + decodingMode !== 'mixed' && + decodingMode !== 'bigint' + ) { + throw new Error(`Unknown decodingMode option: ${decodingMode}`); + } + + if (data.byteLength === 0 || data.byteLength > 8) { + throw new Error( + `Data has unacceptable length. Expected length is between 1 and 8, got ${data.byteLength}` + ); + } + + // insert 0s at the beginning if data is smaller than 8 bytes + const padding = new Uint8Array(8 - data.byteLength); + const encoding = concatArrays(padding, data); + const view = new DataView(encoding.buffer); + + const num = view.getBigUint64(0); + const isBig = num > BigInt(Number.MAX_SAFE_INTEGER); + + if (decodingMode === 'safe') { + if (isBig) { + throw new Error( + `Integer exceeds maximum safe integer: ${num.toString()}. Try decoding with "mixed" or "safe" decodingMode.` + ); + } + return Number(num); + } + + if (decodingMode === 'mixed' && !isBig) { + return Number(num); + } + + return num; +} diff --git a/packages/sdk/src/group.ts b/packages/sdk/src/group.ts new file mode 100644 index 00000000..23060dd6 --- /dev/null +++ b/packages/sdk/src/group.ts @@ -0,0 +1,51 @@ +import type { Transaction } from '@algorandfoundation/algokit-transact' +import { getTransactionIdRaw, groupTransactions as groupTxns } from '@algorandfoundation/algokit-transact' +import { msgpackRawEncode } from './encoding/encoding.js' +import * as nacl from './nacl/naclWrappers.js' +import * as utils from './utils/utils.js' + +const ALGORAND_MAX_TX_GROUP_SIZE = 16 +const TX_GROUP_TAG = new TextEncoder().encode('TG') + +function txGroupPreimage(txnHashes: Uint8Array[]): Uint8Array { + if (txnHashes.length > ALGORAND_MAX_TX_GROUP_SIZE) { + throw new Error(`${txnHashes.length} transactions grouped together but max group size is ${ALGORAND_MAX_TX_GROUP_SIZE}`) + } + if (txnHashes.length === 0) { + throw new Error('Cannot compute group ID of zero transactions') + } + const bytes = msgpackRawEncode({ + txlist: txnHashes, + }) + return utils.concatArrays(TX_GROUP_TAG, bytes) +} + +/** + * computeGroupID returns group ID for a group of transactions + * @param txns - array of transactions + * @returns Uint8Array + */ +export function computeGroupID(txns: ReadonlyArray): Uint8Array { + const hashes: Uint8Array[] = [] + for (const txn of txns) { + hashes.push(getTransactionIdRaw(txn)) + } + + const toBeHashed = txGroupPreimage(hashes) + const gid = nacl.genericHash(toBeHashed) + return Uint8Array.from(gid) +} + +/** + * assignGroupID assigns group id to a given list of unsigned transactions + * @param txns - array of transactions. Returns a new array with group IDs assigned (immutable) + * @returns Transaction[] - New array of transactions with group IDs assigned + */ +export function assignGroupID(txns: Transaction[]): Transaction[] { + // Mutate the transaction to keep the existing algosdk behaviour + const groupedTxn = groupTxns(txns) + txns.forEach((txn, i) => { + txn.group = groupedTxn[i].group + }) + return txns +} diff --git a/packages/sdk/src/heartbeat.ts b/packages/sdk/src/heartbeat.ts new file mode 100644 index 00000000..90404473 --- /dev/null +++ b/packages/sdk/src/heartbeat.ts @@ -0,0 +1,168 @@ +import { Address } from './encoding/address.js'; +import { Encodable, Schema } from './encoding/encoding.js'; +import { + AddressSchema, + Uint64Schema, + ByteArraySchema, + FixedLengthByteArraySchema, + NamedMapSchema, + allOmitEmpty, +} from './encoding/schema/index.js'; + +export class HeartbeatProof implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 's', // Sig + valueSchema: new FixedLengthByteArraySchema(64), + }, + { + key: 'p', // PK + valueSchema: new FixedLengthByteArraySchema(32), + }, + { + key: 'p2', // PK2 + valueSchema: new FixedLengthByteArraySchema(32), + }, + { + key: 'p1s', // PK1Sig + valueSchema: new FixedLengthByteArraySchema(64), + }, + { + key: 'p2s', // PK2Sig + valueSchema: new FixedLengthByteArraySchema(64), + }, + ]) + ); + + public sig: Uint8Array; + + public pk: Uint8Array; + + public pk2: Uint8Array; + + public pk1Sig: Uint8Array; + + public pk2Sig: Uint8Array; + + public constructor(params: { + sig: Uint8Array; + pk: Uint8Array; + pk2: Uint8Array; + pk1Sig: Uint8Array; + pk2Sig: Uint8Array; + }) { + this.sig = params.sig; + this.pk = params.pk; + this.pk2 = params.pk2; + this.pk1Sig = params.pk1Sig; + this.pk2Sig = params.pk2Sig; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return HeartbeatProof.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['s', this.sig], + ['p', this.pk], + ['p2', this.pk2], + ['p1s', this.pk1Sig], + ['p2s', this.pk2Sig], + ]); + } + + public static fromEncodingData(data: unknown): HeartbeatProof { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded HeartbeatProof: ${data}`); + } + return new HeartbeatProof({ + sig: data.get('s'), + pk: data.get('p'), + pk2: data.get('p2'), + pk1Sig: data.get('p1s'), + pk2Sig: data.get('p2s'), + }); + } +} + +export class Heartbeat implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'a', // HbAddress + valueSchema: new AddressSchema(), + }, + { + key: 'prf', // HbProof + valueSchema: HeartbeatProof.encodingSchema, + }, + { + key: 'sd', // HbSeed + valueSchema: new ByteArraySchema(), + }, + { + key: 'vid', // HbVoteID + valueSchema: new FixedLengthByteArraySchema(32), + }, + { + key: 'kd', // HbKeyDilution + valueSchema: new Uint64Schema(), + }, + ]) + ); + + public address: Address; + + public proof: HeartbeatProof; + + public seed: Uint8Array; + + public voteID: Uint8Array; + + public keyDilution: bigint; + + public constructor(params: { + address: Address; + proof: HeartbeatProof; + seed: Uint8Array; + voteID: Uint8Array; + keyDilution: bigint; + }) { + this.address = params.address; + this.proof = params.proof; + this.seed = params.seed; + this.voteID = params.voteID; + this.keyDilution = params.keyDilution; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return Heartbeat.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['a', this.address], + ['prf', this.proof.toEncodingData()], + ['sd', this.seed], + ['vid', this.voteID], + ['kd', this.keyDilution], + ]); + } + + public static fromEncodingData(data: unknown): Heartbeat { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Heartbeat: ${data}`); + } + return new Heartbeat({ + address: data.get('a'), + proof: HeartbeatProof.fromEncodingData(data.get('prf')), + seed: data.get('sd'), + voteID: data.get('vid'), + keyDilution: data.get('kd'), + }); + } +} diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts new file mode 100644 index 00000000..01b8996b --- /dev/null +++ b/packages/sdk/src/index.ts @@ -0,0 +1,110 @@ +import type { SignedTransaction, Transaction } from '@algorandfoundation/algokit-transact' +import { encodeSignedTransaction, encodeTransaction, getTransactionId } from '@algorandfoundation/algokit-transact' +import * as convert from './convert' +import { Address } from './encoding/address' +import * as nacl from './nacl/naclWrappers' +import * as utils from './utils/utils' + +const SIGN_BYTES_PREFIX = Uint8Array.from([77, 88]) // "MX" + +// Errors +export const MULTISIG_BAD_SENDER_ERROR_MSG = 'The transaction sender address and multisig preimage do not match.' + +/** + * signTransaction takes an object with either payment or key registration fields and + * a secret key and returns a signed blob. + * + * Payment transaction fields: from, to, amount, fee, firstValid, lastValid, genesisHash, + * note(optional), GenesisID(optional), closeRemainderTo(optional) + * + * Key registration fields: fee, firstValid, lastValid, voteKey, selectionKey, voteFirst, + * voteLast, voteKeyDilution, genesisHash, note(optional), GenesisID(optional) + * + * If flatFee is not set and the final calculated fee is lower than the protocol minimum fee, the fee will be increased to match the minimum. + * @param txn - object with either payment or key registration fields + * @param sk - Algorand Secret Key + * @returns object contains the binary signed transaction and its txID + */ +export function signTransaction(txn: Transaction, sk: Uint8Array) { + // Sign the transaction using nacl + const bytesToSign = encodeTransaction(txn) + const signature = nacl.sign(bytesToSign, sk) + + const signedTxn: SignedTransaction = { + txn: txn, + signature, + } + + return { + txID: getTransactionId(txn), + blob: encodeSignedTransaction(signedTxn), + } +} + +/** + * signBytes takes arbitrary bytes and a secret key, prepends the bytes with "MX" for domain separation, signs the bytes + * with the private key, and returns the signature. + * @param bytes - Uint8array + * @param sk - Algorand secret key + * @returns binary signature + */ +export function signBytes(bytes: Uint8Array, sk: Uint8Array) { + const toBeSigned = utils.concatArrays(SIGN_BYTES_PREFIX, bytes) + const sig = nacl.sign(toBeSigned, sk) + return sig +} + +/** + * verifyBytes takes array of bytes, an address, and a signature and verifies if the signature is correct for the public + * key and the bytes (the bytes should have been signed with "MX" prepended for domain separation). + * @param bytes - Uint8Array + * @param signature - binary signature + * @param addr - string address + * @returns bool + */ +export function verifyBytes(bytes: Uint8Array, signature: Uint8Array, addr: string | Address) { + const toBeVerified = utils.concatArrays(SIGN_BYTES_PREFIX, bytes) + const addrObj = typeof addr === 'string' ? Address.fromString(addr) : addr + return nacl.verify(toBeVerified, signature, addrObj.publicKey) +} + +export const ERROR_MULTISIG_BAD_SENDER = new Error(MULTISIG_BAD_SENDER_ERROR_MSG) +export const ERROR_INVALID_MICROALGOS = new Error(convert.INVALID_MICROALGOS_ERROR_MSG) + +export * from './abi/index' +export { default as generateAccount } from './account' +export * from './client' +// Export client classes with algosdk-compatible names +export { KmdClient as Kmd } from './client/kmd' +export { IndexerClient as Indexer } from './client/v2/indexer/index' +export * as indexerModels from './client/v2/indexer/models/types' +export * from './composer' +export * from './convert' +export { Address, decodeAddress, encodeAddress, getApplicationAddress, isValidAddress } from './encoding/address' +export { bigIntToBytes, bytesToBigInt } from './encoding/bigint' +export { base64ToBytes, bytesToBase64, bytesToHex, bytesToString, coerceToBytes, hexToBytes } from './encoding/binarydata' +export * from './encoding/encoding' +export { decodeUint64, encodeUint64 } from './encoding/uint64' +export { assignGroupID, computeGroupID } from './group' +export * from './logic/sourcemap' +export * from './logicsig' +export * from './makeTxn' +export { + masterDerivationKeyToMnemonic, + mnemonicFromSeed, + mnemonicToMasterDerivationKey, + mnemonicToSecretKey, + secretKeyToMnemonic, + seedFromMnemonic, +} from './mnemonic/mnemonic' +export * from './multisig' +export * from './signer' +export { signLogicSigTransaction, signLogicSigTransactionObject } from './signing' +export * from './stateproof' +export * from './types/account' +export type { default as Account } from './types/account' +export * from './types/intDecoding' +export { default as IntDecoding } from './types/intDecoding' +export * from './types/transactions/index' +export * from './utils/utils' +export { waitForConfirmation } from './wait' diff --git a/packages/sdk/src/logic/sourcemap.ts b/packages/sdk/src/logic/sourcemap.ts new file mode 100644 index 00000000..54f94a8e --- /dev/null +++ b/packages/sdk/src/logic/sourcemap.ts @@ -0,0 +1,134 @@ +// @ts-ignore - vlq doesn't have proper type exports +import * as vlq from 'vlq'; + +/** + * Represents a location in a source file. + */ +export interface SourceLocation { + line: number; + column: number; + sourceIndex: number; + nameIndex?: number; +} + +/** + * Represents the location of a specific PC in a source line. + */ +export interface PcLineLocation { + pc: number; + column: number; + nameIndex?: number; +} + +/** + * Contains a mapping from TEAL program PC to source file location. + */ +export class ProgramSourceMap { + public readonly version: number; + /** + * A list of original sources used by the "mappings" entry. + */ + public readonly sources: string[]; + /** + * A list of symbol names used by the "mappings" entry. + */ + public readonly names: string[]; + /** + * A string with the encoded mapping data. + */ + public readonly mappings: string; + + private pcToLocation: Map; + + // Key is `${sourceIndex}:${line}` + private sourceAndLineToPc: Map; + + constructor({ + version, + sources, + names, + mappings, + }: { + version: number; + sources: string[]; + names: string[]; + mappings: string; + }) { + this.version = version; + this.sources = sources; + this.names = names; + this.mappings = mappings; + + if (this.version !== 3) + throw new Error(`Only version 3 is supported, got ${this.version}`); + + if (this.mappings === undefined) + throw new Error( + 'mapping undefined, cannot build source map without `mapping`' + ); + + const pcList = this.mappings.split(';').map(vlq.decode); + + this.pcToLocation = new Map(); + this.sourceAndLineToPc = new Map(); + + const lastLocation = { + line: 0, + column: 0, + sourceIndex: 0, + nameIndex: 0, + } satisfies SourceLocation; + for (const [pc, data] of pcList.entries()) { + const dataArray = data as number[]; + if (dataArray.length < 4) continue; + + const nameDelta = dataArray.length > 4 ? dataArray[4] : undefined; + const [, sourceDelta, lineDelta, columnDelta] = dataArray; + + lastLocation.sourceIndex += sourceDelta; + lastLocation.line += lineDelta; + lastLocation.column += columnDelta; + if (typeof nameDelta !== 'undefined') { + lastLocation.nameIndex += nameDelta; + } + + const sourceAndLineKey = `${lastLocation.sourceIndex}:${lastLocation.line}`; + let pcsForSourceAndLine = this.sourceAndLineToPc.get(sourceAndLineKey); + if (pcsForSourceAndLine === undefined) { + pcsForSourceAndLine = []; + this.sourceAndLineToPc.set(sourceAndLineKey, pcsForSourceAndLine); + } + + const pcInLine: PcLineLocation = { + pc, + column: lastLocation.column, + }; + const pcLocation: SourceLocation = { + line: lastLocation.line, + column: lastLocation.column, + sourceIndex: lastLocation.sourceIndex, + }; + if (typeof nameDelta !== 'undefined') { + pcInLine.nameIndex = lastLocation.nameIndex; + pcLocation.nameIndex = lastLocation.nameIndex; + } + + pcsForSourceAndLine.push(pcInLine); + this.pcToLocation.set(pc, pcLocation); + } + } + + getPcs(): number[] { + return Array.from(this.pcToLocation.keys()); + } + + getLocationForPc(pc: number): SourceLocation | undefined { + return this.pcToLocation.get(pc); + } + + getPcsOnSourceLine(sourceIndex: number, line: number): PcLineLocation[] { + const pcs = this.sourceAndLineToPc.get(`${sourceIndex}:${line}`); + if (pcs === undefined) return []; + return pcs; + } +} diff --git a/packages/sdk/src/logicsig.ts b/packages/sdk/src/logicsig.ts new file mode 100644 index 00000000..2e20996e --- /dev/null +++ b/packages/sdk/src/logicsig.ts @@ -0,0 +1,539 @@ +import * as nacl from './nacl/naclWrappers.js'; +import { Address, isValidAddress } from './encoding/address.js'; +import * as encoding from './encoding/encoding.js'; +import { + NamedMapSchema, + ArraySchema, + ByteArraySchema, + FixedLengthByteArraySchema, + OptionalSchema, + allOmitEmpty, +} from './encoding/schema/index.js'; +import { + MultisigMetadata, + verifyMultisig, + addressFromMultisigPreImg, + pksFromAddresses, +} from './multisig.js'; +import * as utils from './utils/utils.js'; +import { + EncodedMultisig, + encodedMultiSigToEncodingData, + encodedMultiSigFromEncodingData, + ENCODED_MULTISIG_SCHEMA, +} from './types/transactions/encoded.js'; + +// base64regex is the regex to test for base64 strings +const base64regex = + /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/; + +/** sanityCheckProgram performs heuristic program validation: + * check if passed in bytes are Algorand address or is B64 encoded, rather than Teal bytes + * + * @param program - Program bytes to check + */ +export function sanityCheckProgram(program: Uint8Array) { + if (!program || program.length === 0) throw new Error('empty program'); + + const lineBreakOrd = '\n'.charCodeAt(0); + const blankSpaceOrd = ' '.charCodeAt(0); + const tildeOrd = '~'.charCodeAt(0); + + const isPrintable = (x: number) => blankSpaceOrd <= x && x <= tildeOrd; + const isAsciiPrintable = program.every( + (x: number) => x === lineBreakOrd || isPrintable(x) + ); + + if (isAsciiPrintable) { + const programStr = new TextDecoder().decode(program); + + if (isValidAddress(programStr)) + throw new Error('requesting program bytes, get Algorand address'); + + if (base64regex.test(programStr)) + throw new Error('program should not be b64 encoded'); + + throw new Error( + 'program bytes are all ASCII printable characters, not looking like Teal byte code' + ); + } +} + +const programTag = new TextEncoder().encode('Program'); +const multisigProgramTag = new TextEncoder().encode('MsigProgram'); + +/** + LogicSig implementation + + LogicSig cannot sign transactions in all cases. Instead, use LogicSigAccount as a safe, general purpose signing mechanism. Since LogicSig does not track the provided signature's public key, LogicSig cannot sign transactions when delegated to a non-multisig account _and_ the sender is not the delegating account. + */ +export class LogicSig implements encoding.Encodable { + static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'l', + valueSchema: new ByteArraySchema(), + }, + { + key: 'arg', + valueSchema: new ArraySchema(new ByteArraySchema()), + }, + { + key: 'sig', + valueSchema: new OptionalSchema(new FixedLengthByteArraySchema(64)), + }, + { + key: 'msig', + valueSchema: new OptionalSchema(ENCODED_MULTISIG_SCHEMA), + }, + { + key: 'lmsig', + valueSchema: new OptionalSchema(ENCODED_MULTISIG_SCHEMA), + }, + ]) + ); + + logic: Uint8Array; + args: Uint8Array[]; + sig?: Uint8Array; + msig?: EncodedMultisig; + lmsig?: EncodedMultisig; + + constructor(program: Uint8Array, programArgs?: Array | null) { + if ( + programArgs && + (!Array.isArray(programArgs) || + !programArgs.every((arg) => arg.constructor === Uint8Array)) + ) { + throw new TypeError('Invalid arguments'); + } + + let args: Uint8Array[] = []; + if (programArgs != null) + args = programArgs.map((arg) => new Uint8Array(arg)); + + sanityCheckProgram(program); + + this.logic = program; + this.args = args; + this.sig = undefined; + this.msig = undefined; + this.lmsig = undefined; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): encoding.Schema { + return LogicSig.encodingSchema; + } + + toEncodingData(): Map { + const data = new Map([ + ['l', this.logic], + ['arg', this.args], + ['sig', this.sig], + ]); + if (this.msig) { + data.set('msig', encodedMultiSigToEncodingData(this.msig)); + } + if (this.lmsig) { + data.set('lmsig', encodedMultiSigToEncodingData(this.lmsig)); + } + return data; + } + + static fromEncodingData(data: unknown): LogicSig { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded logic sig: ${data}`); + } + const lsig = new LogicSig(data.get('l'), data.get('arg')); + lsig.sig = data.get('sig'); + if (data.get('msig')) { + lsig.msig = encodedMultiSigFromEncodingData(data.get('msig')); + } + if (data.get('lmsig')) { + lsig.lmsig = encodedMultiSigFromEncodingData(data.get('lmsig')); + } + return lsig; + } + + /** + * Performs signature verification + * @param publicKey - Verification key (derived from sender address or escrow address) + */ + verify(publicKey: Uint8Array) { + const sigCount = [this.sig, this.msig, this.lmsig].filter(Boolean).length; + if (sigCount > 1) { + return false; + } + + try { + sanityCheckProgram(this.logic); + } catch (e) { + return false; + } + + const toBeSigned = utils.concatArrays(programTag, this.logic); + + if (!this.sig && !this.msig && !this.lmsig) { + const hash = nacl.genericHash(toBeSigned); + return utils.arrayEqual(hash, publicKey); + } + + if (this.sig) { + return nacl.verify(toBeSigned, this.sig, publicKey); + } + + if (this.lmsig) { + const multisigAddr = addressFromMultisigPreImg({ + version: this.lmsig.v, + threshold: this.lmsig.thr, + pks: this.lmsig.subsig.map((subsig) => subsig.pk), + }); + const lmsigProgram = utils.concatArrays( + multisigProgramTag, + multisigAddr.publicKey, + this.logic + ); + return verifyMultisig(lmsigProgram, this.lmsig!, publicKey); + } + + if (this.msig) { + return verifyMultisig(toBeSigned, this.msig!, publicKey); + } + + return false; + } + + /** + * Compute hash of the logic sig program (that is the same as escrow account address) as string address + * @returns String representation of the address + */ + address(): Address { + const toBeSigned = utils.concatArrays(programTag, this.logic); + const hash = nacl.genericHash(toBeSigned); + return new Address(Uint8Array.from(hash)); + } + + /** + * Creates signature (if no msig provided) or multi signature otherwise + * @param secretKey - Secret key to sign with + * @param msig - Multisig account as \{version, threshold, addrs\} + */ + sign(secretKey: Uint8Array, msig?: MultisigMetadata) { + if (msig == null) { + this.sig = this.signProgram(secretKey); + } else { + const subsigs = pksFromAddresses(msig.addrs).map((pk) => ({ pk })); + + this.lmsig = { + v: msig.version, + thr: msig.threshold, + subsig: subsigs, + }; + + const [sig, index] = this.singleSignMultisig(secretKey, this.lmsig); + this.lmsig.subsig[index].s = sig; + } + } + + /** + * Appends a signature to multi signature + * @param secretKey - Secret key to sign with + */ + appendToMultisig(secretKey: Uint8Array) { + if (this.lmsig === undefined) { + throw new Error('no multisig present'); + } + const [sig, index] = this.singleSignMultisig(secretKey, this.lmsig); + this.lmsig.subsig[index].s = sig; + } + + signProgram(secretKey: Uint8Array) { + const toBeSigned = utils.concatArrays(programTag, this.logic); + const sig = nacl.sign(toBeSigned, secretKey); + return sig; + } + + signProgramMultisig(secretKey: Uint8Array, msig: EncodedMultisig) { + const multisigAddr = addressFromMultisigPreImg({ + version: msig.v, + threshold: msig.thr, + pks: msig.subsig.map((subsig) => subsig.pk), + }); + const toBeSigned = utils.concatArrays( + multisigProgramTag, + multisigAddr.publicKey, + this.logic + ); + const sig = nacl.sign(toBeSigned, secretKey); + return sig; + } + + singleSignMultisig( + secretKey: Uint8Array, + msig: EncodedMultisig + ): [sig: Uint8Array, index: number] { + let index = -1; + const myPk = nacl.keyPairFromSecretKey(secretKey).publicKey; + for (let i = 0; i < msig.subsig.length; i++) { + const { pk } = msig.subsig[i]; + if (utils.arrayEqual(pk, myPk)) { + index = i; + break; + } + } + if (index === -1) { + throw new Error('invalid secret key'); + } + const sig = this.signProgramMultisig(secretKey, msig); + return [sig, index]; + } + + toByte(): Uint8Array { + return encoding.encodeMsgpack(this); + } + + static fromByte(encoded: ArrayLike): LogicSig { + return encoding.decodeMsgpack(encoded, LogicSig); + } +} + +/** + * Represents an account that can sign with a LogicSig program. + */ +export class LogicSigAccount implements encoding.Encodable { + static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'lsig', + valueSchema: LogicSig.encodingSchema, + }, + { + key: 'sigkey', + valueSchema: new OptionalSchema(new FixedLengthByteArraySchema(32)), + }, + ]) + ); + + lsig: LogicSig; + sigkey?: Uint8Array; + + /** + * Create a new LogicSigAccount. By default this will create an escrow + * LogicSig account. Call `sign` or `signMultisig` on the newly created + * LogicSigAccount to make it a delegated account. + * + * @param program - The compiled TEAL program which contains the logic for + * this LogicSig. + * @param args - An optional array of arguments for the program. + */ + constructor(program: Uint8Array, args?: Array | null) { + this.lsig = new LogicSig(program, args); + this.sigkey = undefined; + } + + // eslint-disable-next-line class-methods-use-this + getEncodingSchema(): encoding.Schema { + return LogicSigAccount.encodingSchema; + } + + toEncodingData(): Map { + return new Map([ + ['lsig', this.lsig.toEncodingData()], + ['sigkey', this.sigkey], + ]); + } + + static fromEncodingData(data: unknown): LogicSigAccount { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded logic sig account: ${data}`); + } + const value = data as Map; + const lsig = LogicSig.fromEncodingData(value.get('lsig')); + const lsigAccount = new LogicSigAccount(lsig.logic, lsig.args); + lsigAccount.lsig = lsig; // Restore other properties of the lsig + lsigAccount.sigkey = value.get('sigkey') as Uint8Array; + return lsigAccount; + } + + /** + * Encode this object into msgpack. + */ + toByte(): Uint8Array { + return encoding.encodeMsgpack(this); + } + + /** + * Decode a msgpack object into a LogicSigAccount. + * @param encoded - The encoded LogicSigAccount. + */ + static fromByte(encoded: ArrayLike): LogicSigAccount { + return encoding.decodeMsgpack(encoded, LogicSigAccount); + } + + /** + * Check if this LogicSigAccount has been delegated to another account with a + * signature. + * + * Note this function only checks for the presence of a delegation signature. + * To verify the delegation signature, use `verify`. + */ + isDelegated() { + return !!(this.lsig.sig || this.lsig.msig || this.lsig.lmsig); + } + + /** + * Verifies this LogicSig's program and signatures. + * @returns true if and only if the LogicSig program and signatures are valid. + */ + verify() { + const addr = this.address(); + return this.lsig.verify(addr.publicKey); + } + + /** + * Get the address of this LogicSigAccount. + * + * If the LogicSig is delegated to another account, this will return the + * address of that account. + * + * If the LogicSig is not delegated to another account, this will return an + * escrow address that is the hash of the LogicSig's program code. + */ + address(): Address { + const sigCount = [this.lsig.sig, this.lsig.msig, this.lsig.lmsig].filter( + Boolean + ).length; + if (sigCount > 1) { + throw new Error( + 'LogicSig has too many signatures. At most one of sig, msig, or lmsig may be present' + ); + } + + if (this.lsig.sig) { + if (!this.sigkey) { + throw new Error('Signing key for delegated account is missing'); + } + return new Address(this.sigkey); + } + + const msig = this.lsig.lmsig || this.lsig.msig; + if (msig) { + const msigMetadata = { + version: msig.v, + threshold: msig.thr, + pks: msig.subsig.map((subsig) => subsig.pk), + }; + return addressFromMultisigPreImg(msigMetadata); + } + + return this.lsig.address(); + } + + /** + * Turns this LogicSigAccount into a delegated LogicSig. This type of LogicSig + * has the authority to sign transactions on behalf of another account, called + * the delegating account. Use this function if the delegating account is a + * multisig account. + * + * @param msig - The multisig delegating account + * @param secretKey - The secret key of one of the members of the delegating + * multisig account. Use `appendToMultisig` to add additional signatures + * from other members. + */ + signMultisig(msig: MultisigMetadata, secretKey: Uint8Array) { + this.lsig.sign(secretKey, msig); + } + + /** + * Adds an additional signature from a member of the delegating multisig + * account. + * + * @param secretKey - The secret key of one of the members of the delegating + * multisig account. + */ + appendToMultisig(secretKey: Uint8Array) { + this.lsig.appendToMultisig(secretKey); + } + + /** + * Turns this LogicSigAccount into a delegated LogicSig. This type of LogicSig + * has the authority to sign transactions on behalf of another account, called + * the delegating account. If the delegating account is a multisig account, + * use `signMultisig` instead. + * + * @param secretKey - The secret key of the delegating account. + */ + sign(secretKey: Uint8Array) { + this.lsig.sign(secretKey); + this.sigkey = nacl.keyPairFromSecretKey(secretKey).publicKey; + } +} + +/** + * logicSigFromByte accepts encoded logic sig bytes and attempts to call logicsig.fromByte on it, + * returning the result + */ +export function logicSigFromByte(encoded: Uint8Array): LogicSig { + return encoding.decodeMsgpack(encoded, LogicSig); +} + +const SIGN_PROGRAM_DATA_PREFIX = new TextEncoder().encode('ProgData'); + +/** + * tealSign creates a signature compatible with ed25519verify opcode from program hash + * @param sk - Uint8Array with secret key + * @param data - Uint8Array with data to sign + * @param programHash - string representation of teal program hash (= contract address for LogicSigs) + */ +export function tealSign( + sk: Uint8Array, + data: Uint8Array, + programHash: string | Address +) { + const programAddr = + typeof programHash === 'string' + ? Address.fromString(programHash) + : programHash; + const parts = utils.concatArrays(programAddr.publicKey, data); + const toBeSigned = utils.concatArrays(SIGN_PROGRAM_DATA_PREFIX, parts); + return nacl.sign(toBeSigned, sk); +} + +/** + * verifyTealSign verifies a signature as would the ed25519verify opcode + * @param data - Uint8Array with original signed data + * @param programHash - string representation of teal program hash (= contract address for LogicSigs) + * @param sig - uint8array with the signature to verify (produced by tealSign/tealSignFromProgram) + * @param pk - uint8array with public key to verify against + */ +export function verifyTealSign( + data: Uint8Array, + programHash: string | Address, + sig: Uint8Array, + pk: Uint8Array +) { + const programAddr = + typeof programHash === 'string' + ? Address.fromString(programHash) + : programHash; + const parts = utils.concatArrays(programAddr.publicKey, data); + const toBeSigned = utils.concatArrays(SIGN_PROGRAM_DATA_PREFIX, parts); + return nacl.verify(toBeSigned, sig, pk); +} + +/** + * tealSignFromProgram creates a signature compatible with ed25519verify opcode from raw program bytes + * @param sk - uint8array with secret key + * @param data - Uint8Array with data to sign + * @param program - Uint8Array with teal program + */ +export function tealSignFromProgram( + sk: Uint8Array, + data: Uint8Array, + program: Uint8Array +) { + const lsig = new LogicSig(program); + const contractAddress = lsig.address(); + return tealSign(sk, data, contractAddress); +} diff --git a/packages/sdk/src/makeTxn.ts b/packages/sdk/src/makeTxn.ts new file mode 100644 index 00000000..059e87b7 --- /dev/null +++ b/packages/sdk/src/makeTxn.ts @@ -0,0 +1,946 @@ +import type { Transaction } from '@algorandfoundation/algokit-transact' +import { OnApplicationComplete, TransactionType } from '@algorandfoundation/algokit-transact' +import { foreignArraysToResourceReferences } from './appAccess.js' +import { Address } from './encoding/address.js' +import { + ApplicationCallReferenceParams, + ApplicationCallTransactionParams, + AssetConfigurationTransactionParams, + AssetFreezeTransactionParams, + AssetTransferTransactionParams, + KeyRegistrationTransactionParams, + PaymentTransactionParams, +} from './types/transactions/base.js' + +// Helper function to convert Address to string +function addressToString(addr: string | Address | undefined): string | undefined { + if (!addr) return undefined + return typeof addr === 'string' ? addr : addr.toString() +} + +// Helper function to ensure bigint +function ensureBigInt(value: number | bigint | undefined): bigint | undefined { + if (value === undefined) return undefined + return typeof value === 'bigint' ? value : BigInt(value) +} + +import { TransactionParams as AlgodTransactionParams } from '@algorandfoundation/algokit-algod-client' + +export type SdkTransactionParams = AlgodTransactionParams & { + firstRound: bigint +} + +/** Contains parameters common to every transaction type */ +export interface CommonTransactionParams { + /** Algorand address of sender */ + sender: string | Address + /** Suggested parameters relevant to the network that will accept this transaction */ + suggestedParams: SdkTransactionParams + /** Optional, arbitrary data to be stored in the transaction's note field */ + note?: Uint8Array + /** + * Optional, 32-byte lease to associate with this transaction. + * + * The sender cannot send another transaction with the same lease until the last round of original + * transaction has passed. + */ + lease?: Uint8Array + /** The Algorand address that will be used to authorize all future transactions from the sender, if provided. */ + rekeyTo?: string | Address +} + +/** + * Create a new payment transaction + * + * @param options - Payment transaction parameters + */ +export function makePaymentTxnWithSuggestedParamsFromObject({ + sender, + receiver, + amount, + closeRemainderTo, + suggestedParams, + note, + lease, + rekeyTo, +}: PaymentTransactionParams & CommonTransactionParams): Transaction { + const txn: Transaction = { + type: TransactionType.Payment, + sender: addressToString(sender)!, + firstValid: BigInt(suggestedParams.firstRound), + lastValid: BigInt(suggestedParams.lastRound), + genesisHash: suggestedParams.genesisHash, + genesisId: suggestedParams.genesisId, + note, + lease, + rekeyTo: addressToString(rekeyTo), + payment: { + receiver: addressToString(receiver)!, + amount: ensureBigInt(amount)!, + closeRemainderTo: addressToString(closeRemainderTo), + }, + } + + return txn +} + +/** + * Create a new key registration transaction + * + * @param options - Key registration transaction parameters + */ +export function makeKeyRegistrationTxnWithSuggestedParamsFromObject({ + sender, + voteKey, + selectionKey, + stateProofKey, + voteFirst, + voteLast, + voteKeyDilution, + nonParticipation, + suggestedParams, + note, + lease, + rekeyTo, +}: KeyRegistrationTransactionParams & CommonTransactionParams): Transaction { + const txn: Transaction = { + type: TransactionType.KeyRegistration, + sender: addressToString(sender)!, + firstValid: BigInt(suggestedParams.firstRound), + lastValid: BigInt(suggestedParams.lastRound), + genesisHash: suggestedParams.genesisHash, + genesisId: suggestedParams.genesisId, + note, + lease, + rekeyTo: addressToString(rekeyTo), + keyRegistration: { + voteKey, + selectionKey, + stateProofKey, + voteFirst: ensureBigInt(voteFirst), + voteLast: ensureBigInt(voteLast), + voteKeyDilution: ensureBigInt(voteKeyDilution), + nonParticipation, + }, + } + + return txn +} + +/** + * Base function for creating any type of asset config transaction. + * + * @param options - Asset config transaction parameters + */ +export function makeBaseAssetConfigTxn({ + sender, + assetIndex, + total, + decimals, + defaultFrozen, + manager, + reserve, + freeze, + clawback, + unitName, + assetName, + assetURL, + assetMetadataHash, + note, + lease, + rekeyTo, + suggestedParams, +}: AssetConfigurationTransactionParams & CommonTransactionParams): Transaction { + const txn: Transaction = { + type: TransactionType.AssetConfig, + sender: addressToString(sender)!, + firstValid: BigInt(suggestedParams.firstRound), + lastValid: BigInt(suggestedParams.lastRound), + genesisHash: suggestedParams.genesisHash, + genesisId: suggestedParams.genesisId, + note, + lease, + rekeyTo: addressToString(rekeyTo), + assetConfig: { + assetId: ensureBigInt(assetIndex)!, + total: ensureBigInt(total), + decimals: typeof decimals === 'number' ? decimals : undefined, + defaultFrozen, + manager: addressToString(manager), + reserve: addressToString(reserve), + freeze: addressToString(freeze), + clawback: addressToString(clawback), + unitName, + assetName, + url: assetURL, + metadataHash: assetMetadataHash, + }, + } + + return txn +} + +/** + * Create a new asset creation transaction + * + * @param options - Asset creation transaction parameters + */ +export function makeAssetCreateTxnWithSuggestedParamsFromObject({ + sender, + total, + decimals, + defaultFrozen, + manager, + reserve, + freeze, + clawback, + unitName, + assetName, + assetURL, + assetMetadataHash, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit & CommonTransactionParams): Transaction { + return makeBaseAssetConfigTxn({ + sender, + assetIndex: 0, + total, + decimals, + defaultFrozen, + manager, + reserve, + freeze, + clawback, + unitName, + assetName, + assetURL, + assetMetadataHash, + note, + lease, + rekeyTo, + suggestedParams, + }) +} + +/** Contains asset modification transaction parameters */ +export interface AssetModificationTransactionParams { + /** + * The unique ID of the asset to be modified + */ + assetIndex: number | bigint + + /** + * The Algorand address in charge of reserve, freeze, clawback, destruction, etc. + * + * If empty, this role will be irrevocably removed from this asset. + */ + manager?: string | Address + + /** + * The Algorand address representing asset reserve. + * + * If empty, this role will be irrevocably removed from this asset. + */ + reserve?: string | Address + + /** + * The Algorand address with power to freeze/unfreeze asset holdings. + * + * If empty, this role will be irrevocably removed from this asset. + */ + freeze?: string | Address + + /** + * The Algorand address with power to revoke asset holdings. + * + * If empty, this role will be irrevocably removed from this asset. + */ + clawback?: string | Address + + /** + * This is a safety flag to prevent unintentionally removing a role from an asset. If undefined or + * true, an error will be thrown if any of assetManager, assetReserve, assetFreeze, or + * assetClawback are empty. + * + * Set this to false to allow removing roles by leaving the corresponding address empty. + */ + strictEmptyAddressChecking?: boolean +} + +/** + * Create a new asset config transaction. This transaction can be issued by the asset manager to + * change the manager, reserve, freeze, or clawback address. + * + * You must respecify existing addresses to keep them the same; leaving a field blank is the same as + * turning that feature off for this asset. + * + * @param options - Asset modification transaction parameters + */ +export function makeAssetConfigTxnWithSuggestedParamsFromObject({ + sender, + assetIndex, + manager, + reserve, + freeze, + clawback, + strictEmptyAddressChecking, + note, + lease, + rekeyTo, + suggestedParams, +}: AssetModificationTransactionParams & CommonTransactionParams): Transaction { + if (!assetIndex) { + throw Error('assetIndex must be provided') + } + const strictChecking = strictEmptyAddressChecking ?? true + if (strictChecking && (manager == null || reserve == null || freeze == null || clawback == null)) { + throw Error( + 'strictEmptyAddressChecking is enabled, but an address is empty. If this is intentional, set strictEmptyAddressChecking to false.', + ) + } + return makeBaseAssetConfigTxn({ + sender, + assetIndex, + manager, + reserve, + freeze, + clawback, + note, + lease, + rekeyTo, + suggestedParams, + }) +} + +/** + * Create a new asset destroy transaction. This will allow the asset's manager to remove this asset + * from the ledger, provided all outstanding assets are held by the creator. + * + * @param options - Asset destroy transaction parameters + */ +export function makeAssetDestroyTxnWithSuggestedParamsFromObject({ + sender, + assetIndex, + note, + lease, + rekeyTo, + suggestedParams, +}: Required> & CommonTransactionParams): Transaction { + if (!assetIndex) { + throw Error('assetIndex must be provided') + } + return makeBaseAssetConfigTxn({ + sender, + assetIndex, + note, + lease, + rekeyTo, + suggestedParams, + }) +} + +/** + * Create a new asset freeze transaction. This transaction allows the asset's freeze manager to + * freeze or un-freeze an account, blocking or allowing asset transfers to and from the targeted + * account. + * + * @param options - Asset freeze transaction parameters + */ +export function makeAssetFreezeTxnWithSuggestedParamsFromObject({ + sender, + assetIndex, + freezeTarget, + frozen, + suggestedParams, + note, + lease, + rekeyTo, +}: AssetFreezeTransactionParams & CommonTransactionParams): Transaction { + const txn: Transaction = { + type: TransactionType.AssetFreeze, + sender: addressToString(sender)!, + firstValid: BigInt(suggestedParams.firstRound), + lastValid: BigInt(suggestedParams.lastRound), + genesisHash: suggestedParams.genesisHash, + genesisId: suggestedParams.genesisId, + note, + lease, + rekeyTo: addressToString(rekeyTo), + assetFreeze: { + assetId: ensureBigInt(assetIndex)!, + freezeTarget: addressToString(freezeTarget)!, + frozen, + }, + } + + return txn +} + +/** + * Create a new asset transfer transaction. + * + * Special case: to opt into an assets, set amount=0 and sender=receiver. + * + * @param options - Asset transfer transaction parameters + */ +export function makeAssetTransferTxnWithSuggestedParamsFromObject({ + sender, + receiver, + amount, + closeRemainderTo, + assetSender, + note, + assetIndex, + suggestedParams, + rekeyTo, + lease, +}: AssetTransferTransactionParams & CommonTransactionParams): Transaction { + if (!assetIndex) { + throw Error('assetIndex must be provided') + } + + const txn: Transaction = { + type: TransactionType.AssetTransfer, + sender: addressToString(sender)!, + firstValid: BigInt(suggestedParams.firstRound), + lastValid: BigInt(suggestedParams.lastRound), + genesisHash: suggestedParams.genesisHash, + genesisId: suggestedParams.genesisId, + note, + lease, + rekeyTo: addressToString(rekeyTo), + assetTransfer: { + assetId: ensureBigInt(assetIndex)!, + receiver: addressToString(receiver)!, + amount: ensureBigInt(amount)!, + assetSender: addressToString(assetSender), + closeRemainderTo: addressToString(closeRemainderTo), + }, + } + + return txn +} + +/** + * Base function for creating any application call transaction. + * + * @param options - Application call transaction parameters + */ +export function makeApplicationCallTxnFromObject({ + sender, + appIndex, + onComplete, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + approvalProgram, + clearProgram, + numLocalInts, + numLocalByteSlices, + numGlobalInts, + numGlobalByteSlices, + extraPages, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + rejectVersion, // TODO: handle reject version + note, + lease, + rekeyTo, + suggestedParams, +}: ApplicationCallTransactionParams & CommonTransactionParams & ApplicationCallReferenceParams): Transaction { + if (onComplete == null) { + throw Error('onComplete must be provided') + } + if (access && (accounts || foreignApps || foreignAssets || boxes || holdings || locals)) { + throw Error('cannot specify both access and other access fields') + } + let access2 = access + if (convertToAccess) { + access2 = foreignArraysToResourceReferences({ + appIndex, + accounts, + foreignApps, + foreignAssets, + holdings, + locals, + boxes, + }) + } + + // Convert legacy foreign arrays to new format if access is not provided + const accountReferences = access2 ? undefined : accounts?.map((a) => addressToString(a)!) + const appReferences = access2 ? undefined : foreignApps?.map((a) => ensureBigInt(a)!) + const assetReferences = access2 ? undefined : foreignAssets?.map((a) => ensureBigInt(a)!) + + // Convert boxes if present (boxes have app index and name) + const boxReferences = access2 + ? undefined + : boxes?.map((box) => ({ + appId: ensureBigInt(box.appId) || BigInt(0), + name: box.name, + })) + + const txn: Transaction = { + type: TransactionType.AppCall, + sender: addressToString(sender)!, + firstValid: BigInt(suggestedParams.firstRound), + lastValid: BigInt(suggestedParams.lastRound), + genesisHash: suggestedParams.genesisHash, + genesisId: suggestedParams.genesisId, + note, + lease, + rekeyTo: addressToString(rekeyTo), + appCall: { + appId: ensureBigInt(appIndex) || BigInt(0), + onComplete, + approvalProgram, + clearStateProgram: clearProgram, + globalStateSchema: + numGlobalInts !== undefined || numGlobalByteSlices !== undefined + ? { + numUints: Number(numGlobalInts) || 0, + numByteSlices: Number(numGlobalByteSlices) || 0, + } + : undefined, + localStateSchema: + numLocalInts !== undefined || numLocalByteSlices !== undefined + ? { + numUints: Number(numLocalInts) || 0, + numByteSlices: Number(numLocalByteSlices) || 0, + } + : undefined, + extraProgramPages: extraPages !== undefined ? Number(extraPages) : undefined, + args: appArgs, + // Only pass legacy foreign arrays if access is not provided + accountReferences: access2 ? undefined : accountReferences, + assetReferences: access2 ? undefined : assetReferences, + appReferences: access2 ? undefined : appReferences, + boxReferences: access2 ? undefined : boxReferences, + accessReferences: access2, + }, + } + + return txn +} + +/** + * Make a transaction that will create an application. + * + * @param options - Application creation transaction parameters + */ +export function makeApplicationCreateTxnFromObject({ + sender, + onComplete, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + approvalProgram, + clearProgram, + numLocalInts, + numLocalByteSlices, + numGlobalInts, + numGlobalByteSlices, + extraPages, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit & + Required> & + CommonTransactionParams & + ApplicationCallReferenceParams): Transaction { + if (!approvalProgram || !clearProgram) { + throw Error('approvalProgram and clearProgram must be provided') + } + if (onComplete == null) { + throw Error('onComplete must be provided') + } + return makeApplicationCallTxnFromObject({ + sender, + appIndex: 0, + onComplete, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + approvalProgram, + clearProgram, + numLocalInts, + numLocalByteSlices, + numGlobalInts, + numGlobalByteSlices, + extraPages, + note, + lease, + rekeyTo, + suggestedParams, + }) +} + +/** + * Make a transaction that changes an application's approval and clear programs + * + * @param options - Application update transaction parameters + */ +export function makeApplicationUpdateTxnFromObject({ + sender, + appIndex, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + approvalProgram, + clearProgram, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit< + ApplicationCallTransactionParams, + | 'onComplete' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + | 'extraPages' + | 'approvalProgram' + | 'clearProgram' +> & + Required> & + CommonTransactionParams & + ApplicationCallReferenceParams): Transaction { + if (!appIndex) { + throw Error('appIndex must be provided') + } + if (!approvalProgram || !clearProgram) { + throw Error('approvalProgram and clearProgram must be provided') + } + return makeApplicationCallTxnFromObject({ + sender, + appIndex, + onComplete: OnApplicationComplete.UpdateApplication, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + approvalProgram, + clearProgram, + note, + lease, + rekeyTo, + suggestedParams, + }) +} + +/** + * Make a transaction that deletes an application + * + * @param options - Application deletion transaction parameters + */ +export function makeApplicationDeleteTxnFromObject({ + sender, + appIndex, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit< + ApplicationCallTransactionParams, + | 'onComplete' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + | 'extraPages' + | 'approvalProgram' + | 'clearProgram' +> & + CommonTransactionParams & + ApplicationCallReferenceParams): Transaction { + if (!appIndex) { + throw Error('appIndex must be provided') + } + return makeApplicationCallTxnFromObject({ + sender, + appIndex, + onComplete: OnApplicationComplete.DeleteApplication, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + note, + lease, + rekeyTo, + suggestedParams, + }) +} + +/** + * Make a transaction that opts in to use an application + * + * @param options - Application opt-in transaction parameters + */ +export function makeApplicationOptInTxnFromObject({ + sender, + appIndex, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit< + ApplicationCallTransactionParams, + | 'onComplete' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + | 'extraPages' + | 'approvalProgram' + | 'clearProgram' +> & + CommonTransactionParams & + ApplicationCallReferenceParams): Transaction { + if (!appIndex) { + throw Error('appIndex must be provided') + } + return makeApplicationCallTxnFromObject({ + sender, + appIndex, + onComplete: OnApplicationComplete.OptIn, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + note, + convertToAccess, + holdings, + locals, + access, + lease, + rekeyTo, + suggestedParams, + }) +} + +/** + * Make a transaction that closes out a user's state in an application + * + * @param options - Application close-out transaction parameters + */ +export function makeApplicationCloseOutTxnFromObject({ + sender, + appIndex, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit< + ApplicationCallTransactionParams, + | 'onComplete' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + | 'extraPages' + | 'approvalProgram' + | 'clearProgram' +> & + CommonTransactionParams & + ApplicationCallReferenceParams): Transaction { + if (!appIndex) { + throw Error('appIndex must be provided') + } + return makeApplicationCallTxnFromObject({ + sender, + appIndex, + onComplete: OnApplicationComplete.CloseOut, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + note, + lease, + rekeyTo, + suggestedParams, + }) +} + +/** + * Make a transaction that clears a user's state in an application + * + * @param options - Application clear state transaction parameters + */ +export function makeApplicationClearStateTxnFromObject({ + sender, + appIndex, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit< + ApplicationCallTransactionParams, + | 'onComplete' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + | 'extraPages' + | 'approvalProgram' + | 'clearProgram' +> & + CommonTransactionParams & + ApplicationCallReferenceParams): Transaction { + if (!appIndex) { + throw Error('appIndex must be provided') + } + return makeApplicationCallTxnFromObject({ + sender, + appIndex, + onComplete: OnApplicationComplete.ClearState, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + note, + lease, + rekeyTo, + suggestedParams, + }) +} + +/** + * Make a transaction that just calls an application, doing nothing on completion + * + * @param options - Application no-op transaction parameters + */ +export function makeApplicationNoOpTxnFromObject({ + sender, + appIndex, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + note, + lease, + rekeyTo, + suggestedParams, +}: Omit< + ApplicationCallTransactionParams, + | 'onComplete' + | 'numLocalInts' + | 'numLocalByteSlices' + | 'numGlobalInts' + | 'numGlobalByteSlices' + | 'extraPages' + | 'approvalProgram' + | 'clearProgram' +> & + CommonTransactionParams & + ApplicationCallReferenceParams): Transaction { + if (!appIndex) { + throw Error('appIndex must be provided') + } + return makeApplicationCallTxnFromObject({ + sender, + appIndex, + onComplete: OnApplicationComplete.NoOp, + appArgs, + accounts, + foreignApps, + foreignAssets, + boxes, + convertToAccess, + holdings, + locals, + access, + note, + lease, + rekeyTo, + suggestedParams, + }) +} diff --git a/packages/sdk/src/mnemonic/mnemonic.ts b/packages/sdk/src/mnemonic/mnemonic.ts new file mode 100644 index 00000000..0b9adade --- /dev/null +++ b/packages/sdk/src/mnemonic/mnemonic.ts @@ -0,0 +1,181 @@ +/* eslint-disable no-bitwise */ +import english from './wordlists/english.js'; +import * as nacl from '../nacl/naclWrappers.js'; +import { Address } from '../encoding/address.js'; +import Account from '../types/account.js'; + +export const FAIL_TO_DECODE_MNEMONIC_ERROR_MSG = 'failed to decode mnemonic'; +export const NOT_IN_WORDS_LIST_ERROR_MSG = + 'the mnemonic contains a word that is not in the wordlist'; + +// https://stackoverflow.com/a/51452614 +function toUint11Array(buffer8: Uint8Array | number[]): number[] { + const buffer11: number[] = []; + let acc = 0; + let accBits = 0; + function add(octet: number) { + acc |= octet << accBits; + accBits += 8; + if (accBits >= 11) { + buffer11.push(acc & 0x7ff); + acc >>= 11; + accBits -= 11; + } + } + function flush() { + if (accBits) { + buffer11.push(acc); + } + } + + buffer8.forEach(add); + flush(); + return buffer11; +} + +function applyWords(nums: number[]): string[] { + return nums.map((n) => english[n]); +} + +function computeChecksum(seed: Uint8Array): string { + const hashBuffer = nacl.genericHash(seed); + const uint11Hash = toUint11Array(hashBuffer); + const words = applyWords(uint11Hash); + + return words[0]; +} + +/** + * mnemonicFromSeed converts a 32-byte key into a 25 word mnemonic. The generated mnemonic includes a checksum. + * Each word in the mnemonic represents 11 bits of data, and the last 11 bits are reserved for the checksum. + * @param seed - 32 bytes long seed + * @returns 25 words mnemonic + */ +export function mnemonicFromSeed(seed: Uint8Array) { + // Sanity length check + if (seed.length !== nacl.SEED_BTYES_LENGTH) { + throw new RangeError(`Seed length must be ${nacl.SEED_BTYES_LENGTH}`); + } + + const uint11Array = toUint11Array(seed); + const words = applyWords(uint11Array); + const checksumWord = computeChecksum(seed); + + return `${words.join(' ')} ${checksumWord}`; +} + +// from Uint11Array +// https://stackoverflow.com/a/51452614 +function toUint8Array(buffer11: number[]): Uint8Array { + const buffer8: number[] = []; + let acc = 0; + let accBits = 0; + function add(ui11: number) { + acc |= ui11 << accBits; + accBits += 11; + while (accBits >= 8) { + buffer8.push(acc & 0xff); + acc >>= 8; + accBits -= 8; + } + } + function flush() { + if (accBits) { + buffer8.push(acc); + } + } + + buffer11.forEach(add); + flush(); + return new Uint8Array(buffer8); +} + +/** + * seedFromMnemonic converts a mnemonic generated using this library into the source key used to create it. + * It returns an error if the passed mnemonic has an incorrect checksum, if the number of words is unexpected, or if one + * of the passed words is not found in the words list. + * @param mnemonic - 25 words mnemonic + * @returns 32 bytes long seed + */ +export function seedFromMnemonic(mnemonic: string) { + const words = mnemonic.split(' '); + const key = words.slice(0, 24); + + // Check that all words are in list + for (const w of key) { + if (english.indexOf(w) === -1) throw new Error(NOT_IN_WORDS_LIST_ERROR_MSG); + } + + const checksum = words[words.length - 1]; + const uint11Array = key.map((word) => english.indexOf(word)); + + // Convert the key to uint8Array + let uint8Array = toUint8Array(uint11Array); + + // We need to chop the last byte - + // the short explanation - Since 256 is not divisible by 11, we have an extra 0x0 byte. + // The longer explanation - When splitting the 256 bits to chunks of 11, we get 23 words and a left over of 3 bits. + // This left gets padded with another 8 bits to the create the 24th word. + // While converting back to byte array, our new 264 bits array is divisible by 8 but the last byte is just the padding. + + // check that we have 33 bytes long array as expected + if (uint8Array.length !== 33) + throw new Error(FAIL_TO_DECODE_MNEMONIC_ERROR_MSG); + + // check that the last byte is actually 0x0 + if (uint8Array[uint8Array.length - 1] !== 0x0) + throw new Error(FAIL_TO_DECODE_MNEMONIC_ERROR_MSG); + + // chop it ! + uint8Array = uint8Array.slice(0, uint8Array.length - 1); + + // compute checksum + const cs = computeChecksum(uint8Array); + + // success! + if (cs === checksum) return uint8Array; + + throw new Error(FAIL_TO_DECODE_MNEMONIC_ERROR_MSG); +} + +/** + * mnemonicToSecretKey takes a mnemonic string and returns the corresponding Algorand address and its secret key. + * @param mn - 25 words Algorand mnemonic + * @throws error if fails to decode the mnemonic + */ +export function mnemonicToSecretKey(mn: string): Account { + const seed = seedFromMnemonic(mn); + const keys = nacl.keyPairFromSeed(seed); + const addr = new Address(keys.publicKey); + return { addr, sk: keys.secretKey }; +} + +/** + * secretKeyToMnemonic takes an Algorand secret key and returns the corresponding mnemonic. + * @param sk - Algorand secret key + * @returns Secret key's associated mnemonic + */ +export function secretKeyToMnemonic(sk: Uint8Array) { + // get the seed from the sk + const seed = sk.slice(0, nacl.SEED_BTYES_LENGTH); + return mnemonicFromSeed(seed); +} + +/** + * mnemonicToMasterDerivationKey takes a mnemonic string and returns the corresponding master derivation key. + * @param mn - 25 words Algorand mnemonic + * @returns Uint8Array + * @throws error if fails to decode the mnemonic + */ +export function mnemonicToMasterDerivationKey(mn: string) { + return seedFromMnemonic(mn); +} + +/** + * masterDerivationKeyToMnemonic takes a master derivation key and returns the corresponding mnemonic. + * @param mdk - Uint8Array + * @returns string mnemonic + */ +export function masterDerivationKeyToMnemonic(mdk: Uint8Array) { + return mnemonicFromSeed(mdk); +} diff --git a/packages/sdk/src/mnemonic/wordlists/english.ts b/packages/sdk/src/mnemonic/wordlists/english.ts new file mode 100644 index 00000000..99954e85 --- /dev/null +++ b/packages/sdk/src/mnemonic/wordlists/english.ts @@ -0,0 +1,2052 @@ +const english = [ + 'abandon', + 'ability', + 'able', + 'about', + 'above', + 'absent', + 'absorb', + 'abstract', + 'absurd', + 'abuse', + 'access', + 'accident', + 'account', + 'accuse', + 'achieve', + 'acid', + 'acoustic', + 'acquire', + 'across', + 'act', + 'action', + 'actor', + 'actress', + 'actual', + 'adapt', + 'add', + 'addict', + 'address', + 'adjust', + 'admit', + 'adult', + 'advance', + 'advice', + 'aerobic', + 'affair', + 'afford', + 'afraid', + 'again', + 'age', + 'agent', + 'agree', + 'ahead', + 'aim', + 'air', + 'airport', + 'aisle', + 'alarm', + 'album', + 'alcohol', + 'alert', + 'alien', + 'all', + 'alley', + 'allow', + 'almost', + 'alone', + 'alpha', + 'already', + 'also', + 'alter', + 'always', + 'amateur', + 'amazing', + 'among', + 'amount', + 'amused', + 'analyst', + 'anchor', + 'ancient', + 'anger', + 'angle', + 'angry', + 'animal', + 'ankle', + 'announce', + 'annual', + 'another', + 'answer', + 'antenna', + 'antique', + 'anxiety', + 'any', + 'apart', + 'apology', + 'appear', + 'apple', + 'approve', + 'april', + 'arch', + 'arctic', + 'area', + 'arena', + 'argue', + 'arm', + 'armed', + 'armor', + 'army', + 'around', + 'arrange', + 'arrest', + 'arrive', + 'arrow', + 'art', + 'artefact', + 'artist', + 'artwork', + 'ask', + 'aspect', + 'assault', + 'asset', + 'assist', + 'assume', + 'asthma', + 'athlete', + 'atom', + 'attack', + 'attend', + 'attitude', + 'attract', + 'auction', + 'audit', + 'august', + 'aunt', + 'author', + 'auto', + 'autumn', + 'average', + 'avocado', + 'avoid', + 'awake', + 'aware', + 'away', + 'awesome', + 'awful', + 'awkward', + 'axis', + 'baby', + 'bachelor', + 'bacon', + 'badge', + 'bag', + 'balance', + 'balcony', + 'ball', + 'bamboo', + 'banana', + 'banner', + 'bar', + 'barely', + 'bargain', + 'barrel', + 'base', + 'basic', + 'basket', + 'battle', + 'beach', + 'bean', + 'beauty', + 'because', + 'become', + 'beef', + 'before', + 'begin', + 'behave', + 'behind', + 'believe', + 'below', + 'belt', + 'bench', + 'benefit', + 'best', + 'betray', + 'better', + 'between', + 'beyond', + 'bicycle', + 'bid', + 'bike', + 'bind', + 'biology', + 'bird', + 'birth', + 'bitter', + 'black', + 'blade', + 'blame', + 'blanket', + 'blast', + 'bleak', + 'bless', + 'blind', + 'blood', + 'blossom', + 'blouse', + 'blue', + 'blur', + 'blush', + 'board', + 'boat', + 'body', + 'boil', + 'bomb', + 'bone', + 'bonus', + 'book', + 'boost', + 'border', + 'boring', + 'borrow', + 'boss', + 'bottom', + 'bounce', + 'box', + 'boy', + 'bracket', + 'brain', + 'brand', + 'brass', + 'brave', + 'bread', + 'breeze', + 'brick', + 'bridge', + 'brief', + 'bright', + 'bring', + 'brisk', + 'broccoli', + 'broken', + 'bronze', + 'broom', + 'brother', + 'brown', + 'brush', + 'bubble', + 'buddy', + 'budget', + 'buffalo', + 'build', + 'bulb', + 'bulk', + 'bullet', + 'bundle', + 'bunker', + 'burden', + 'burger', + 'burst', + 'bus', + 'business', + 'busy', + 'butter', + 'buyer', + 'buzz', + 'cabbage', + 'cabin', + 'cable', + 'cactus', + 'cage', + 'cake', + 'call', + 'calm', + 'camera', + 'camp', + 'can', + 'canal', + 'cancel', + 'candy', + 'cannon', + 'canoe', + 'canvas', + 'canyon', + 'capable', + 'capital', + 'captain', + 'car', + 'carbon', + 'card', + 'cargo', + 'carpet', + 'carry', + 'cart', + 'case', + 'cash', + 'casino', + 'castle', + 'casual', + 'cat', + 'catalog', + 'catch', + 'category', + 'cattle', + 'caught', + 'cause', + 'caution', + 'cave', + 'ceiling', + 'celery', + 'cement', + 'census', + 'century', + 'cereal', + 'certain', + 'chair', + 'chalk', + 'champion', + 'change', + 'chaos', + 'chapter', + 'charge', + 'chase', + 'chat', + 'cheap', + 'check', + 'cheese', + 'chef', + 'cherry', + 'chest', + 'chicken', + 'chief', + 'child', + 'chimney', + 'choice', + 'choose', + 'chronic', + 'chuckle', + 'chunk', + 'churn', + 'cigar', + 'cinnamon', + 'circle', + 'citizen', + 'city', + 'civil', + 'claim', + 'clap', + 'clarify', + 'claw', + 'clay', + 'clean', + 'clerk', + 'clever', + 'click', + 'client', + 'cliff', + 'climb', + 'clinic', + 'clip', + 'clock', + 'clog', + 'close', + 'cloth', + 'cloud', + 'clown', + 'club', + 'clump', + 'cluster', + 'clutch', + 'coach', + 'coast', + 'coconut', + 'code', + 'coffee', + 'coil', + 'coin', + 'collect', + 'color', + 'column', + 'combine', + 'come', + 'comfort', + 'comic', + 'common', + 'company', + 'concert', + 'conduct', + 'confirm', + 'congress', + 'connect', + 'consider', + 'control', + 'convince', + 'cook', + 'cool', + 'copper', + 'copy', + 'coral', + 'core', + 'corn', + 'correct', + 'cost', + 'cotton', + 'couch', + 'country', + 'couple', + 'course', + 'cousin', + 'cover', + 'coyote', + 'crack', + 'cradle', + 'craft', + 'cram', + 'crane', + 'crash', + 'crater', + 'crawl', + 'crazy', + 'cream', + 'credit', + 'creek', + 'crew', + 'cricket', + 'crime', + 'crisp', + 'critic', + 'crop', + 'cross', + 'crouch', + 'crowd', + 'crucial', + 'cruel', + 'cruise', + 'crumble', + 'crunch', + 'crush', + 'cry', + 'crystal', + 'cube', + 'culture', + 'cup', + 'cupboard', + 'curious', + 'current', + 'curtain', + 'curve', + 'cushion', + 'custom', + 'cute', + 'cycle', + 'dad', + 'damage', + 'damp', + 'dance', + 'danger', + 'daring', + 'dash', + 'daughter', + 'dawn', + 'day', + 'deal', + 'debate', + 'debris', + 'decade', + 'december', + 'decide', + 'decline', + 'decorate', + 'decrease', + 'deer', + 'defense', + 'define', + 'defy', + 'degree', + 'delay', + 'deliver', + 'demand', + 'demise', + 'denial', + 'dentist', + 'deny', + 'depart', + 'depend', + 'deposit', + 'depth', + 'deputy', + 'derive', + 'describe', + 'desert', + 'design', + 'desk', + 'despair', + 'destroy', + 'detail', + 'detect', + 'develop', + 'device', + 'devote', + 'diagram', + 'dial', + 'diamond', + 'diary', + 'dice', + 'diesel', + 'diet', + 'differ', + 'digital', + 'dignity', + 'dilemma', + 'dinner', + 'dinosaur', + 'direct', + 'dirt', + 'disagree', + 'discover', + 'disease', + 'dish', + 'dismiss', + 'disorder', + 'display', + 'distance', + 'divert', + 'divide', + 'divorce', + 'dizzy', + 'doctor', + 'document', + 'dog', + 'doll', + 'dolphin', + 'domain', + 'donate', + 'donkey', + 'donor', + 'door', + 'dose', + 'double', + 'dove', + 'draft', + 'dragon', + 'drama', + 'drastic', + 'draw', + 'dream', + 'dress', + 'drift', + 'drill', + 'drink', + 'drip', + 'drive', + 'drop', + 'drum', + 'dry', + 'duck', + 'dumb', + 'dune', + 'during', + 'dust', + 'dutch', + 'duty', + 'dwarf', + 'dynamic', + 'eager', + 'eagle', + 'early', + 'earn', + 'earth', + 'easily', + 'east', + 'easy', + 'echo', + 'ecology', + 'economy', + 'edge', + 'edit', + 'educate', + 'effort', + 'egg', + 'eight', + 'either', + 'elbow', + 'elder', + 'electric', + 'elegant', + 'element', + 'elephant', + 'elevator', + 'elite', + 'else', + 'embark', + 'embody', + 'embrace', + 'emerge', + 'emotion', + 'employ', + 'empower', + 'empty', + 'enable', + 'enact', + 'end', + 'endless', + 'endorse', + 'enemy', + 'energy', + 'enforce', + 'engage', + 'engine', + 'enhance', + 'enjoy', + 'enlist', + 'enough', + 'enrich', + 'enroll', + 'ensure', + 'enter', + 'entire', + 'entry', + 'envelope', + 'episode', + 'equal', + 'equip', + 'era', + 'erase', + 'erode', + 'erosion', + 'error', + 'erupt', + 'escape', + 'essay', + 'essence', + 'estate', + 'eternal', + 'ethics', + 'evidence', + 'evil', + 'evoke', + 'evolve', + 'exact', + 'example', + 'excess', + 'exchange', + 'excite', + 'exclude', + 'excuse', + 'execute', + 'exercise', + 'exhaust', + 'exhibit', + 'exile', + 'exist', + 'exit', + 'exotic', + 'expand', + 'expect', + 'expire', + 'explain', + 'expose', + 'express', + 'extend', + 'extra', + 'eye', + 'eyebrow', + 'fabric', + 'face', + 'faculty', + 'fade', + 'faint', + 'faith', + 'fall', + 'false', + 'fame', + 'family', + 'famous', + 'fan', + 'fancy', + 'fantasy', + 'farm', + 'fashion', + 'fat', + 'fatal', + 'father', + 'fatigue', + 'fault', + 'favorite', + 'feature', + 'february', + 'federal', + 'fee', + 'feed', + 'feel', + 'female', + 'fence', + 'festival', + 'fetch', + 'fever', + 'few', + 'fiber', + 'fiction', + 'field', + 'figure', + 'file', + 'film', + 'filter', + 'final', + 'find', + 'fine', + 'finger', + 'finish', + 'fire', + 'firm', + 'first', + 'fiscal', + 'fish', + 'fit', + 'fitness', + 'fix', + 'flag', + 'flame', + 'flash', + 'flat', + 'flavor', + 'flee', + 'flight', + 'flip', + 'float', + 'flock', + 'floor', + 'flower', + 'fluid', + 'flush', + 'fly', + 'foam', + 'focus', + 'fog', + 'foil', + 'fold', + 'follow', + 'food', + 'foot', + 'force', + 'forest', + 'forget', + 'fork', + 'fortune', + 'forum', + 'forward', + 'fossil', + 'foster', + 'found', + 'fox', + 'fragile', + 'frame', + 'frequent', + 'fresh', + 'friend', + 'fringe', + 'frog', + 'front', + 'frost', + 'frown', + 'frozen', + 'fruit', + 'fuel', + 'fun', + 'funny', + 'furnace', + 'fury', + 'future', + 'gadget', + 'gain', + 'galaxy', + 'gallery', + 'game', + 'gap', + 'garage', + 'garbage', + 'garden', + 'garlic', + 'garment', + 'gas', + 'gasp', + 'gate', + 'gather', + 'gauge', + 'gaze', + 'general', + 'genius', + 'genre', + 'gentle', + 'genuine', + 'gesture', + 'ghost', + 'giant', + 'gift', + 'giggle', + 'ginger', + 'giraffe', + 'girl', + 'give', + 'glad', + 'glance', + 'glare', + 'glass', + 'glide', + 'glimpse', + 'globe', + 'gloom', + 'glory', + 'glove', + 'glow', + 'glue', + 'goat', + 'goddess', + 'gold', + 'good', + 'goose', + 'gorilla', + 'gospel', + 'gossip', + 'govern', + 'gown', + 'grab', + 'grace', + 'grain', + 'grant', + 'grape', + 'grass', + 'gravity', + 'great', + 'green', + 'grid', + 'grief', + 'grit', + 'grocery', + 'group', + 'grow', + 'grunt', + 'guard', + 'guess', + 'guide', + 'guilt', + 'guitar', + 'gun', + 'gym', + 'habit', + 'hair', + 'half', + 'hammer', + 'hamster', + 'hand', + 'happy', + 'harbor', + 'hard', + 'harsh', + 'harvest', + 'hat', + 'have', + 'hawk', + 'hazard', + 'head', + 'health', + 'heart', + 'heavy', + 'hedgehog', + 'height', + 'hello', + 'helmet', + 'help', + 'hen', + 'hero', + 'hidden', + 'high', + 'hill', + 'hint', + 'hip', + 'hire', + 'history', + 'hobby', + 'hockey', + 'hold', + 'hole', + 'holiday', + 'hollow', + 'home', + 'honey', + 'hood', + 'hope', + 'horn', + 'horror', + 'horse', + 'hospital', + 'host', + 'hotel', + 'hour', + 'hover', + 'hub', + 'huge', + 'human', + 'humble', + 'humor', + 'hundred', + 'hungry', + 'hunt', + 'hurdle', + 'hurry', + 'hurt', + 'husband', + 'hybrid', + 'ice', + 'icon', + 'idea', + 'identify', + 'idle', + 'ignore', + 'ill', + 'illegal', + 'illness', + 'image', + 'imitate', + 'immense', + 'immune', + 'impact', + 'impose', + 'improve', + 'impulse', + 'inch', + 'include', + 'income', + 'increase', + 'index', + 'indicate', + 'indoor', + 'industry', + 'infant', + 'inflict', + 'inform', + 'inhale', + 'inherit', + 'initial', + 'inject', + 'injury', + 'inmate', + 'inner', + 'innocent', + 'input', + 'inquiry', + 'insane', + 'insect', + 'inside', + 'inspire', + 'install', + 'intact', + 'interest', + 'into', + 'invest', + 'invite', + 'involve', + 'iron', + 'island', + 'isolate', + 'issue', + 'item', + 'ivory', + 'jacket', + 'jaguar', + 'jar', + 'jazz', + 'jealous', + 'jeans', + 'jelly', + 'jewel', + 'job', + 'join', + 'joke', + 'journey', + 'joy', + 'judge', + 'juice', + 'jump', + 'jungle', + 'junior', + 'junk', + 'just', + 'kangaroo', + 'keen', + 'keep', + 'ketchup', + 'key', + 'kick', + 'kid', + 'kidney', + 'kind', + 'kingdom', + 'kiss', + 'kit', + 'kitchen', + 'kite', + 'kitten', + 'kiwi', + 'knee', + 'knife', + 'knock', + 'know', + 'lab', + 'label', + 'labor', + 'ladder', + 'lady', + 'lake', + 'lamp', + 'language', + 'laptop', + 'large', + 'later', + 'latin', + 'laugh', + 'laundry', + 'lava', + 'law', + 'lawn', + 'lawsuit', + 'layer', + 'lazy', + 'leader', + 'leaf', + 'learn', + 'leave', + 'lecture', + 'left', + 'leg', + 'legal', + 'legend', + 'leisure', + 'lemon', + 'lend', + 'length', + 'lens', + 'leopard', + 'lesson', + 'letter', + 'level', + 'liar', + 'liberty', + 'library', + 'license', + 'life', + 'lift', + 'light', + 'like', + 'limb', + 'limit', + 'link', + 'lion', + 'liquid', + 'list', + 'little', + 'live', + 'lizard', + 'load', + 'loan', + 'lobster', + 'local', + 'lock', + 'logic', + 'lonely', + 'long', + 'loop', + 'lottery', + 'loud', + 'lounge', + 'love', + 'loyal', + 'lucky', + 'luggage', + 'lumber', + 'lunar', + 'lunch', + 'luxury', + 'lyrics', + 'machine', + 'mad', + 'magic', + 'magnet', + 'maid', + 'mail', + 'main', + 'major', + 'make', + 'mammal', + 'man', + 'manage', + 'mandate', + 'mango', + 'mansion', + 'manual', + 'maple', + 'marble', + 'march', + 'margin', + 'marine', + 'market', + 'marriage', + 'mask', + 'mass', + 'master', + 'match', + 'material', + 'math', + 'matrix', + 'matter', + 'maximum', + 'maze', + 'meadow', + 'mean', + 'measure', + 'meat', + 'mechanic', + 'medal', + 'media', + 'melody', + 'melt', + 'member', + 'memory', + 'mention', + 'menu', + 'mercy', + 'merge', + 'merit', + 'merry', + 'mesh', + 'message', + 'metal', + 'method', + 'middle', + 'midnight', + 'milk', + 'million', + 'mimic', + 'mind', + 'minimum', + 'minor', + 'minute', + 'miracle', + 'mirror', + 'misery', + 'miss', + 'mistake', + 'mix', + 'mixed', + 'mixture', + 'mobile', + 'model', + 'modify', + 'mom', + 'moment', + 'monitor', + 'monkey', + 'monster', + 'month', + 'moon', + 'moral', + 'more', + 'morning', + 'mosquito', + 'mother', + 'motion', + 'motor', + 'mountain', + 'mouse', + 'move', + 'movie', + 'much', + 'muffin', + 'mule', + 'multiply', + 'muscle', + 'museum', + 'mushroom', + 'music', + 'must', + 'mutual', + 'myself', + 'mystery', + 'myth', + 'naive', + 'name', + 'napkin', + 'narrow', + 'nasty', + 'nation', + 'nature', + 'near', + 'neck', + 'need', + 'negative', + 'neglect', + 'neither', + 'nephew', + 'nerve', + 'nest', + 'net', + 'network', + 'neutral', + 'never', + 'news', + 'next', + 'nice', + 'night', + 'noble', + 'noise', + 'nominee', + 'noodle', + 'normal', + 'north', + 'nose', + 'notable', + 'note', + 'nothing', + 'notice', + 'novel', + 'now', + 'nuclear', + 'number', + 'nurse', + 'nut', + 'oak', + 'obey', + 'object', + 'oblige', + 'obscure', + 'observe', + 'obtain', + 'obvious', + 'occur', + 'ocean', + 'october', + 'odor', + 'off', + 'offer', + 'office', + 'often', + 'oil', + 'okay', + 'old', + 'olive', + 'olympic', + 'omit', + 'once', + 'one', + 'onion', + 'online', + 'only', + 'open', + 'opera', + 'opinion', + 'oppose', + 'option', + 'orange', + 'orbit', + 'orchard', + 'order', + 'ordinary', + 'organ', + 'orient', + 'original', + 'orphan', + 'ostrich', + 'other', + 'outdoor', + 'outer', + 'output', + 'outside', + 'oval', + 'oven', + 'over', + 'own', + 'owner', + 'oxygen', + 'oyster', + 'ozone', + 'pact', + 'paddle', + 'page', + 'pair', + 'palace', + 'palm', + 'panda', + 'panel', + 'panic', + 'panther', + 'paper', + 'parade', + 'parent', + 'park', + 'parrot', + 'party', + 'pass', + 'patch', + 'path', + 'patient', + 'patrol', + 'pattern', + 'pause', + 'pave', + 'payment', + 'peace', + 'peanut', + 'pear', + 'peasant', + 'pelican', + 'pen', + 'penalty', + 'pencil', + 'people', + 'pepper', + 'perfect', + 'permit', + 'person', + 'pet', + 'phone', + 'photo', + 'phrase', + 'physical', + 'piano', + 'picnic', + 'picture', + 'piece', + 'pig', + 'pigeon', + 'pill', + 'pilot', + 'pink', + 'pioneer', + 'pipe', + 'pistol', + 'pitch', + 'pizza', + 'place', + 'planet', + 'plastic', + 'plate', + 'play', + 'please', + 'pledge', + 'pluck', + 'plug', + 'plunge', + 'poem', + 'poet', + 'point', + 'polar', + 'pole', + 'police', + 'pond', + 'pony', + 'pool', + 'popular', + 'portion', + 'position', + 'possible', + 'post', + 'potato', + 'pottery', + 'poverty', + 'powder', + 'power', + 'practice', + 'praise', + 'predict', + 'prefer', + 'prepare', + 'present', + 'pretty', + 'prevent', + 'price', + 'pride', + 'primary', + 'print', + 'priority', + 'prison', + 'private', + 'prize', + 'problem', + 'process', + 'produce', + 'profit', + 'program', + 'project', + 'promote', + 'proof', + 'property', + 'prosper', + 'protect', + 'proud', + 'provide', + 'public', + 'pudding', + 'pull', + 'pulp', + 'pulse', + 'pumpkin', + 'punch', + 'pupil', + 'puppy', + 'purchase', + 'purity', + 'purpose', + 'purse', + 'push', + 'put', + 'puzzle', + 'pyramid', + 'quality', + 'quantum', + 'quarter', + 'question', + 'quick', + 'quit', + 'quiz', + 'quote', + 'rabbit', + 'raccoon', + 'race', + 'rack', + 'radar', + 'radio', + 'rail', + 'rain', + 'raise', + 'rally', + 'ramp', + 'ranch', + 'random', + 'range', + 'rapid', + 'rare', + 'rate', + 'rather', + 'raven', + 'raw', + 'razor', + 'ready', + 'real', + 'reason', + 'rebel', + 'rebuild', + 'recall', + 'receive', + 'recipe', + 'record', + 'recycle', + 'reduce', + 'reflect', + 'reform', + 'refuse', + 'region', + 'regret', + 'regular', + 'reject', + 'relax', + 'release', + 'relief', + 'rely', + 'remain', + 'remember', + 'remind', + 'remove', + 'render', + 'renew', + 'rent', + 'reopen', + 'repair', + 'repeat', + 'replace', + 'report', + 'require', + 'rescue', + 'resemble', + 'resist', + 'resource', + 'response', + 'result', + 'retire', + 'retreat', + 'return', + 'reunion', + 'reveal', + 'review', + 'reward', + 'rhythm', + 'rib', + 'ribbon', + 'rice', + 'rich', + 'ride', + 'ridge', + 'rifle', + 'right', + 'rigid', + 'ring', + 'riot', + 'ripple', + 'risk', + 'ritual', + 'rival', + 'river', + 'road', + 'roast', + 'robot', + 'robust', + 'rocket', + 'romance', + 'roof', + 'rookie', + 'room', + 'rose', + 'rotate', + 'rough', + 'round', + 'route', + 'royal', + 'rubber', + 'rude', + 'rug', + 'rule', + 'run', + 'runway', + 'rural', + 'sad', + 'saddle', + 'sadness', + 'safe', + 'sail', + 'salad', + 'salmon', + 'salon', + 'salt', + 'salute', + 'same', + 'sample', + 'sand', + 'satisfy', + 'satoshi', + 'sauce', + 'sausage', + 'save', + 'say', + 'scale', + 'scan', + 'scare', + 'scatter', + 'scene', + 'scheme', + 'school', + 'science', + 'scissors', + 'scorpion', + 'scout', + 'scrap', + 'screen', + 'script', + 'scrub', + 'sea', + 'search', + 'season', + 'seat', + 'second', + 'secret', + 'section', + 'security', + 'seed', + 'seek', + 'segment', + 'select', + 'sell', + 'seminar', + 'senior', + 'sense', + 'sentence', + 'series', + 'service', + 'session', + 'settle', + 'setup', + 'seven', + 'shadow', + 'shaft', + 'shallow', + 'share', + 'shed', + 'shell', + 'sheriff', + 'shield', + 'shift', + 'shine', + 'ship', + 'shiver', + 'shock', + 'shoe', + 'shoot', + 'shop', + 'short', + 'shoulder', + 'shove', + 'shrimp', + 'shrug', + 'shuffle', + 'shy', + 'sibling', + 'sick', + 'side', + 'siege', + 'sight', + 'sign', + 'silent', + 'silk', + 'silly', + 'silver', + 'similar', + 'simple', + 'since', + 'sing', + 'siren', + 'sister', + 'situate', + 'six', + 'size', + 'skate', + 'sketch', + 'ski', + 'skill', + 'skin', + 'skirt', + 'skull', + 'slab', + 'slam', + 'sleep', + 'slender', + 'slice', + 'slide', + 'slight', + 'slim', + 'slogan', + 'slot', + 'slow', + 'slush', + 'small', + 'smart', + 'smile', + 'smoke', + 'smooth', + 'snack', + 'snake', + 'snap', + 'sniff', + 'snow', + 'soap', + 'soccer', + 'social', + 'sock', + 'soda', + 'soft', + 'solar', + 'soldier', + 'solid', + 'solution', + 'solve', + 'someone', + 'song', + 'soon', + 'sorry', + 'sort', + 'soul', + 'sound', + 'soup', + 'source', + 'south', + 'space', + 'spare', + 'spatial', + 'spawn', + 'speak', + 'special', + 'speed', + 'spell', + 'spend', + 'sphere', + 'spice', + 'spider', + 'spike', + 'spin', + 'spirit', + 'split', + 'spoil', + 'sponsor', + 'spoon', + 'sport', + 'spot', + 'spray', + 'spread', + 'spring', + 'spy', + 'square', + 'squeeze', + 'squirrel', + 'stable', + 'stadium', + 'staff', + 'stage', + 'stairs', + 'stamp', + 'stand', + 'start', + 'state', + 'stay', + 'steak', + 'steel', + 'stem', + 'step', + 'stereo', + 'stick', + 'still', + 'sting', + 'stock', + 'stomach', + 'stone', + 'stool', + 'story', + 'stove', + 'strategy', + 'street', + 'strike', + 'strong', + 'struggle', + 'student', + 'stuff', + 'stumble', + 'style', + 'subject', + 'submit', + 'subway', + 'success', + 'such', + 'sudden', + 'suffer', + 'sugar', + 'suggest', + 'suit', + 'summer', + 'sun', + 'sunny', + 'sunset', + 'super', + 'supply', + 'supreme', + 'sure', + 'surface', + 'surge', + 'surprise', + 'surround', + 'survey', + 'suspect', + 'sustain', + 'swallow', + 'swamp', + 'swap', + 'swarm', + 'swear', + 'sweet', + 'swift', + 'swim', + 'swing', + 'switch', + 'sword', + 'symbol', + 'symptom', + 'syrup', + 'system', + 'table', + 'tackle', + 'tag', + 'tail', + 'talent', + 'talk', + 'tank', + 'tape', + 'target', + 'task', + 'taste', + 'tattoo', + 'taxi', + 'teach', + 'team', + 'tell', + 'ten', + 'tenant', + 'tennis', + 'tent', + 'term', + 'test', + 'text', + 'thank', + 'that', + 'theme', + 'then', + 'theory', + 'there', + 'they', + 'thing', + 'this', + 'thought', + 'three', + 'thrive', + 'throw', + 'thumb', + 'thunder', + 'ticket', + 'tide', + 'tiger', + 'tilt', + 'timber', + 'time', + 'tiny', + 'tip', + 'tired', + 'tissue', + 'title', + 'toast', + 'tobacco', + 'today', + 'toddler', + 'toe', + 'together', + 'toilet', + 'token', + 'tomato', + 'tomorrow', + 'tone', + 'tongue', + 'tonight', + 'tool', + 'tooth', + 'top', + 'topic', + 'topple', + 'torch', + 'tornado', + 'tortoise', + 'toss', + 'total', + 'tourist', + 'toward', + 'tower', + 'town', + 'toy', + 'track', + 'trade', + 'traffic', + 'tragic', + 'train', + 'transfer', + 'trap', + 'trash', + 'travel', + 'tray', + 'treat', + 'tree', + 'trend', + 'trial', + 'tribe', + 'trick', + 'trigger', + 'trim', + 'trip', + 'trophy', + 'trouble', + 'truck', + 'true', + 'truly', + 'trumpet', + 'trust', + 'truth', + 'try', + 'tube', + 'tuition', + 'tumble', + 'tuna', + 'tunnel', + 'turkey', + 'turn', + 'turtle', + 'twelve', + 'twenty', + 'twice', + 'twin', + 'twist', + 'two', + 'type', + 'typical', + 'ugly', + 'umbrella', + 'unable', + 'unaware', + 'uncle', + 'uncover', + 'under', + 'undo', + 'unfair', + 'unfold', + 'unhappy', + 'uniform', + 'unique', + 'unit', + 'universe', + 'unknown', + 'unlock', + 'until', + 'unusual', + 'unveil', + 'update', + 'upgrade', + 'uphold', + 'upon', + 'upper', + 'upset', + 'urban', + 'urge', + 'usage', + 'use', + 'used', + 'useful', + 'useless', + 'usual', + 'utility', + 'vacant', + 'vacuum', + 'vague', + 'valid', + 'valley', + 'valve', + 'van', + 'vanish', + 'vapor', + 'various', + 'vast', + 'vault', + 'vehicle', + 'velvet', + 'vendor', + 'venture', + 'venue', + 'verb', + 'verify', + 'version', + 'very', + 'vessel', + 'veteran', + 'viable', + 'vibrant', + 'vicious', + 'victory', + 'video', + 'view', + 'village', + 'vintage', + 'violin', + 'virtual', + 'virus', + 'visa', + 'visit', + 'visual', + 'vital', + 'vivid', + 'vocal', + 'voice', + 'void', + 'volcano', + 'volume', + 'vote', + 'voyage', + 'wage', + 'wagon', + 'wait', + 'walk', + 'wall', + 'walnut', + 'want', + 'warfare', + 'warm', + 'warrior', + 'wash', + 'wasp', + 'waste', + 'water', + 'wave', + 'way', + 'wealth', + 'weapon', + 'wear', + 'weasel', + 'weather', + 'web', + 'wedding', + 'weekend', + 'weird', + 'welcome', + 'west', + 'wet', + 'whale', + 'what', + 'wheat', + 'wheel', + 'when', + 'where', + 'whip', + 'whisper', + 'wide', + 'width', + 'wife', + 'wild', + 'will', + 'win', + 'window', + 'wine', + 'wing', + 'wink', + 'winner', + 'winter', + 'wire', + 'wisdom', + 'wise', + 'wish', + 'witness', + 'wolf', + 'woman', + 'wonder', + 'wood', + 'wool', + 'word', + 'work', + 'world', + 'worry', + 'worth', + 'wrap', + 'wreck', + 'wrestle', + 'wrist', + 'write', + 'wrong', + 'yard', + 'year', + 'yellow', + 'you', + 'young', + 'youth', + 'zebra', + 'zero', + 'zone', + 'zoo', +]; + +export default english; diff --git a/packages/sdk/src/multisig.ts b/packages/sdk/src/multisig.ts new file mode 100644 index 00000000..6bde6f58 --- /dev/null +++ b/packages/sdk/src/multisig.ts @@ -0,0 +1,182 @@ +import * as nacl from './nacl/naclWrappers.js'; +import { + Address, + ALGORAND_ADDRESS_BYTE_LENGTH, + ALGORAND_CHECKSUM_BYTE_LENGTH, +} from './encoding/address.js'; +import * as utils from './utils/utils.js'; +import { EncodedMultisig } from './types/transactions/encoded.js'; + +/** + Utilities for manipulating multisig transaction blobs. + */ + +/** + * Required options for creating a multisignature + * + * Documentation available at: https://developer.algorand.org/docs/get-details/transactions/signatures/#multisignatures + */ +export interface MultisigMetadata { + /** + * Multisig version + */ + version: number; + + /** + * Multisig threshold value. Authorization requires a subset of signatures, + * equal to or greater than the threshold value. + */ + threshold: number; + + /** + * A list of Algorand addresses representing possible signers for this multisig. Order is important. + */ + addrs: Array; +} + +// Convert "MultisigAddr" UTF-8 to byte array +const MULTISIG_PREIMG2ADDR_PREFIX = new Uint8Array([ + 77, 117, 108, 116, 105, 115, 105, 103, 65, 100, 100, 114, +]); + +const INVALID_MSIG_VERSION_ERROR_MSG = 'invalid multisig version'; +const INVALID_MSIG_THRESHOLD_ERROR_MSG = 'bad multisig threshold'; +const INVALID_MSIG_PK_ERROR_MSG = 'bad multisig public key - wrong length'; +const UNEXPECTED_PK_LEN_ERROR_MSG = 'nacl public key length is not 32 bytes'; + +export function pksFromAddresses(addrs: Array): Uint8Array[] { + return addrs.map((addr) => { + if (typeof addr === 'string') { + return Address.fromString(addr).publicKey; + } + return addr.publicKey; + }); +} + +/** + * fromMultisigPreImg takes multisig parameters and returns a 32 byte typed array public key, + * representing an address that identifies the "exact group, version, and public keys" that are required for signing. + * Hash("MultisigAddr" || version uint8 || threshold uint8 || PK1 || PK2 || ...) + * Encoding this output yields a human readable address. + * @param version - multisig version + * @param threshold - multisig threshold + * @param pks - array of typed array public keys + */ +export function addressFromMultisigPreImg({ + version, + threshold, + pks, +}: Omit & { + pks: Uint8Array[]; +}): Address { + if (version !== 1 || version > 255 || version < 0) { + // ^ a tad redundant, but in case in the future version != 1, still check for uint8 + throw new Error(INVALID_MSIG_VERSION_ERROR_MSG); + } + if ( + threshold === 0 || + pks.length === 0 || + threshold > pks.length || + threshold > 255 + ) { + throw new Error(INVALID_MSIG_THRESHOLD_ERROR_MSG); + } + const pkLen = ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH; + if (pkLen !== nacl.PUBLIC_KEY_LENGTH) { + throw new Error(UNEXPECTED_PK_LEN_ERROR_MSG); + } + const merged = new Uint8Array( + MULTISIG_PREIMG2ADDR_PREFIX.length + 2 + pkLen * pks.length + ); + merged.set(MULTISIG_PREIMG2ADDR_PREFIX, 0); + merged.set([version], MULTISIG_PREIMG2ADDR_PREFIX.length); + merged.set([threshold], MULTISIG_PREIMG2ADDR_PREFIX.length + 1); + for (let i = 0; i < pks.length; i++) { + if (pks[i].length !== pkLen) { + throw new Error(INVALID_MSIG_PK_ERROR_MSG); + } + merged.set(pks[i], MULTISIG_PREIMG2ADDR_PREFIX.length + 2 + i * pkLen); + } + return new Address(Uint8Array.from(nacl.genericHash(merged))); +} + +/** + * fromMultisigPreImgAddrs takes multisig parameters and returns a human readable Algorand address. + * This is equivalent to fromMultisigPreImg, but interfaces with encoded addresses. + * @param version - multisig version + * @param threshold - multisig threshold + * @param addrs - array of encoded addresses + */ +export function addressFromMultisigPreImgAddrs({ + version, + threshold, + addrs, +}: MultisigMetadata): Address { + const pks = pksFromAddresses(addrs); + return addressFromMultisigPreImg({ version, threshold, pks }); +} + +export function verifyMultisig( + toBeVerified: Uint8Array, + msig: EncodedMultisig, + publicKey: Uint8Array +) { + const version = msig.v; + const threshold = msig.thr; + const subsigs = msig.subsig; + + const pks = subsigs.map((subsig) => subsig.pk); + if (msig.subsig.length < threshold) { + return false; + } + + let pk: Uint8Array; + try { + pk = addressFromMultisigPreImg({ version, threshold, pks }).publicKey; + } catch (e) { + return false; + } + + if (!utils.arrayEqual(pk, publicKey)) { + return false; + } + + let counter = 0; + for (const subsig of subsigs) { + if (subsig.s !== undefined) { + counter += 1; + } + } + if (counter < threshold) { + return false; + } + + let verifiedCounter = 0; + for (const subsig of subsigs) { + if (subsig.s !== undefined) { + if (nacl.verify(toBeVerified, subsig.s, subsig.pk)) { + verifiedCounter += 1; + } + } + } + + if (verifiedCounter < threshold) { + return false; + } + + return true; +} + +/** + * multisigAddress takes multisig metadata (preimage) and returns the corresponding human readable Algorand address. + * @param version - multisig version + * @param threshold - multisig threshold + * @param addrs - list of Algorand addresses + */ +export function multisigAddress({ + version, + threshold, + addrs, +}: MultisigMetadata): Address { + return addressFromMultisigPreImgAddrs({ version, threshold, addrs }); +} diff --git a/packages/sdk/src/multisigSigning.ts b/packages/sdk/src/multisigSigning.ts new file mode 100644 index 00000000..0d9bfdf7 --- /dev/null +++ b/packages/sdk/src/multisigSigning.ts @@ -0,0 +1,319 @@ +import type { MultisigSignature, MultisigSubsignature, SignedTransaction, Transaction } from '@algorandfoundation/algokit-transact' +import { decodeSignedTransaction, encodeSignedTransaction, encodeTransaction, getTransactionId } from '@algorandfoundation/algokit-transact' +import { Address } from './encoding/address.js' +import { MultisigMetadata, addressFromMultisigPreImg, pksFromAddresses } from './multisig.js' +import * as nacl from './nacl/naclWrappers.js' +import * as utils from './utils/utils.js' + +export const MULTISIG_MERGE_LESSTHANTWO_ERROR_MSG = 'Not enough multisig transactions to merge. Need at least two' +export const MULTISIG_MERGE_MISMATCH_ERROR_MSG = 'Cannot merge txs. txIDs differ' +export const MULTISIG_MERGE_MISMATCH_AUTH_ADDR_MSG = 'Cannot merge txs. Auth addrs differ' +export const MULTISIG_MERGE_WRONG_PREIMAGE_ERROR_MSG = 'Cannot merge txs. Multisig preimages differ' +export const MULTISIG_MERGE_SIG_MISMATCH_ERROR_MSG = 'Cannot merge txs. subsigs are mismatched.' +export const MULTISIG_NO_MUTATE_ERROR_MSG = 'Cannot mutate a multisig field as it would invalidate all existing signatures.' +export const MULTISIG_USE_PARTIAL_SIGN_ERROR_MSG = 'Cannot sign a multisig transaction using `signTxn`. Use `partialSignTxn` instead.' +export const MULTISIG_SIGNATURE_LENGTH_ERROR_MSG = 'Cannot add multisig signature. Signature is not of the correct length.' +const MULTISIG_KEY_NOT_EXIST_ERROR_MSG = 'Key does not exist' + +/** + * createMultisigTransaction creates a raw, unsigned multisig transaction blob. + * @param txn - the actual transaction. + * @param version - multisig version + * @param threshold - multisig threshold + * @param pks - ordered list of public keys in this multisig + * @returns encoded multisig blob + */ +export function createMultisigTransaction(txn: Transaction, { version, threshold, addrs }: MultisigMetadata) { + // construct the appendable multisigned transaction format + const pks = pksFromAddresses(addrs) + const subsignatures: MultisigSubsignature[] = pks.map((pk) => ({ + address: new Address(pk).toString(), + signature: undefined, + })) + + const multiSignature: MultisigSignature = { + version, + threshold, + subsignatures, + } + + // if the address of this multisig is different from the transaction sender, + // we need to add the auth-addr field + const msigAddr = addressFromMultisigPreImg({ + version, + threshold, + pks, + }) + let authAddress: string | undefined + if (msigAddr.toString() !== txn.sender) { + authAddress = msigAddr.toString() + } + + const signedTxn: SignedTransaction = { + txn: txn, + multiSignature, + authAddress, + } + + return encodeSignedTransaction(signedTxn) +} + +interface MultisigOptions { + rawSig: Uint8Array + myPk: Uint8Array +} + +interface MultisigMetadataWithPks extends Omit { + pks: Uint8Array[] +} + +/** + * createMultisigTransactionWithSignature creates a multisig transaction blob with an included signature. + * @param txn - the actual transaction to sign. + * @param rawSig - a Uint8Array raw signature of that transaction + * @param myPk - a public key that corresponds with rawSig + * @param version - multisig version + * @param threshold - multisig threshold + * @param pks - ordered list of public keys in this multisig + * @returns encoded multisig blob + */ +function createMultisigTransactionWithSignature( + txn: Transaction, + { rawSig, myPk }: MultisigOptions, + { version, threshold, pks }: MultisigMetadataWithPks, +): Uint8Array { + // Create an empty encoded multisig transaction + const encodedMsig = createMultisigTransaction(txn, { + version, + threshold, + addrs: pks.map((pk) => new Address(pk)), + }) + // note: this is not signed yet, but will be shortly + const signedTxn = decodeSignedTransaction(encodedMsig) + + let keyExist = false + + // append the multisig signature to the corresponding public key in the multisig blob + const updatedSubsigs = signedTxn.multiSignature!.subsignatures.map((subsig) => { + if (Address.fromString(subsig.address).publicKey.every((byte, idx) => byte === myPk[idx])) { + keyExist = true + return { ...subsig, signature: rawSig } + } + return subsig + }) + + if (!keyExist) { + throw new Error(MULTISIG_KEY_NOT_EXIST_ERROR_MSG) + } + + const updatedSignedTxn: SignedTransaction = { + ...signedTxn, + multiSignature: { + ...signedTxn.multiSignature!, + subsignatures: updatedSubsigs, + }, + } + + return encodeSignedTransaction(updatedSignedTxn) +} + +/** + * partialSignTxn partially signs this transaction and returns a partially-signed multisig transaction, + * encoded with msgpack as a typed array. + * @param transaction - The transaction to sign + * @param version - multisig version + * @param threshold - multisig threshold + * @param pks - multisig public key list, order is important. + * @param sk - an Algorand secret key to sign with. + * @returns an encoded, partially signed multisig transaction. + */ +function partialSignTxn(transaction: Transaction, { version, threshold, pks }: MultisigMetadataWithPks, sk: Uint8Array) { + // get signature verifier + const myPk = nacl.keyPairFromSecretKey(sk).publicKey + const bytesToSign = encodeTransaction(transaction) + const rawSig = nacl.sign(bytesToSign, sk) + return createMultisigTransactionWithSignature(transaction, { rawSig, myPk }, { version, threshold, pks }) +} + +/** + * partialSignWithMultisigSignature partially signs this transaction with an external raw multisig signature and returns + * a partially-signed multisig transaction, encoded with msgpack as a typed array. + * @param transaction - The transaction to sign + * @param metadata - multisig metadata + * @param signerAddr - address of the signer + * @param signature - raw multisig signature + * @returns an encoded, partially signed multisig transaction. + */ +function partialSignWithMultisigSignature( + transaction: Transaction, + metadata: MultisigMetadataWithPks, + signerAddr: string | Address, + signature: Uint8Array, +) { + if (!nacl.isValidSignatureLength(signature.length)) { + throw new Error(MULTISIG_SIGNATURE_LENGTH_ERROR_MSG) + } + const signerAddressObj = typeof signerAddr === 'string' ? Address.fromString(signerAddr) : signerAddr + return createMultisigTransactionWithSignature( + transaction, + { + rawSig: signature, + myPk: signerAddressObj.publicKey, + }, + metadata, + ) +} + +/** + * mergeMultisigTransactions takes a list of multisig transaction blobs, and merges them. + * @param multisigTxnBlobs - a list of blobs representing encoded multisig txns + * @returns typed array msg-pack encoded multisig txn + */ +export function mergeMultisigTransactions(multisigTxnBlobs: Uint8Array[]) { + if (multisigTxnBlobs.length < 2) { + throw new Error(MULTISIG_MERGE_LESSTHANTWO_ERROR_MSG) + } + const refSigTx = decodeSignedTransaction(multisigTxnBlobs[0]) + if (!refSigTx.multiSignature) { + throw new Error('Invalid multisig transaction, multisig structure missing at index 0') + } + const refTxID = getTransactionId(refSigTx.txn) + const refAuthAddr = refSigTx.authAddress + const refPreImage = { + version: refSigTx.multiSignature.version, + threshold: refSigTx.multiSignature.threshold, + pks: refSigTx.multiSignature.subsignatures.map((subsig) => Address.fromString(subsig.address).publicKey), + } + const refMsigAddr = addressFromMultisigPreImg(refPreImage) + + const newSubsigs: MultisigSubsignature[] = refSigTx.multiSignature.subsignatures.map((sig) => ({ ...sig })) + for (let i = 1; i < multisigTxnBlobs.length; i++) { + const unisig = decodeSignedTransaction(multisigTxnBlobs[i]) + if (!unisig.multiSignature) { + throw new Error(`Invalid multisig transaction, multisig structure missing at index ${i}`) + } + + if (getTransactionId(unisig.txn) !== refTxID) { + throw new Error(MULTISIG_MERGE_MISMATCH_ERROR_MSG) + } + + const authAddr = unisig.authAddress + if (refAuthAddr !== authAddr) { + throw new Error(MULTISIG_MERGE_MISMATCH_AUTH_ADDR_MSG) + } + + // check multisig has same preimage as reference + if (unisig.multiSignature.subsignatures.length !== refSigTx.multiSignature.subsignatures.length) { + throw new Error(MULTISIG_MERGE_WRONG_PREIMAGE_ERROR_MSG) + } + const preimg: MultisigMetadataWithPks = { + version: unisig.multiSignature.version, + threshold: unisig.multiSignature.threshold, + pks: unisig.multiSignature.subsignatures.map((subsig) => Address.fromString(subsig.address).publicKey), + } + const msgigAddr = addressFromMultisigPreImg(preimg) + if (refMsigAddr.toString() !== msgigAddr.toString()) { + throw new Error(MULTISIG_MERGE_WRONG_PREIMAGE_ERROR_MSG) + } + + // now, we can merge + unisig.multiSignature.subsignatures.forEach((uniSubsig, index) => { + if (!uniSubsig.signature) return + const current = newSubsigs[index] + if (current.signature && !utils.arrayEqual(uniSubsig.signature, current.signature)) { + // mismatch + throw new Error(MULTISIG_MERGE_SIG_MISMATCH_ERROR_MSG) + } + current.signature = uniSubsig.signature + }) + } + + const multiSignature: MultisigSignature = { + version: refSigTx.multiSignature.version, + threshold: refSigTx.multiSignature.threshold, + subsignatures: newSubsigs, + } + + const signedTxn: SignedTransaction = { + txn: refSigTx.txn, + multiSignature, + authAddress: refAuthAddr, + } + + return encodeSignedTransaction(signedTxn) +} + +/** + * signMultisigTransaction takes a raw transaction (see signTransaction), a multisig preimage, a secret key, and returns + * a multisig transaction, which is a blob representing a transaction and multisignature account preimage. The returned + * multisig txn can accumulate additional signatures through mergeMultisigTransactions or appendSignMultisigTransaction. + * @param txn - object with either payment or key registration fields + * @param version - multisig version + * @param threshold - multisig threshold + * @param addrs - a list of Algorand addresses representing possible signers for this multisig. Order is important. + * @param sk - Algorand secret key. The corresponding pk should be in the pre image. + * @returns object containing txID, and blob of partially signed multisig transaction (with multisig preimage information) + * If the final calculated fee is lower than the protocol minimum fee, the fee will be increased to match the minimum. + */ +export function signMultisigTransaction(txn: Transaction, { version, threshold, addrs }: MultisigMetadata, sk: Uint8Array) { + // build pks for partialSign + const pks = pksFromAddresses(addrs) + const blob = partialSignTxn(txn, { version, threshold, pks }, sk) + return { + txID: getTransactionId(txn), + blob, + } +} + +/** + * appendSignMultisigTransaction takes a multisig transaction blob, and appends our signature to it. + * While we could derive public key preimagery from the partially-signed multisig transaction, + * we ask the caller to pass it back in, to ensure they know what they are signing. + * @param multisigTxnBlob - an encoded multisig txn. Supports non-payment txn types. + * @param version - multisig version + * @param threshold - multisig threshold + * @param addrs - a list of Algorand addresses representing possible signers for this multisig. Order is important. + * @param sk - Algorand secret key + * @returns object containing txID, and blob representing encoded multisig txn + */ +export function appendSignMultisigTransaction( + multisigTxnBlob: Uint8Array, + { version, threshold, addrs }: MultisigMetadata, + sk: Uint8Array, +) { + const pks = pksFromAddresses(addrs) + // obtain underlying txn, sign it, and merge it + const multisigTxObj = decodeSignedTransaction(multisigTxnBlob) + const partialSignedBlob = partialSignTxn(multisigTxObj.txn, { version, threshold, pks }, sk) + return { + txID: getTransactionId(multisigTxObj.txn), + blob: mergeMultisigTransactions([multisigTxnBlob, partialSignedBlob]), + } +} + +/** + * appendMultisigTransactionSignature takes a multisig transaction blob, and appends a given raw signature to it. + * This makes it possible to compile a multisig signature using only raw signatures from external methods. + * @param multisigTxnBlob - an encoded multisig txn. Supports non-payment txn types. + * @param version - multisig version + * @param threshold - multisig threshold + * @param addrs - a list of Algorand addresses representing possible signers for this multisig. Order is important. + * @param signerAddr - address of the signer + * @param signature - raw multisig signature + * @returns object containing txID, and blob representing encoded multisig txn + */ +export function appendSignRawMultisigSignature( + multisigTxnBlob: Uint8Array, + { version, threshold, addrs }: MultisigMetadata, + signerAddr: string | Address, + signature: Uint8Array, +) { + const pks = pksFromAddresses(addrs) + // obtain underlying txn, sign it, and merge it + const multisigTxObj = decodeSignedTransaction(multisigTxnBlob) + const partialSignedBlob = partialSignWithMultisigSignature(multisigTxObj.txn, { version, threshold, pks }, signerAddr, signature) + return { + txID: getTransactionId(multisigTxObj.txn), + blob: mergeMultisigTransactions([multisigTxnBlob, partialSignedBlob]), + } +} diff --git a/packages/sdk/src/nacl/naclWrappers.ts b/packages/sdk/src/nacl/naclWrappers.ts new file mode 100644 index 00000000..00d85811 --- /dev/null +++ b/packages/sdk/src/nacl/naclWrappers.ts @@ -0,0 +1,55 @@ +import nacl from 'tweetnacl'; +import sha512 from 'js-sha512'; +import { isReactNative } from '../utils/utils.js'; + +export function genericHash(arr: sha512.Message) { + return sha512.sha512_256.array(arr); +} + +export function randomBytes(length: number) { + if (isReactNative()) { + console.warn( + `It looks like you're running in react-native. In order to perform common crypto operations you will need to polyfill common operations such as crypto.getRandomValues` + ); + } + return nacl.randomBytes(length); +} + +export function keyPairFromSeed(seed: Uint8Array) { + return nacl.sign.keyPair.fromSeed(seed); +} + +export function keyPair() { + const seed = randomBytes(nacl.box.secretKeyLength); + return keyPairFromSeed(seed); +} + +export function isValidSignatureLength(len: number) { + return len === nacl.sign.signatureLength; +} + +export function keyPairFromSecretKey(sk: Uint8Array) { + return nacl.sign.keyPair.fromSecretKey(sk); +} + +export function sign(msg: Uint8Array, secretKey: Uint8Array) { + return nacl.sign.detached(msg, secretKey); +} + +export function bytesEqual(a: Uint8Array, b: Uint8Array) { + return nacl.verify(a, b); +} + +export function verify( + message: Uint8Array, + signature: Uint8Array, + verifyKey: Uint8Array +) { + return nacl.sign.detached.verify(message, signature, verifyKey); +} + +// constants +export const PUBLIC_KEY_LENGTH = nacl.sign.publicKeyLength; +export const SECRET_KEY_LENGTH = nacl.sign.secretKeyLength; +export const HASH_BYTES_LENGTH = 32; +export const SEED_BTYES_LENGTH = 32; diff --git a/packages/sdk/src/signer.ts b/packages/sdk/src/signer.ts new file mode 100644 index 00000000..15bdeba4 --- /dev/null +++ b/packages/sdk/src/signer.ts @@ -0,0 +1,129 @@ +import type { SignedTransaction, Transaction } from '@algorandfoundation/algokit-transact' +import { encodeSignedTransaction, encodeTransaction } from '@algorandfoundation/algokit-transact' +import { LogicSigAccount } from './logicsig.js' +import { MultisigMetadata } from './multisig.js' +import { mergeMultisigTransactions, signMultisigTransaction } from './multisigSigning.js' +import * as nacl from './nacl/naclWrappers.js' +import { signLogicSigTransactionObject } from './signing.js' +import Account from './types/account.js' + +/** + * This type represents a function which can sign transactions from an atomic transaction group. + * @param txnGroup - The atomic group containing transactions to be signed + * @param indexesToSign - An array of indexes in the atomic transaction group that should be signed + * @returns A promise which resolves an array of encoded signed transactions. The length of the + * array will be the same as the length of indexesToSign, and each index i in the array + * corresponds to the signed transaction from txnGroup[indexesToSign[i]] + */ +export type TransactionSigner = (txnGroup: Transaction[], indexesToSign: number[]) => Promise + +/** + * Create a TransactionSigner that can sign transactions for the provided basic Account. + */ +export function makeBasicAccountTransactionSigner(account: Account): TransactionSigner { + return (txnGroup: Transaction[], indexesToSign: number[]) => { + const signed: Uint8Array[] = [] + + for (const index of indexesToSign) { + const txn = txnGroup[index] + const authAddress = account.addr.toString() + + // Sign transaction using nacl + const bytesToSign = encodeTransaction(txn) + const signature = nacl.sign(bytesToSign, account.sk) + + const signedTxn: SignedTransaction = { + txn: txn, + signature, + authAddress: authAddress !== txn.sender ? authAddress : undefined, + } + + signed.push(encodeSignedTransaction(signedTxn)) + } + + return Promise.resolve(signed) + } +} + +/** + * Create a TransactionSigner that can sign transactions for the provided LogicSigAccount. + */ +export function makeLogicSigAccountTransactionSigner(account: LogicSigAccount): TransactionSigner { + return (txnGroup: Transaction[], indexesToSign: number[]) => { + const signed: Uint8Array[] = [] + + for (const index of indexesToSign) { + const { blob } = signLogicSigTransactionObject(txnGroup[index], account) + signed.push(blob) + } + + return Promise.resolve(signed) + } +} + +/** + * Create a TransactionSigner that can sign transactions for the provided Multisig account. + * @param msig - The Multisig account metadata + * @param sks - An array of private keys belonging to the msig which should sign the transactions. + */ +export function makeMultiSigAccountTransactionSigner(msig: MultisigMetadata, sks: Uint8Array[]): TransactionSigner { + return (txnGroup: Transaction[], indexesToSign: number[]) => { + const signed: Uint8Array[] = [] + + for (const index of indexesToSign) { + const txn = txnGroup[index] + const partialSigs: Uint8Array[] = [] + + for (const sk of sks) { + const { blob } = signMultisigTransaction(txn, msig, sk) + partialSigs.push(blob) + } + + if (partialSigs.length > 1) { + signed.push(mergeMultisigTransactions(partialSigs)) + } else { + signed.push(partialSigs[0]) + } + } + + return Promise.resolve(signed) + } +} + +/** + * Create a makeEmptyTransactionSigner that does not specify any signer or + * signing capabilities. This should only be used to simulate transactions. + */ +export function makeEmptyTransactionSigner(): TransactionSigner { + return (txnGroup: Transaction[], indexesToSign: number[]) => { + const unsigned: Uint8Array[] = [] + + for (const index of indexesToSign) { + const stxn: SignedTransaction = { + txn: txnGroup[index], + signature: new Uint8Array(64), + } + unsigned.push(encodeSignedTransaction(stxn)) + } + + return Promise.resolve(unsigned) + } +} + +/** Represents an unsigned transactions and a signer that can authorize that transaction. */ +export interface TransactionWithSigner { + /** An unsigned transaction */ + txn: Transaction + /** A transaction signer that can authorize txn */ + signer: TransactionSigner +} + +/** + * Check if a value conforms to the TransactionWithSigner structure. + * @param value - The value to check. + * @returns True if an only if the value has the structure of a TransactionWithSigner. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function isTransactionWithSigner(value: any): value is TransactionWithSigner { + return typeof value === 'object' && Object.keys(value).length === 2 && typeof value.txn === 'object' && typeof value.signer === 'function' +} diff --git a/packages/sdk/src/signing.ts b/packages/sdk/src/signing.ts new file mode 100644 index 00000000..06b8dcf0 --- /dev/null +++ b/packages/sdk/src/signing.ts @@ -0,0 +1,101 @@ +import type { LogicSignature, SignedTransaction, Transaction } from '@algorandfoundation/algokit-transact' +import { encodeSignedTransaction, getTransactionId } from '@algorandfoundation/algokit-transact' +import { Address } from './encoding/address.js' +import { LogicSig, LogicSigAccount } from './logicsig.js' +import { addressFromMultisigPreImg } from './multisig.js' + +function signLogicSigTransactionWithAddress(txn: Transaction, lsig: LogicSig, lsigAddress: Address) { + if (!lsig.verify(lsigAddress.publicKey)) { + throw new Error('Logic signature verification failed. Ensure the program and signature are valid.') + } + + // Convert Address to string for comparison + const lsigAddressStr = lsigAddress.toString() + let authAddress: string | undefined + if (lsigAddressStr !== txn.sender) { + authAddress = lsigAddressStr + } + + // Create LogicSignature from LogicSig + const logicSignature: LogicSignature = { + logic: lsig.logic, + args: lsig.args, + signature: lsig.sig, + multiSignature: lsig.lmsig + ? { + version: lsig.lmsig.v, + threshold: lsig.lmsig.thr, + subsignatures: lsig.lmsig.subsig.map((subsig) => ({ + address: new Address(subsig.pk).toString(), + signature: subsig.s, + })), + } + : undefined, + } + + const signedTxn: SignedTransaction = { + txn: txn, + logicSignature, + authAddress, + } + + return { + txID: getTransactionId(txn), + blob: encodeSignedTransaction(signedTxn), + } +} + +/** + * signLogicSigTransactionObject takes a transaction and a LogicSig object and + * returns a signed transaction. + * + * @param txn - The transaction to sign. + * @param lsigObject - The LogicSig object that will sign the transaction. + * + * @returns Object containing txID and blob representing signed transaction. + */ +export function signLogicSigTransactionObject(txn: Transaction, lsigObject: LogicSig | LogicSigAccount) { + let lsig: LogicSig + let lsigAddress: Address + + if (lsigObject instanceof LogicSigAccount) { + lsig = lsigObject.lsig + lsigAddress = lsigObject.address() + } else { + lsig = lsigObject + + if (lsig.sig) { + // For a LogicSig with a non-multisig delegating account, we cannot derive + // the address of that account from only its signature, so assume the + // delegating account is the sender. If that's not the case, the signing + // will fail. + // Convert sender string to Address + lsigAddress = Address.fromString(txn.sender) + } else if (lsig.lmsig) { + const msigMetadata = { + version: lsig.lmsig.v, + threshold: lsig.lmsig.thr, + pks: lsig.lmsig.subsig.map((subsig) => subsig.pk), + } + lsigAddress = addressFromMultisigPreImg(msigMetadata) + } else { + lsigAddress = lsig.address() + } + } + + return signLogicSigTransactionWithAddress(txn, lsig, lsigAddress) +} + +/** + * signLogicSigTransaction takes a transaction and a LogicSig object and returns + * a signed transaction. + * + * @param txn - The transaction to sign. + * @param lsigObject - The LogicSig object that will sign the transaction. + * + * @returns Object containing txID and blob representing signed transaction. + * @throws error on failure + */ +export function signLogicSigTransaction(txn: Transaction, lsigObject: LogicSig | LogicSigAccount) { + return signLogicSigTransactionObject(txn, lsigObject) +} diff --git a/packages/sdk/src/stateproof.ts b/packages/sdk/src/stateproof.ts new file mode 100644 index 00000000..bf1e17d2 --- /dev/null +++ b/packages/sdk/src/stateproof.ts @@ -0,0 +1,595 @@ +import { Encodable, Schema } from './encoding/encoding.js'; +import { + Uint64Schema, + ByteArraySchema, + FixedLengthByteArraySchema, + ArraySchema, + NamedMapSchema, + Uint64MapSchema, + allOmitEmpty, + convertMap, +} from './encoding/schema/index.js'; + +export class HashFactory implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 't', valueSchema: new Uint64Schema() }, // hashType + ]) + ); + + public hashType: number; + + public constructor(params: { hashType: number }) { + this.hashType = params.hashType; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return HashFactory.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([['t', this.hashType]]); + } + + public static fromEncodingData(data: unknown): HashFactory { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded HashFactory: ${data}`); + } + return new HashFactory({ + hashType: Number(data.get('t')), + }); + } +} + +export class MerkleArrayProof implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'pth', // path + valueSchema: new ArraySchema(new ByteArraySchema()), + }, + { + key: 'hsh', // hashFactory + valueSchema: HashFactory.encodingSchema, + }, + { + key: 'td', // treeDepth + valueSchema: new Uint64Schema(), + }, + ]) + ); + + /** + * Path is bounded by MaxNumLeavesOnEncodedTree since there could be multiple reveals, and + * given the distribution of the elt positions and the depth of the tree, the path length can + * increase up to 2^MaxEncodedTreeDepth / 2 + */ + public path: Uint8Array[]; + + public hashFactory: HashFactory; + + /** + * TreeDepth represents the depth of the tree that is being proven. It is the number of edges + * from the root to a leaf. + */ + public treeDepth: number; + + public constructor(params: { + path: Uint8Array[]; + hashFactory: HashFactory; + treeDepth: number; + }) { + this.path = params.path; + this.hashFactory = params.hashFactory; + this.treeDepth = params.treeDepth; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return MerkleArrayProof.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['pth', this.path], + ['hsh', this.hashFactory.toEncodingData()], + ['td', this.treeDepth], + ]); + } + + public static fromEncodingData(data: unknown): MerkleArrayProof { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded MerkleArrayProof: ${data}`); + } + return new MerkleArrayProof({ + path: data.get('pth'), + hashFactory: HashFactory.fromEncodingData(data.get('hsh')), + treeDepth: Number(data.get('td')), + }); + } +} + +/** + * MerkleSignatureVerifier is used to verify a merkle signature. + */ +export class MerkleSignatureVerifier implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'cmt', // commitment + valueSchema: new FixedLengthByteArraySchema(64), + }, + { + key: 'lf', // keyLifetime + valueSchema: new Uint64Schema(), + }, + ]) + ); + + public commitment: Uint8Array; + + public keyLifetime: bigint; + + public constructor(params: { commitment: Uint8Array; keyLifetime: bigint }) { + this.commitment = params.commitment; + this.keyLifetime = params.keyLifetime; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return MerkleSignatureVerifier.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['cmt', this.commitment], + ['lf', this.keyLifetime], + ]); + } + + public static fromEncodingData(data: unknown): MerkleSignatureVerifier { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded MerkleSignatureVerifier: ${data}`); + } + return new MerkleSignatureVerifier({ + commitment: data.get('cmt'), + keyLifetime: data.get('lf'), + }); + } +} + +/** + * A Participant corresponds to an account whose AccountData.Status is Online, and for which the + * expected sigRound satisfies AccountData.VoteFirstValid <= sigRound <= AccountData.VoteLastValid. + * + * In the Algorand ledger, it is possible for multiple accounts to have the same PK. Thus, the PK is + * not necessarily unique among Participants. However, each account will produce a unique Participant + * struct, to avoid potential DoS attacks where one account claims to have the same VoteID PK as + * another account. + */ +export class Participant implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'p', // pk + valueSchema: MerkleSignatureVerifier.encodingSchema, + }, + { + key: 'w', // weight + valueSchema: new Uint64Schema(), + }, + ]) + ); + + /** + * pk is the identifier used to verify the signature for a specific participant + */ + public pk: MerkleSignatureVerifier; + + /** + * weight is AccountData.MicroAlgos. + */ + public weight: bigint; + + public constructor(params: { pk: MerkleSignatureVerifier; weight: bigint }) { + this.pk = params.pk; + this.weight = params.weight; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return Participant.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['p', this.pk.toEncodingData()], + ['w', this.weight], + ]); + } + + public static fromEncodingData(data: unknown): Participant { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Participant: ${data}`); + } + return new Participant({ + pk: MerkleSignatureVerifier.fromEncodingData(data.get('p')), + weight: data.get('w'), + }); + } +} + +export class FalconVerifier implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'k', valueSchema: new FixedLengthByteArraySchema(0x701) }, // publicKey + ]) + ); + + public publicKey: Uint8Array; + + public constructor(params: { publicKey: Uint8Array }) { + this.publicKey = params.publicKey; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return FalconVerifier.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([['k', this.publicKey]]); + } + + public static fromEncodingData(data: unknown): FalconVerifier { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded FalconVerifier: ${data}`); + } + return new FalconVerifier({ + publicKey: data.get('k'), + }); + } +} + +/** + * FalconSignatureStruct represents a signature in the merkle signature scheme using falcon signatures + * as an underlying crypto scheme. It consists of an ephemeral public key, a signature, a merkle + * verification path and an index. The merkle signature considered valid only if the Signature is + * verified under the ephemeral public key and the Merkle verification path verifies that the + * ephemeral public key is located at the given index of the tree (for the root given in the + * long-term public key). More details can be found on Algorand's spec + */ +export class FalconSignatureStruct implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'sig', valueSchema: new ByteArraySchema() }, // signature + { key: 'idx', valueSchema: new Uint64Schema() }, // index + { key: 'prf', valueSchema: MerkleArrayProof.encodingSchema }, // proof + { key: 'vkey', valueSchema: FalconVerifier.encodingSchema }, // verifyingKey + ]) + ); + + public signature: Uint8Array; + public vectorCommitmentIndex: bigint; + public proof: MerkleArrayProof; + public verifyingKey: FalconVerifier; + + public constructor(params: { + signature: Uint8Array; + index: bigint; + proof: MerkleArrayProof; + verifyingKey: FalconVerifier; + }) { + this.signature = params.signature; + this.vectorCommitmentIndex = params.index; + this.proof = params.proof; + this.verifyingKey = params.verifyingKey; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return FalconSignatureStruct.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['sig', this.signature], + ['idx', this.vectorCommitmentIndex], + ['prf', this.proof.toEncodingData()], + ['vkey', this.verifyingKey.toEncodingData()], + ]); + } + + public static fromEncodingData(data: unknown): FalconSignatureStruct { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded FalconSignatureStruct: ${data}`); + } + return new FalconSignatureStruct({ + signature: data.get('sig'), + index: data.get('idx'), + proof: MerkleArrayProof.fromEncodingData(data.get('prf')), + verifyingKey: FalconVerifier.fromEncodingData(data.get('vkey')), + }); + } +} + +/** + * A SigslotCommit is a single slot in the sigs array that forms the state proof. + */ +export class SigslotCommit implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 's', valueSchema: FalconSignatureStruct.encodingSchema }, // sigslot + { key: 'l', valueSchema: new Uint64Schema() }, // l + ]) + ); + + /** + * Sig is a signature by the participant on the expected message. + */ + public sig: FalconSignatureStruct; + + /** + * L is the total weight of signatures in lower-numbered slots. This is initialized once the builder + * has collected a sufficient number of signatures. + */ + public l: bigint; + + public constructor(params: { sig: FalconSignatureStruct; l: bigint }) { + this.sig = params.sig; + this.l = params.l; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return SigslotCommit.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['s', this.sig.toEncodingData()], + ['l', this.l], + ]); + } + + public static fromEncodingData(data: unknown): SigslotCommit { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded SigslotCommit: ${data}`); + } + return new SigslotCommit({ + sig: FalconSignatureStruct.fromEncodingData(data.get('s')), + l: data.get('l'), + }); + } +} + +/** + * Reveal is a single array position revealed as part of a state proof. It reveals an element of the + * signature array and the corresponding element of the participants array. + */ +export class Reveal implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 's', valueSchema: SigslotCommit.encodingSchema }, // sigslotCommit + { key: 'p', valueSchema: Participant.encodingSchema }, // participant + ]) + ); + + public sigslot: SigslotCommit; + + public participant: Participant; + + public constructor(params: { + sigslot: SigslotCommit; + participant: Participant; + }) { + this.sigslot = params.sigslot; + this.participant = params.participant; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return Reveal.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['s', this.sigslot.toEncodingData()], + ['p', this.participant.toEncodingData()], + ]); + } + + public static fromEncodingData(data: unknown): Reveal { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded Reveal: ${data}`); + } + return new Reveal({ + sigslot: SigslotCommit.fromEncodingData(data.get('s')), + participant: Participant.fromEncodingData(data.get('p')), + }); + } +} + +export class StateProof implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { + key: 'c', // sigCommit + valueSchema: new ByteArraySchema(), + }, + { + key: 'w', // signedWeight + valueSchema: new Uint64Schema(), + }, + { + key: 'S', // sigProofs + valueSchema: MerkleArrayProof.encodingSchema, + }, + { + key: 'P', // partProofs + valueSchema: MerkleArrayProof.encodingSchema, + }, + { + key: 'v', // merkleSignatureSaltVersion + valueSchema: new Uint64Schema(), + }, + { + key: 'r', // reveals + valueSchema: new Uint64MapSchema(Reveal.encodingSchema), + }, + { + key: 'pr', // positionsToReveal + valueSchema: new ArraySchema(new Uint64Schema()), + }, + ]) + ); + + public sigCommit: Uint8Array; + + public signedWeight: bigint; + + public sigProofs: MerkleArrayProof; + + public partProofs: MerkleArrayProof; + + public merkleSignatureSaltVersion: number; + + /** + * Reveals is a sparse map from the position being revealed to the corresponding elements from the + * sigs and participants arrays. + */ + public reveals: Map; + + public positionsToReveal: bigint[]; + + public constructor(params: { + sigCommit: Uint8Array; + signedWeight: bigint; + sigProofs: MerkleArrayProof; + partProofs: MerkleArrayProof; + merkleSignatureSaltVersion: number; + reveals: Map; + positionsToReveal: bigint[]; + }) { + this.sigCommit = params.sigCommit; + this.signedWeight = params.signedWeight; + this.sigProofs = params.sigProofs; + this.partProofs = params.partProofs; + this.merkleSignatureSaltVersion = params.merkleSignatureSaltVersion; + this.reveals = params.reveals; + this.positionsToReveal = params.positionsToReveal; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return StateProof.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['c', this.sigCommit], + ['w', this.signedWeight], + ['S', this.sigProofs.toEncodingData()], + ['P', this.partProofs.toEncodingData()], + ['v', this.merkleSignatureSaltVersion], + [ + 'r', + convertMap(this.reveals, (key, value) => [key, value.toEncodingData()]), + ], + ['pr', this.positionsToReveal], + ]); + } + + public static fromEncodingData(data: unknown): StateProof { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProof: ${data}`); + } + return new StateProof({ + sigCommit: data.get('c'), + signedWeight: data.get('w'), + sigProofs: MerkleArrayProof.fromEncodingData(data.get('S')), + partProofs: MerkleArrayProof.fromEncodingData(data.get('P')), + merkleSignatureSaltVersion: Number(data.get('v')), + reveals: convertMap(data.get('r'), (key, value) => [ + key as bigint, + Reveal.fromEncodingData(value), + ]), + positionsToReveal: data.get('pr'), + }); + } +} + +export class StateProofMessage implements Encodable { + public static readonly encodingSchema = new NamedMapSchema( + allOmitEmpty([ + { key: 'b', valueSchema: new ByteArraySchema() }, // blockHeadersCommitment + { key: 'v', valueSchema: new ByteArraySchema() }, // votersCommitment + { key: 'P', valueSchema: new Uint64Schema() }, // lnProvenWeight + { key: 'f', valueSchema: new Uint64Schema() }, // firstAttestedRound + { key: 'l', valueSchema: new Uint64Schema() }, // lastAttestedRound + ]) + ); + + public blockHeadersCommitment: Uint8Array; + + public votersCommitment: Uint8Array; + + public lnProvenWeight: bigint; + + public firstAttestedRound: bigint; + + public lastAttestedRound: bigint; + + public constructor(params: { + blockHeadersCommitment: Uint8Array; + votersCommitment: Uint8Array; + lnProvenWeight: bigint; + firstAttestedRound: bigint; + lastAttestedRound: bigint; + }) { + this.blockHeadersCommitment = params.blockHeadersCommitment; + this.votersCommitment = params.votersCommitment; + this.lnProvenWeight = params.lnProvenWeight; + this.firstAttestedRound = params.firstAttestedRound; + this.lastAttestedRound = params.lastAttestedRound; + } + + // eslint-disable-next-line class-methods-use-this + public getEncodingSchema(): Schema { + return StateProofMessage.encodingSchema; + } + + public toEncodingData(): Map { + return new Map([ + ['b', this.blockHeadersCommitment], + ['v', this.votersCommitment], + ['P', this.lnProvenWeight], + ['f', this.firstAttestedRound], + ['l', this.lastAttestedRound], + ]); + } + + public static fromEncodingData(data: unknown): StateProofMessage { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded StateProofMessage: ${data}`); + } + return new StateProofMessage({ + blockHeadersCommitment: data.get('b'), + votersCommitment: data.get('v'), + lnProvenWeight: data.get('P'), + firstAttestedRound: data.get('f'), + lastAttestedRound: data.get('l'), + }); + } + + public static fromMap(data: Map): StateProofMessage { + return new StateProofMessage({ + blockHeadersCommitment: data.get('b') as Uint8Array, + votersCommitment: data.get('v') as Uint8Array, + lnProvenWeight: data.get('P') as bigint, + firstAttestedRound: data.get('f') as bigint, + lastAttestedRound: data.get('l') as bigint, + }); + } +} diff --git a/packages/sdk/src/types/account.ts b/packages/sdk/src/types/account.ts new file mode 100644 index 00000000..209f3968 --- /dev/null +++ b/packages/sdk/src/types/account.ts @@ -0,0 +1,18 @@ +import { Address } from '../encoding/address.js'; + +/** + * An Algorand account object. + * + * Contains an Algorand address and secret key. + */ +export default interface Account { + /** + * Algorand address + */ + addr: Address; + + /** + * Secret key belonging to the Algorand address + */ + sk: Uint8Array; +} diff --git a/packages/sdk/src/types/intDecoding.ts b/packages/sdk/src/types/intDecoding.ts new file mode 100644 index 00000000..e15659c1 --- /dev/null +++ b/packages/sdk/src/types/intDecoding.ts @@ -0,0 +1,29 @@ +/** + * Configure how integers in JSON response will be decoded. + */ +enum IntDecoding { + /** + * All integers will be decoded as Numbers, meaning any values greater than + * Number.MAX_SAFE_INTEGER will lose precision. + */ + UNSAFE = 'unsafe', + + /** + * All integers will be decoded as Numbers, but if any values are greater than + * Number.MAX_SAFE_INTEGER an error will be thrown. + */ + SAFE = 'safe', + + /** + * Integers will be decoded as Numbers if they are less than or equal to + * Number.MAX_SAFE_INTEGER, otherwise they will be decoded as BigInts. + */ + MIXED = 'mixed', + + /** + * All integers will be decoded as BigInts. + */ + BIGINT = 'bigint', +} + +export default IntDecoding; diff --git a/packages/sdk/src/types/transactions/base.ts b/packages/sdk/src/types/transactions/base.ts new file mode 100644 index 00000000..774ed4f5 --- /dev/null +++ b/packages/sdk/src/types/transactions/base.ts @@ -0,0 +1,445 @@ +import { + AccessReference, + BoxReference, + HoldingReference, + LocalsReference, + OnApplicationComplete, + TransactionType, +} from '@algorandfoundation/algokit-transact' +import { Address } from '../../encoding/address.js' +import { HeartbeatProof } from '../../heartbeat.js' +import { SdkTransactionParams } from '../../makeTxn.js' +import { StateProof, StateProofMessage } from '../../stateproof.js' + +/** + * Parameters for resource references in application transactions + */ +export interface ApplicationCallReferenceParams { + /** + * A grouping of the asset index and address of the account + */ + holdings?: HoldingReference[] + + /** A grouping of the application index and address of the account + */ + locals?: LocalsReference[] + + /** + * If true, use the foreign accounts, apps, assets, boxes, holdings, and locals fields to construct the access list + */ + convertToAccess?: boolean +} + +/** + * Contains payment transaction parameters. + * + * The full documentation is available at: + * https://developer.algorand.org/docs/get-details/transactions/transactions/#payment-transaction + */ +export interface PaymentTransactionParams { + /** + * Algorand address of recipient + */ + receiver: string | Address + + /** + * Integer amount to send, in microAlgos. Must be nonnegative. + */ + amount: number | bigint + + /** + * Optional, indicates the sender will close their account and the remaining balance will transfer + * to this account + */ + closeRemainderTo?: string | Address +} + +/** + * Contains key registration transaction parameters + * + * The full documentation is available at: + * https://developer.algorand.org/docs/get-details/transactions/transactions/#key-registration-transaction + */ +export interface KeyRegistrationTransactionParams { + /** + * 32-byte voting key. For key deregistration, leave undefined + */ + voteKey?: Uint8Array + + /** + * 32-byte selection key. For key deregistration, leave undefined + */ + selectionKey?: Uint8Array + + /** + * 64-byte state proof key. For key deregistration, leave undefined + */ + stateProofKey?: Uint8Array + + /** + * First round on which voting keys are valid + */ + voteFirst?: number | bigint + + /** + * Last round on which voting keys are valid + */ + voteLast?: number | bigint + + /** + * The dilution fo the 2-level participation key + */ + voteKeyDilution?: number | bigint + + /** + * Set this value to true to mark this account as nonparticipating. + * + * All new Algorand accounts are participating by default. This means they earn rewards. + */ + nonParticipation?: boolean +} + +/** + * Contains asset configuration transaction parameters. + * + * The full documentation is available at: + * https://developer.algorand.org/docs/get-details/transactions/transactions/#asset-configuration-transaction + */ +export interface AssetConfigurationTransactionParams { + /** + * Asset index uniquely specifying the asset + */ + assetIndex?: number | bigint + + /** + * Total supply of the asset + */ + total?: number | bigint + + /** + * Integer number of decimals for asset unit calcuation + */ + decimals?: number | bigint + + /** + * Whether asset accounts should default to being frozen + */ + defaultFrozen?: boolean + + /** + * The Algorand address in charge of reserve, freeze, clawback, destruction, etc. + */ + manager?: string | Address + + /** + * The Algorand address representing asset reserve + */ + reserve?: string | Address + + /** + * The Algorand address with power to freeze/unfreeze asset holdings + */ + freeze?: string | Address + + /** + * The Algorand address with power to revoke asset holdings + */ + clawback?: string | Address + + /** + * Unit name for this asset + */ + unitName?: string + + /** + * Name for this asset + */ + assetName?: string + + /** + * URL relating to this asset + */ + assetURL?: string + + /** + * Uint8Array containing a hash commitment with respect to the asset. Must be exactly 32 bytes long. + */ + assetMetadataHash?: Uint8Array +} + +/** + * Contains asset transfer transaction parameters. + * + * The full documentation is available at: + * https://developer.algorand.org/docs/get-details/transactions/transactions/#asset-transfer-transaction + */ +export interface AssetTransferTransactionParams { + /** + * Asset index uniquely specifying the asset + */ + assetIndex: number | bigint + + /** + * String representation of Algorand address – if provided, and if "sender" is + * the asset's revocation manager, then deduct from "assetSender" rather than "sender" + */ + assetSender?: string | Address + + /** + * The Algorand address of recipient + */ + receiver: string | Address + + /** + * Integer amount to send + */ + amount: number | bigint + + /** + * Close out remaining asset balance of the sender to this account + */ + closeRemainderTo?: string | Address +} + +/** + * Contains asset freeze transaction parameters. + * + * The full documentation is available at: + * https://developer.algorand.org/docs/get-details/transactions/transactions/#asset-freeze-transaction + */ +export interface AssetFreezeTransactionParams { + /** + * Asset index uniquely specifying the asset + */ + assetIndex: number | bigint + + /** + * Algorand address being frozen or unfrozen + */ + freezeTarget: string | Address + + /** + * true if freezeTarget should be frozen, false if freezeTarget should be allowed to transact + */ + frozen: boolean +} + +/** + * Contains application call transaction parameters. + * + * The full documentation is available at: + * https://developer.algorand.org/docs/get-details/transactions/transactions/#application-call-transaction + */ +export interface ApplicationCallTransactionParams { + /** + * A unique application ID + */ + appIndex: number | bigint + + /** + * What application should do once the program has been run + */ + onComplete: OnApplicationComplete + + /** + * Restricts number of ints in per-user local state + */ + numLocalInts?: number | bigint + + /** + * Restricts number of byte slices in per-user local state + */ + numLocalByteSlices?: number | bigint + + /** + * Restricts number of ints in global state + */ + numGlobalInts?: number | bigint + + /** + * Restricts number of byte slices in global state + */ + numGlobalByteSlices?: number | bigint + + /** + * The compiled TEAL that approves a transaction + */ + approvalProgram?: Uint8Array + + /** + * The compiled TEAL program that runs when clearing state + */ + clearProgram?: Uint8Array + + /** + * Array of Uint8Array, any additional arguments to the application + */ + appArgs?: Uint8Array[] + + /** + * Array of Address strings, any additional accounts to supply to the application + */ + accounts?: Array + + /** + * Array of int, any other apps used by the application, identified by index + */ + foreignApps?: Array + + /** + * Array of int, any assets used by the application, identified by index + */ + foreignAssets?: Array + + /** + * Int representing extra pages of memory to rent during an application create transaction. + */ + extraPages?: number | bigint + + /** + * A grouping of the app ID and name of the box in an Uint8Array + */ + boxes?: BoxReference[] + + /** + * Resources accessed by the application + */ + access?: AccessReference[] + + /** + * The lowest application version for which this transaction should immediately fail. + * 0 indicates that no version check should be performed. + */ + rejectVersion?: number | bigint +} + +/** + * Contains state proof transaction parameters. + */ +export interface StateProofTransactionParams { + /* + * Uint64 identifying a particular configuration of state proofs. + */ + stateProofType?: number | bigint + + /** + * The state proof. + */ + stateProof?: StateProof + + /** + * The state proof message. + */ + message?: StateProofMessage +} + +/** + * Contains heartbeat transaction parameters. + */ +export interface HeartbeatTransactionParams { + /* + * Account address this txn is proving onlineness for + */ + address: Address + + /** + * Signature using HeartbeatAddress's partkey, thereby showing it is online. + */ + proof: HeartbeatProof + + /** + * The block seed for the this transaction's firstValid block. + */ + seed: Uint8Array + + /** + * Must match the hbAddress account's current VoteID + */ + voteID: Uint8Array + + /** + * Must match hbAddress account's current KeyDilution. + */ + keyDilution: bigint +} + +/** + * A full list of all available transaction parameters + * + * The full documentation is available at: + * https://developer.algorand.org/docs/get-details/transactions/transactions/#common-fields-header-and-type + */ +export interface TransactionParams { + /** + * Transaction type + */ + type: TransactionType + + /** + * Algorand address of sender + */ + sender: string | Address + + /** + * Optional, arbitrary data to be included in the transaction's note field + */ + note?: Uint8Array + + /** + * Optional, 32-byte lease to associate with this transaction. + * + * The sender cannot send another transaction with the same lease until the last round of original + * transaction has passed. + */ + lease?: Uint8Array + + /** + * The Algorand address that will be used to authorize all future transactions from the sender, if provided. + */ + rekeyTo?: string | Address + + /** + * Suggested parameters relevant to the network that will accept this transaction + */ + suggestedParams: SdkTransactionParams + + /** + * Payment transaction parameters. Only set if type is TransactionType.pay + */ + paymentParams?: PaymentTransactionParams + + /** + * Key registration transaction parameters. Only set if type is TransactionType.keyreg + */ + keyregParams?: KeyRegistrationTransactionParams + + /** + * Asset configuration transaction parameters. Only set if type is TransactionType.acfg + */ + assetConfigParams?: AssetConfigurationTransactionParams + + /** + * Asset transfer transaction parameters. Only set if type is TransactionType.axfer + */ + assetTransferParams?: AssetTransferTransactionParams + + /** + * Asset freeze transaction parameters. Only set if type is TransactionType.afrz + */ + assetFreezeParams?: AssetFreezeTransactionParams + + /** + * Application call transaction parameters. Only set if type is TransactionType.appl + */ + appCallParams?: ApplicationCallTransactionParams + + /** + * State proof transaction parameters. Only set if type is TransactionType.stpf + */ + stateProofParams?: StateProofTransactionParams + + /** + * Heartbeat transaction parameters. Only set if type is TransactionType.hb + */ + heartbeatParams?: HeartbeatTransactionParams +} diff --git a/packages/sdk/src/types/transactions/encoded.ts b/packages/sdk/src/types/transactions/encoded.ts new file mode 100644 index 00000000..1279a216 --- /dev/null +++ b/packages/sdk/src/types/transactions/encoded.ts @@ -0,0 +1,118 @@ +import { + NamedMapSchema, + FixedLengthByteArraySchema, + Uint64Schema, + ArraySchema, + OptionalSchema, + allOmitEmpty, +} from '../../encoding/schema/index.js'; +import { ensureSafeUnsignedInteger } from '../../utils/utils.js'; + +export interface EncodedSubsig { + /** + * The public key + */ + pk: Uint8Array; + + /** + * The signature provided by the public key, if any + */ + s?: Uint8Array; +} + +export const ENCODED_SUBSIG_SCHEMA = new NamedMapSchema( + allOmitEmpty([ + { + key: 'pk', + valueSchema: new FixedLengthByteArraySchema(32), + }, + { + key: 's', + valueSchema: new OptionalSchema(new FixedLengthByteArraySchema(64)), + }, + ]) +); + +export function encodedSubsigFromEncodingData(data: unknown): EncodedSubsig { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded EncodedSubsig: ${data}`); + } + const subsig: EncodedSubsig = { + pk: data.get('pk'), + }; + if (data.get('s')) { + subsig.s = data.get('s'); + } + return subsig; +} + +export function encodedSubsigToEncodingData( + subsig: EncodedSubsig +): Map { + const data = new Map([['pk', subsig.pk]]); + if (subsig.s) { + data.set('s', subsig.s); + } + return data; +} + +/** + * A rough structure for the encoded multi signature transaction object. + * Every property is labelled with its associated `MultisigMetadata` type property + */ +export interface EncodedMultisig { + /** + * version + */ + v: number; + + /** + * threshold + */ + thr: number; + + /** + * Subset of signatures. A threshold of `thr` signors is required. + */ + subsig: EncodedSubsig[]; +} + +export const ENCODED_MULTISIG_SCHEMA = new NamedMapSchema( + allOmitEmpty([ + { + key: 'v', + valueSchema: new Uint64Schema(), + }, + { + key: 'thr', + valueSchema: new Uint64Schema(), + }, + { + key: 'subsig', + valueSchema: new ArraySchema(ENCODED_SUBSIG_SCHEMA), + }, + ]) +); + +export function encodedMultiSigFromEncodingData( + data: unknown +): EncodedMultisig { + if (!(data instanceof Map)) { + throw new Error(`Invalid decoded EncodedMultiSig: ${data}`); + } + return { + v: ensureSafeUnsignedInteger(data.get('v')), + thr: ensureSafeUnsignedInteger(data.get('thr')), + subsig: data.get('subsig').map(encodedSubsigFromEncodingData), + }; +} + +export function encodedMultiSigToEncodingData( + msig: EncodedMultisig +): Map { + return new Map([ + ['v', msig.v], + ['thr', msig.thr], + ['subsig', msig.subsig.map(encodedSubsigToEncodingData)], + ]); +} diff --git a/packages/sdk/src/types/transactions/index.ts b/packages/sdk/src/types/transactions/index.ts new file mode 100644 index 00000000..de3e406c --- /dev/null +++ b/packages/sdk/src/types/transactions/index.ts @@ -0,0 +1,2 @@ +export * from './base.js'; +export * from './encoded.js'; diff --git a/packages/sdk/src/types/utils.ts b/packages/sdk/src/types/utils.ts new file mode 100644 index 00000000..ff28e48e --- /dev/null +++ b/packages/sdk/src/types/utils.ts @@ -0,0 +1,63 @@ +/** + * Expands types for IntelliSense so they are more human readable + * See https://stackoverflow.com/a/69288824 + */ +export type Expand = T extends (...args: infer A) => infer R + ? (...args: Expand) => Expand + : T extends infer O + ? { [K in keyof O]: O[K] } + : never; + +/** + * Same as TypeScript's Pick, but will distribute the Pick over unions + */ +export type DistributivePick = T extends unknown + ? Pick + : never; + +/** + * Overwrite a type with properties from another type + */ +export type Overwrite> = Pick< + T, + Exclude +> & + U; + +/** + * Same as Overwrite, but will distribute the Overwrite over unions + */ +export type DistributiveOverwrite> = T extends unknown + ? Overwrite + : never; + +/** + * Mark certain keys as prohibited + */ +export type NeverAllow = { + // eslint-disable-next-line no-unused-vars + [P in K]?: never; +}; + +/** + * Rename a specific property of a type to another name + * + * Usage: RenameProperty\<\{ a: string \}, 'a', 'b'\> + * -\> \{ b: string \} + */ +export type RenameProperty = { + [P in keyof T as P extends K ? R : P]: T[P]; +}; + +/** + * Rename multiple properties of one type to another name + * + * Usage: RenameProperties\<\{ a: string, b: number \}, \{ a: 'c', b: 'd' \}\> + * -\> \{ c: string, d: number \} + */ +export type RenameProperties< + T, + R extends { + [K in keyof R]: K extends keyof T ? PropertyKey : 'Error: key not in T'; + }, +> = { [P in keyof T as P extends keyof R ? R[P] : P]: T[P] }; diff --git a/packages/sdk/src/utils/utils.ts b/packages/sdk/src/utils/utils.ts new file mode 100644 index 00000000..3007e909 --- /dev/null +++ b/packages/sdk/src/utils/utils.ts @@ -0,0 +1,207 @@ +import JSONbigWithoutConfig from 'json-bigint'; +import IntDecoding from '../types/intDecoding.js'; + +const JSONbig = JSONbigWithoutConfig({ + useNativeBigInt: true, + strict: true, +}); + +export interface ParseJSONOptions { + intDecoding: IntDecoding; +} + +/** + * Parse JSON with additional options. + * @param str - The JSON string to parse. + * @param options - Configures how integers in this JSON string will be decoded. See the + * `IntDecoding` enum for more details. + */ +export function parseJSON(str: string, { intDecoding }: ParseJSONOptions) { + if ( + intDecoding !== IntDecoding.SAFE && + intDecoding !== IntDecoding.UNSAFE && + intDecoding !== IntDecoding.BIGINT && + intDecoding !== IntDecoding.MIXED + ) { + throw new Error(`Invalid intDecoding option: ${intDecoding}`); + } + return JSONbig.parse(str, (_: string, value: any): any => { + if ( + value != null && + typeof value === 'object' && + Object.getPrototypeOf(value) == null + ) { + // JSONbig.parse objects are created with Object.create(null) and thus have a null prototype + // let us remedy that + Object.setPrototypeOf(value, Object.prototype); + } + + if (typeof value === 'bigint') { + if (intDecoding === IntDecoding.SAFE && value > Number.MAX_SAFE_INTEGER) { + throw new Error( + `Integer exceeds maximum safe integer: ${value.toString()}. Try parsing with a different intDecoding option.` + ); + } + if ( + intDecoding === IntDecoding.BIGINT || + (intDecoding === IntDecoding.MIXED && value > Number.MAX_SAFE_INTEGER) + ) { + return value; + } + // JSONbig.parse converts number to BigInts if they are >= 10**15. This is smaller than + // Number.MAX_SAFE_INTEGER, so we can convert some BigInts back to normal numbers. + return Number(value); + } + + if (typeof value === 'number') { + if (intDecoding === IntDecoding.BIGINT && Number.isInteger(value)) { + return BigInt(value); + } + } + + return value; + }); +} + +/** + * Converts a JavaScript value to a JavaScript Object Notation (JSON) string. + * + * This functions differs from the built-in JSON.stringify in that it supports serializing BigInts. + * + * This function takes the same arguments as the built-in JSON.stringify function. + * + * @param value - A JavaScript value, usually an object or array, to be converted. + * @param replacer - A function that transforms the results. + * @param space - Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. + */ +export function stringifyJSON( + value: any, + replacer?: (this: any, key: string, value: any) => any, + space?: string | number +): string { + return JSONbig.stringify(value, replacer, space); +} + +/** + * ArrayEqual takes two arrays and return true if equal, false otherwise + */ +export function arrayEqual(a: ArrayLike, b: ArrayLike): boolean { + if (a.length !== b.length) { + return false; + } + return Array.from(a).every((val, i) => val === b[i]); +} + +/** + * ConcatArrays takes n number arrays and returns a joint Uint8Array + * @param arrs - An arbitrary number of n array-like number list arguments + * @returns [a,b] + */ +export function concatArrays(...arrs: ArrayLike[]) { + const size = arrs.reduce((sum, arr) => sum + arr.length, 0); + const c = new Uint8Array(size); + + let offset = 0; + for (let i = 0; i < arrs.length; i++) { + c.set(arrs[i], offset); + offset += arrs[i].length; + } + + return c; +} + +/** + * Remove undefined properties from an object + * @param obj - An object, preferably one with some undefined properties + * @returns A copy of the object with undefined properties removed + */ +export function removeUndefinedProperties( + obj: Record +) { + const mutableCopy = { ...obj }; + Object.keys(mutableCopy).forEach((key) => { + if (typeof mutableCopy[key] === 'undefined') delete mutableCopy[key]; + }); + return mutableCopy; +} + +/** + * Check whether the environment is Node.js (as opposed to the browser) + * @returns True if Node.js environment, false otherwise + */ +export function isNode() { + return ( + // @ts-ignore + typeof process === 'object' && + // @ts-ignore + typeof process.versions === 'object' && + // @ts-ignore + typeof process.versions.node !== 'undefined' + ); +} + +/** + * Check whether the environment is ReactNative + * @returns True if ReactNative, false otherwise + */ +export function isReactNative() { + const { navigator } = globalThis as { navigator?: { product?: string } }; + if (typeof navigator === 'object' && navigator.product === 'ReactNative') { + return true; + } + return false; +} + +export function ensureSafeInteger(value: unknown): number { + if (typeof value === 'undefined') { + throw new Error('Value is undefined'); + } + if (typeof value === 'bigint') { + if ( + value > BigInt(Number.MAX_SAFE_INTEGER) || + value < BigInt(Number.MIN_SAFE_INTEGER) + ) { + throw new Error(`BigInt value ${value} is not a safe integer`); + } + return Number(value); + } + if (typeof value === 'number') { + if (Number.isSafeInteger(value)) { + return value; + } + throw new Error(`Value ${value} is not a safe integer`); + } + throw new Error(`Unexpected type ${typeof value}, ${value}`); +} + +export function ensureSafeUnsignedInteger(value: unknown): number { + const intValue = ensureSafeInteger(value); + if (intValue < 0) { + throw new Error(`Value ${intValue} is negative`); + } + return intValue; +} + +export function ensureBigInt(value: unknown): bigint { + if (typeof value === 'undefined') { + throw new Error('Value is undefined'); + } + if (typeof value === 'bigint') { + return value; + } + if (typeof value === 'number') { + if (!Number.isSafeInteger(value)) { + throw new Error(`Value ${value} is not a safe integer`); + } + return BigInt(value); + } + throw new Error(`Unexpected type ${typeof value}, ${value}`); +} + +export function ensureUint64(value: unknown): bigint { + const bigIntValue = ensureBigInt(value); + if (bigIntValue < 0 || bigIntValue > BigInt('0xffffffffffffffff')) { + throw new Error(`Value ${bigIntValue} is not a uint64`); + } + return bigIntValue; +} diff --git a/packages/sdk/src/wait.ts b/packages/sdk/src/wait.ts new file mode 100644 index 00000000..e6a538e2 --- /dev/null +++ b/packages/sdk/src/wait.ts @@ -0,0 +1,55 @@ +import type { AlgodClient, PendingTransactionResponse } from '@algorandfoundation/algokit-algod-client' + +/** + * Wait until a transaction has been confirmed or rejected by the network, or + * until 'waitRounds' number of rounds have passed. + * @param client - An Algodv2 client + * @param txid - The ID of the transaction to wait for. + * @param waitRounds - The maximum number of rounds to wait for. + * @returns A promise that, upon success, will resolve to the output of the + * `pendingTransactionInformation` call for the confirmed transaction. + */ +export async function waitForConfirmation(client: AlgodClient, txid: string, waitRounds: number): Promise { + // Wait until the transaction is confirmed or rejected, or until 'waitRounds' + // number of rounds have passed. + + const status = await client.getStatus() + if (typeof status === 'undefined') { + throw new Error('Unable to get node status') + } + const startRound = status.lastRound + BigInt(1) + const stopRound = startRound + BigInt(waitRounds) + let currentRound = startRound + + /* eslint-disable no-await-in-loop */ + while (currentRound < stopRound) { + let poolError = false + try { + const pendingInfo = await client.pendingTransactionInformation(txid) + + if (pendingInfo.confirmedRound) { + // Got the completed Transaction + return pendingInfo + } + + if (pendingInfo.poolError) { + // If there was a pool error, then the transaction has been rejected + poolError = true + throw new Error(`Transaction Rejected: ${pendingInfo.poolError}`) + } + } catch (err) { + // Ignore errors from PendingTransactionInformation, since it may return 404 if the algod + // instance is behind a load balancer and the request goes to a different algod than the + // one we submitted the transaction to + if (poolError) { + // Rethrow error only if it's because the transaction was rejected + throw err + } + } + + await client.waitForBlock(currentRound) + currentRound += BigInt(1) + } + /* eslint-enable no-await-in-loop */ + throw new Error(`Transaction not confirmed after ${waitRounds} rounds`) +} diff --git a/packages/sdk/tsconfig.json b/packages/sdk/tsconfig.json new file mode 100644 index 00000000..079567a1 --- /dev/null +++ b/packages/sdk/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/*.ts", "tests/**/*.ts"] +} diff --git a/packages/sdk/tsconfig.test.json b/packages/sdk/tsconfig.test.json new file mode 100644 index 00000000..4b8f61b2 --- /dev/null +++ b/packages/sdk/tsconfig.test.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "isolatedModules": true + } +} diff --git a/packages/sdk/vitest.config.ts b/packages/sdk/vitest.config.ts new file mode 100644 index 00000000..3a336b58 --- /dev/null +++ b/packages/sdk/vitest.config.ts @@ -0,0 +1,3 @@ +import config from '../../vitest.config' + +export default config diff --git a/packages/transact/.tstoolkitrc.ts b/packages/transact/.tstoolkitrc.ts deleted file mode 100644 index f19a3d47..00000000 --- a/packages/transact/.tstoolkitrc.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { TsToolkitConfig } from '@makerx/ts-toolkit' - -const config: TsToolkitConfig = { - packageConfig: { - srcDir: 'src', - outDir: 'dist', - main: 'index.js', - customSections: ['module', 'main', 'type', 'types', 'exports'], - }, -} -export default config diff --git a/packages/transact/package.json b/packages/transact/package.json index 7cba9a4e..59a62264 100644 --- a/packages/transact/package.json +++ b/packages/transact/package.json @@ -14,13 +14,6 @@ "**/*" ], "scripts": { - "build": "run-s build:*", - "build-watch": "rolldown --watch -c", - "build:0-clean": "rimraf dist coverage", - "build:1-lint": "npm run lint", - "build:2-compile": "rolldown -c", - "build:3-copy-pkg-json": "tstk copy-package-json -c", - "build:4-copy-readme": "cpy README.md dist", "test": "vitest run --coverage --passWithNoTests", "test:watch": "vitest watch --coverage --passWithNoTests", "lint": "eslint ./src/", @@ -33,7 +26,6 @@ "dependencies": {}, "peerDependencies": {}, "devDependencies": { - "@algorandfoundation/algokit-common": "*", "@noble/ed25519": "^3.0.0" } -} +} \ No newline at end of file diff --git a/packages/transact/rolldown.config.ts b/packages/transact/rolldown.config.ts deleted file mode 100644 index 0cd70473..00000000 --- a/packages/transact/rolldown.config.ts +++ /dev/null @@ -1,4 +0,0 @@ -import createConfig from '../../rolldown' -import pkg from './package.json' with { type: 'json' } - -export default createConfig([...Object.keys(pkg.dependencies || {})]) diff --git a/packages/transact/src/encoding/codecs.spec.ts b/packages/transact/src/encoding/codecs.spec.ts index 8875ba3f..99ce2399 100644 --- a/packages/transact/src/encoding/codecs.spec.ts +++ b/packages/transact/src/encoding/codecs.spec.ts @@ -1,6 +1,6 @@ import { PUBLIC_KEY_BYTE_LENGTH, ZERO_ADDRESS } from '@algorandfoundation/algokit-common' import { describe, expect, test } from 'vitest' -import { addressCodec, bigIntCodec, booleanCodec, bytesCodec, numberCodec, OmitEmptyObjectCodec, stringCodec } from './codecs' +import { OmitEmptyObjectCodec, addressCodec, bigIntCodec, booleanCodec, bytesCodec, numberCodec, stringCodec } from './codecs' describe('Codecs', () => { describe('AddressCodec', () => { diff --git a/packages/transact/src/encoding/msgpack.ts b/packages/transact/src/encoding/msgpack.ts index f0dbef72..d47d8a7b 100644 --- a/packages/transact/src/encoding/msgpack.ts +++ b/packages/transact/src/encoding/msgpack.ts @@ -1,4 +1,4 @@ -import { decode as msgpackDecode, encode as msgpackEncode } from 'algorand-msgpack' +import { encode as msgpackEncode, decode as msgpackDecode } from 'algorand-msgpack' export function encodeMsgpack(data: T): Uint8Array { return new Uint8Array(msgpackEncode(data, { sortKeys: true, ignoreUndefined: true })) diff --git a/packages/transact/src/transactions/app-call.ts b/packages/transact/src/transactions/app-call.ts index c8904178..aadd730b 100644 --- a/packages/transact/src/transactions/app-call.ts +++ b/packages/transact/src/transactions/app-call.ts @@ -110,7 +110,7 @@ export type AppCallTransactionFields = { /** * Resources accessed by the application */ - access?: ResourceReference[] + accessReferences?: AccessReference[] } /** @@ -193,7 +193,7 @@ export type BoxReference = { /** * Names a single resource reference. Only one of the fields should be set. */ -export interface ResourceReference { +export interface AccessReference { /** Any account addresses whose balance record is accessible by the executing ApprovalProgram or ClearStateProgram. */ address?: string /** Application ID whose GlobalState may be read by the executing ApprovalProgram or ClearStateProgram. */ diff --git a/packages/transact/src/transactions/signed-transaction.ts b/packages/transact/src/transactions/signed-transaction.ts index 16a9e362..1de7c85e 100644 --- a/packages/transact/src/transactions/signed-transaction.ts +++ b/packages/transact/src/transactions/signed-transaction.ts @@ -10,7 +10,7 @@ export type SignedTransaction = { /** * The transaction that has been signed. */ - transaction: Transaction + txn: Transaction /** * Optional Ed25519 signature authorizing the transaction. @@ -150,7 +150,7 @@ export function decodeSignedTransactions(encodedSignedTransactions: Uint8Array[] * Validate a signed transaction structure */ function validateSignedTransaction(signedTransaction: SignedTransaction): void { - validateTransaction(signedTransaction.transaction) + validateTransaction(signedTransaction.txn) // Validate that only one signature type is set const sigTypes = [signedTransaction.signature, signedTransaction.multiSignature, signedTransaction.logicSignature] @@ -186,7 +186,7 @@ function toMultisigSignatureDto(multisigSignature: MultisigSignature): MultisigS function toSignedTransactionDto(signedTransaction: SignedTransaction): SignedTransactionDto { const stx_dto: SignedTransactionDto = { - txn: toTransactionDto(signedTransaction.transaction), + txn: toTransactionDto(signedTransaction.txn), } if (signedTransaction.signature) { @@ -231,7 +231,7 @@ function fromMultisigSignatureDto(msigDto: MultisigSignatureDto): MultisigSignat function fromSignedTransactionDto(signedTransactionDto: SignedTransactionDto): SignedTransaction { const stx: SignedTransaction = { - transaction: fromTransactionDto(signedTransactionDto.txn), + txn: fromTransactionDto(signedTransactionDto.txn), } if (signedTransactionDto.sig) { diff --git a/packages/transact/src/transactions/transaction.spec.ts b/packages/transact/src/transactions/transaction.spec.ts index 920f2578..58172941 100644 --- a/packages/transact/src/transactions/transaction.spec.ts +++ b/packages/transact/src/transactions/transaction.spec.ts @@ -2,13 +2,13 @@ import { EMPTY_SIGNATURE } from '@algorandfoundation/algokit-common' import { describe, expect, test } from 'vitest' import { encodeSignedTransaction } from './signed-transaction' import { + Transaction, + TransactionType, encodeTransaction, encodeTransactionRaw, estimateTransactionSize, getTransactionId, getTransactionIdRaw, - Transaction, - TransactionType, validateTransaction, } from './transaction' @@ -18,7 +18,7 @@ describe('Transaction Validation', () => { describe('Core transaction validation', () => { test('should throw error when sender is missing', () => { const transaction: Transaction = { - transactionType: TransactionType.Payment, + type: TransactionType.Payment, sender: '', firstValid: 1000n, lastValid: 2000n, @@ -33,7 +33,7 @@ describe('Transaction Validation', () => { test('should throw error when no transaction type specific field is set', () => { const transaction: Transaction = { - transactionType: TransactionType.Payment, + type: TransactionType.Payment, sender: VALID_ADDRESS_1, firstValid: 1000n, lastValid: 2000n, @@ -44,7 +44,7 @@ describe('Transaction Validation', () => { test('should throw error when multiple transaction type specific fields are set', () => { const transaction: Transaction = { - transactionType: TransactionType.Payment, + type: TransactionType.Payment, sender: VALID_ADDRESS_1, firstValid: 1000n, lastValid: 2000n, @@ -64,7 +64,7 @@ describe('Transaction Validation', () => { test('should validate valid payment transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.Payment, + type: TransactionType.Payment, sender: VALID_ADDRESS_1, firstValid: 1000n, lastValid: 2000n, @@ -83,10 +83,10 @@ describe('Transaction Validation', () => { ['estimateTransactionSize', estimateTransactionSize], ['getTransactionIdRaw', getTransactionIdRaw], ['getTransactionId', getTransactionId], - ['encodeSignedTransaction', (transaction: Transaction) => encodeSignedTransaction({ transaction, signature: EMPTY_SIGNATURE })], + ['encodeSignedTransaction', (transaction: Transaction) => encodeSignedTransaction({ txn: transaction, signature: EMPTY_SIGNATURE })], ])('should validate when calling %s', (_, sut) => { const transaction: Transaction = { - transactionType: TransactionType.AssetTransfer, + type: TransactionType.AssetTransfer, sender: VALID_ADDRESS_1, firstValid: 1000n, lastValid: 2000n, diff --git a/packages/transact/src/transactions/transaction.ts b/packages/transact/src/transactions/transaction.ts index 9f347ec9..df08ccea 100644 --- a/packages/transact/src/transactions/transaction.ts +++ b/packages/transact/src/transactions/transaction.ts @@ -32,7 +32,7 @@ import { StateSchemaDto, TransactionDto, } from '../encoding/transaction-dto' -import { AppCallTransactionFields, OnApplicationComplete, ResourceReference, StateSchema, validateAppCallTransaction } from './app-call' +import { AccessReference, AppCallTransactionFields, OnApplicationComplete, StateSchema, validateAppCallTransaction } from './app-call' import { AssetConfigTransactionFields, validateAssetConfigTransaction } from './asset-config' import { AssetFreezeTransactionFields, validateAssetFreezeTransaction } from './asset-freeze' import { AssetTransferTransactionFields, validateAssetTransferTransaction } from './asset-transfer' @@ -52,7 +52,7 @@ export type Transaction = { /** * The type of transaction */ - transactionType: TransactionType + type: TransactionType /** * The account that authorized the transaction. @@ -219,7 +219,7 @@ export type FeeParams = { */ export function getEncodedTransactionType(encoded_transaction: Uint8Array): TransactionType { const decoded = decodeTransaction(encoded_transaction) - return decoded.transactionType + return decoded.type } /** @@ -553,7 +553,7 @@ const heartbeatProofDtoCodec = new OmitEmptyObjectCodec() export function toTransactionDto(transaction: Transaction): TransactionDto { const txDto: TransactionDto = { - type: toTransactionTypeDto(transaction.transactionType), + type: toTransactionTypeDto(transaction.type), fv: bigIntCodec.encode(transaction.firstValid), lv: bigIntCodec.encode(transaction.lastValid), snd: addressCodec.encode(transaction.sender), @@ -628,13 +628,21 @@ export function toTransactionDto(transaction: Transaction): TransactionDto { txDto.apas = bigIntArrayCodec.encode(transaction.appCall.assetReferences ?? []) // Encode box references if (transaction.appCall.boxReferences && transaction.appCall.boxReferences.length > 0) { - txDto.apbx = transaction.appCall.boxReferences.map((box) => ({ - i: bigIntCodec.encode(box.appId), - n: bytesCodec.encode(box.name), - })) + txDto.apbx = transaction.appCall.boxReferences.map((box) => { + const isCurrentApp = box.appId === 0n || box.appId === transaction.appCall?.appId + const foreignAppsIndex = (transaction.appCall?.appReferences ?? []).indexOf(box.appId) + 1 + if (foreignAppsIndex === 0 && !isCurrentApp) { + throw new Error(`Box ref with appId ${box.appId} not in appReferences`) + } + + return { + i: numberCodec.encode(foreignAppsIndex), + n: bytesCodec.encode(box.name), + } + }) } // Encode access references - if (transaction.appCall.access && transaction.appCall.access.length > 0) { + if (transaction.appCall.accessReferences && transaction.appCall.accessReferences.length > 0) { const accessList: ResourceReferenceDto[] = [] const appId = transaction.appCall.appId @@ -653,7 +661,7 @@ export function toTransactionDto(transaction: Transaction): TransactionDto { } // Helper function to ensure a reference exists and return its 1-based index - function ensure(target: ResourceReference): number { + function ensure(target: AccessReference): number { for (let idx = 0; idx < accessList.length; idx++) { const a = accessList[idx] if ( @@ -676,14 +684,14 @@ export function toTransactionDto(transaction: Transaction): TransactionDto { return accessList.length // length is 1-based position of new element } - for (const resourceReference of transaction.appCall.access) { - if (resourceReference.address || resourceReference.assetId || resourceReference.appId) { - ensure(resourceReference) + for (const accessReferences of transaction.appCall.accessReferences) { + if (accessReferences.address || accessReferences.assetId || accessReferences.appId) { + ensure(accessReferences) continue } - if (resourceReference.holding) { - const holding = resourceReference.holding + if (accessReferences.holding) { + const holding = accessReferences.holding let addressIndex = 0 if (holding.address && holding.address !== ZERO_ADDRESS) { addressIndex = ensure({ address: holding.address }) @@ -698,8 +706,8 @@ export function toTransactionDto(transaction: Transaction): TransactionDto { continue } - if (resourceReference.locals) { - const locals = resourceReference.locals + if (accessReferences.locals) { + const locals = accessReferences.locals let addressIndex = 0 if (locals.address && locals.address !== ZERO_ADDRESS) { addressIndex = ensure({ address: locals.address }) @@ -708,18 +716,19 @@ export function toTransactionDto(transaction: Transaction): TransactionDto { if (locals.appId && locals.appId !== appId) { appIndex = ensure({ appId: locals.appId }) } - // TODO: PD - confirm what happens if both are 0 - accessList.push({ - l: { - d: numberCodec.encode(addressIndex), - p: numberCodec.encode(appIndex), - }, - }) + if (addressIndex !== 0 || appIndex !== 0) { + accessList.push({ + l: { + d: numberCodec.encode(addressIndex), + p: numberCodec.encode(appIndex), + }, + }) + } continue } - if (resourceReference.box) { - const b = resourceReference.box + if (accessReferences.box) { + const b = accessReferences.box let appIdx = 0 if (b.appId && b.appId !== appId) { appIdx = ensure({ appId: b.appId }) @@ -819,12 +828,11 @@ export function toTransactionDto(transaction: Transaction): TransactionDto { return txDto } -// TODO: PD - fix bug https://github.com/algorand/js-algorand-sdk/issues/1013 export function fromTransactionDto(transactionDto: TransactionDto): Transaction { const transactionType = fromTransactionTypeDto(transactionDto.type) const tx: Transaction = { - transactionType, + type: transactionType, sender: addressCodec.decode(transactionDto.snd), firstValid: bigIntCodec.decode(transactionDto.fv), lastValid: bigIntCodec.decode(transactionDto.lv), @@ -892,14 +900,29 @@ export function fromTransactionDto(transactionDto: TransactionDto): Transaction accountReferences: transactionDto.apat?.map((addr) => addressCodec.decode(addr)), appReferences: transactionDto.apfa?.map((id) => bigIntCodec.decode(id)), assetReferences: transactionDto.apas?.map((id) => bigIntCodec.decode(id)), - boxReferences: transactionDto.apbx?.map((box) => ({ - appId: bigIntCodec.decode(box.i), - name: bytesCodec.decode(box.n), - })), - access: transactionDto.al + boxReferences: transactionDto.apbx?.map((box) => { + const index = typeof box.i === 'bigint' ? Number(box.i) : (box.i ?? 0) + let appId: bigint + if (index === 0) { + // 0 means current app + appId = 0n + } else { + // 1-based index into foreignApps array + const foreignAppId = transactionDto.apfa?.[index - 1] + if (foreignAppId === undefined) { + throw new Error(`Failed to find the app reference at index ${index - 1}`) + } + appId = bigIntCodec.decode(foreignAppId) + } + return { + appId: appId, + name: bytesCodec.decode(box.n), + } + }), + accessReferences: transactionDto.al ? (() => { const accessList = transactionDto.al! - const result: ResourceReference[] = [] + const result: AccessReference[] = [] for (const ref of accessList) { if (ref.d) { @@ -922,13 +945,13 @@ export function fromTransactionDto(transactionDto: TransactionDto): Transaction throw new Error(`Holding missing asset index: ${JSON.stringify(ref.h)}`) } - const holdingAddress = addrIdx === 0 ? ZERO_ADDRESS : result[addrIdx - 1].address! - const holdingAssetId = result[assetIdx - 1].assetId! + const holdingAddress = addrIdx === 0 ? ZERO_ADDRESS : accessList[addrIdx - 1].d! + const holdingAssetId = accessList[assetIdx - 1].s! result.push({ holding: { - address: holdingAddress, - assetId: holdingAssetId, + address: typeof holdingAddress === 'string' ? holdingAddress : addressCodec.decode(holdingAddress), + assetId: bigIntCodec.decode(holdingAssetId), }, }) continue @@ -938,13 +961,13 @@ export function fromTransactionDto(transactionDto: TransactionDto): Transaction const addrIdx = ref.l.d ?? 0 const appIdx = ref.l.p ?? 0 - const localsAddress = addrIdx === 0 ? ZERO_ADDRESS : result[addrIdx - 1].address! - const localsAppId = appIdx === 0 ? BigInt(0) : result[appIdx - 1].appId! + const localsAddress = addrIdx === 0 ? ZERO_ADDRESS : accessList[addrIdx - 1].d! + const localsAppId = appIdx === 0 ? BigInt(0) : accessList[appIdx - 1].p! result.push({ locals: { - address: localsAddress, - appId: localsAppId, + address: typeof localsAddress === 'string' ? localsAddress : addressCodec.decode(localsAddress), + appId: bigIntCodec.decode(localsAppId), }, }) continue @@ -957,11 +980,11 @@ export function fromTransactionDto(transactionDto: TransactionDto): Transaction throw new Error(`Box missing name: ${JSON.stringify(ref.b)}`) } - const boxAppId = boxAppIdx === 0 ? BigInt(0) : result[boxAppIdx - 1].appId! + const boxAppId = boxAppIdx === 0 ? BigInt(0) : accessList[boxAppIdx - 1].p! result.push({ box: { - appId: boxAppId, + appId: bigIntCodec.decode(boxAppId), name: bytesCodec.decode(name), }, }) diff --git a/packages/transact/tests/app_call.test.ts b/packages/transact/tests/app_call.test.ts index 81ef1ba5..dd5f5128 100644 --- a/packages/transact/tests/app_call.test.ts +++ b/packages/transact/tests/app_call.test.ts @@ -1,6 +1,8 @@ -import { describe, expect, test } from 'vitest' +import { ZERO_ADDRESS } from '@algorandfoundation/algokit-common' +import { ResourceReferenceDto } from '@algorandfoundation/algokit-transact/encoding/transaction-dto' +import { assert, describe, expect, test } from 'vitest' import { OnApplicationComplete } from '../src/transactions/app-call' -import { Transaction, TransactionType, validateTransaction } from '../src/transactions/transaction' +import { Transaction, TransactionType, fromTransactionDto, toTransactionDto, validateTransaction } from '../src/transactions/transaction' import { testData } from './common' import { assertAssignFee, @@ -75,7 +77,7 @@ describe('App Call', () => { describe('App Creation Validation', () => { test('should throw error when approval program is missing for app creation', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -92,7 +94,7 @@ describe('App Call', () => { test('should throw error when clear state program is missing for app creation', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -109,7 +111,7 @@ describe('App Call', () => { test('should throw error when extra program pages exceed maximum', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -130,7 +132,7 @@ describe('App Call', () => { test('should throw error when approval program exceeds max size', () => { const largeProgram = new Uint8Array(2049) // Exceeds basic 2048 byte limit const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -149,7 +151,7 @@ describe('App Call', () => { test('should throw error when clear state program exceeds max size', () => { const largeProgram = new Uint8Array(2049) // Exceeds basic 2048 byte limit const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -169,7 +171,7 @@ describe('App Call', () => { const mediumProgram1 = new Uint8Array(1500) const mediumProgram2 = new Uint8Array(1500) // Combined: 3000 > 2048 const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -189,7 +191,7 @@ describe('App Call', () => { test('should throw error when global state schema exceeds maximum keys', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -210,7 +212,7 @@ describe('App Call', () => { test('should throw error when local state schema exceeds maximum keys', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -231,7 +233,7 @@ describe('App Call', () => { test('should validate valid app creation transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -258,7 +260,7 @@ describe('App Call', () => { test('should validate app creation with large programs when extra pages are provided', () => { const largeProgram = new Uint8Array(4000) // Requires extra pages const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -278,7 +280,7 @@ describe('App Call', () => { describe('App Update Validation', () => { test('should throw error when approval program is missing for app update', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -295,7 +297,7 @@ describe('App Call', () => { test('should throw error when clear state program is missing for app update', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -312,7 +314,7 @@ describe('App Call', () => { test('should throw error when trying to modify immutable field (global state schema)', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -336,7 +338,7 @@ describe('App Call', () => { test('should throw error when trying to modify immutable field (local state schema)', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -360,7 +362,7 @@ describe('App Call', () => { test('should throw error when trying to modify immutable field (extra program pages)', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -380,7 +382,7 @@ describe('App Call', () => { test('should validate valid app update transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -400,7 +402,7 @@ describe('App Call', () => { describe('App Call/Delete Validation', () => { test('should validate valid app call transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -419,7 +421,7 @@ describe('App Call', () => { test('should validate valid app delete transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -434,7 +436,7 @@ describe('App Call', () => { test('should validate app opt-in transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -449,7 +451,7 @@ describe('App Call', () => { test('should validate app close-out transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -464,7 +466,7 @@ describe('App Call', () => { test('should validate app clear state transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -482,7 +484,7 @@ describe('App Call', () => { test('should throw error when too many args are provided', () => { const manyArgs = Array.from({ length: 17 }, (_, i) => new Uint8Array([i])) // Max is 16 const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -499,7 +501,7 @@ describe('App Call', () => { test('should throw error when args total size exceeds maximum', () => { const largeArg = new Uint8Array(2049) // Exceeds 2048 byte limit const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -516,7 +518,7 @@ describe('App Call', () => { test('should throw error when too many account references are provided', () => { const manyAccounts = Array.from({ length: 9 }, () => 'ADSFKJSDFKJSDFKJSDFKJSDFKJSDFKJSDFKJSDFKJSDFKJSDFK') // Max is 8 const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -527,13 +529,13 @@ describe('App Call', () => { }, } - expect(() => validateTransaction(transaction)).toThrow('App call validation failed: Account references cannot exceed 8 refs') + expect(() => validateTransaction(transaction)).toThrow('Account references cannot exceed 8 refs') }) test('should throw error when too many app references are provided', () => { const manyApps = Array.from({ length: 9 }, (_, i) => BigInt(i + 1)) // Max is 8 const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -544,13 +546,13 @@ describe('App Call', () => { }, } - expect(() => validateTransaction(transaction)).toThrow('App call validation failed: App references cannot exceed 8 refs') + expect(() => validateTransaction(transaction)).toThrow('App references cannot exceed 8 refs') }) test('should throw error when too many asset references are provided', () => { const manyAssets = Array.from({ length: 9 }, (_, i) => BigInt(i + 1)) // Max is 8 const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -561,7 +563,7 @@ describe('App Call', () => { }, } - expect(() => validateTransaction(transaction)).toThrow('App call validation failed: Asset references cannot exceed 8 refs') + expect(() => validateTransaction(transaction)).toThrow('Asset references cannot exceed 8 refs') }) test('should validate app call with maximum allowed references', () => { @@ -572,7 +574,7 @@ describe('App Call', () => { const maxArgs = Array.from({ length: 16 }, (_, i) => new Uint8Array([i])) const transaction: Transaction = { - transactionType: TransactionType.AppCall, + type: TransactionType.AppCall, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -590,4 +592,82 @@ describe('App Call', () => { }) }) }) + + describe('Encoding / decoding tests', () => { + test('should decode access list', () => { + const addr1 = 'FDMKB5D72THLYSJEBHBDHUE7XFRDOM5IHO44SOJ7AWPD6EZMWOQ2WKN7HQ' + const txn: Transaction = { + sender: 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4', + firstValid: 322575n, + lastValid: 322575n, + fee: 1000n, + genesisId: 'testnet-v1.0', + genesisHash: new Uint8Array(Buffer.from('SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', 'base64')), + type: TransactionType.AppCall, + appCall: { + appId: 111n, + onComplete: OnApplicationComplete.NoOp, + accessReferences: [ + { + holding: { + assetId: 123n, + address: addr1, + }, + }, + { address: addr1 }, + { assetId: 123n }, + ], + }, + } + + // This code is here to demonstrate the problem. + // When encoding, the cross product references are added first, + // so modify the access list encoding data to simulate how it may be encoded on chain. + const txnDto = toTransactionDto(txn) + const accessList = txnDto.al! + // Index 2 is actually the holding reference. + // Manually adjust the indexes, because we'll be re-ording the list. + accessList[2]!.h!.d = 2 + accessList[2]!.h!.s = 3 + const updateAccessList: ResourceReferenceDto[] = [] + updateAccessList.push(accessList[2]) + updateAccessList.push(accessList[0]) + updateAccessList.push(accessList[1]) + txnDto.al = updateAccessList + + const decodedTxn = fromTransactionDto(txnDto) + assert.deepStrictEqual(decodedTxn?.appCall!.accessReferences, txn?.appCall!.accessReferences) + }) + + test('should skip empty access local item', () => { + const sender = 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4' + + const txn: Transaction = { + sender: sender, + firstValid: 322575n, + lastValid: 322575n, + fee: 1000n, + genesisId: 'testnet-v1.0', + genesisHash: new Uint8Array(Buffer.from('SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', 'base64')), + type: TransactionType.AppCall, + appCall: { + appId: 111n, + onComplete: OnApplicationComplete.NoOp, + accessReferences: [ + { + // When the appId is the current app and the address is ZERO_ADDRESS + // They are converted to zero values and should be skipped from the msgpack encode + locals: { + appId: 111n, + address: ZERO_ADDRESS, + }, + }, + ], + }, + } + + const txnDto = toTransactionDto(txn) + assert.isEmpty(txnDto.al!) + }) + }) }) diff --git a/packages/transact/tests/asset_config.test.ts b/packages/transact/tests/asset_config.test.ts index 12541718..3f5ccc1e 100644 --- a/packages/transact/tests/asset_config.test.ts +++ b/packages/transact/tests/asset_config.test.ts @@ -73,7 +73,7 @@ describe('AssetConfig', () => { describe('Asset Creation Validation', () => { test('should throw error when total is missing for asset creation', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -91,7 +91,7 @@ describe('AssetConfig', () => { test('should throw error when decimals exceed maximum', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -111,7 +111,7 @@ describe('AssetConfig', () => { test('should throw error when unit name is too long', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -130,7 +130,7 @@ describe('AssetConfig', () => { test('should throw error when asset name is too long', () => { const longName = 'A'.repeat(33) // Maximum is 32 bytes const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -149,7 +149,7 @@ describe('AssetConfig', () => { test('should throw error when URL is too long', () => { const longUrl = `https://${'a'.repeat(90)}` // Total > 96 bytes const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -170,7 +170,7 @@ describe('AssetConfig', () => { const longName = 'A'.repeat(33) const longUrl = `https://${'a'.repeat(90)}` const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -199,7 +199,7 @@ describe('AssetConfig', () => { test('should validate valid asset creation transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -224,7 +224,7 @@ describe('AssetConfig', () => { test('should validate asset creation with minimum valid values', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -243,7 +243,7 @@ describe('AssetConfig', () => { const maxUnitName = 'MAXUNIT8' // 8 bytes maximum const maxUrl = `https://${'a'.repeat(88)}` // 96 bytes total (7 + 89 = 96) const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -262,7 +262,7 @@ describe('AssetConfig', () => { test('should validate asset creation with default frozen true', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -282,7 +282,7 @@ describe('AssetConfig', () => { describe('Asset Configuration/Reconfiguration Validation', () => { test('should throw error when trying to modify immutable field (total)', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -297,7 +297,7 @@ describe('AssetConfig', () => { test('should throw error when trying to modify immutable field (decimals)', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -314,7 +314,7 @@ describe('AssetConfig', () => { test('should throw multiple errors when trying to modify multiple immutable fields', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -347,7 +347,7 @@ describe('AssetConfig', () => { test('should validate valid asset reconfiguration', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -365,7 +365,7 @@ describe('AssetConfig', () => { test('should validate valid asset destruction (no params)', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -380,7 +380,7 @@ describe('AssetConfig', () => { test('should validate asset reconfiguration removing all special addresses', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -398,7 +398,7 @@ describe('AssetConfig', () => { test('should validate asset reconfiguration with single field change', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetConfig, + type: TransactionType.AssetConfig, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, diff --git a/packages/transact/tests/asset_freeze.test.ts b/packages/transact/tests/asset_freeze.test.ts index e66fdc81..6c8e9273 100644 --- a/packages/transact/tests/asset_freeze.test.ts +++ b/packages/transact/tests/asset_freeze.test.ts @@ -66,7 +66,7 @@ describe('Asset Freeze', () => { describe('Asset Freeze Validation', () => { test('should throw error when asset ID is zero', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetFreeze, + type: TransactionType.AssetFreeze, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -82,7 +82,7 @@ describe('Asset Freeze', () => { test('should validate valid asset freeze transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetFreeze, + type: TransactionType.AssetFreeze, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -98,7 +98,7 @@ describe('Asset Freeze', () => { test('should validate asset unfreeze transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetFreeze, + type: TransactionType.AssetFreeze, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -115,7 +115,7 @@ describe('Asset Freeze', () => { test('should validate freezing the sender themselves', () => { const senderAddress = 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA' const transaction: Transaction = { - transactionType: TransactionType.AssetFreeze, + type: TransactionType.AssetFreeze, sender: senderAddress, firstValid: 1000n, lastValid: 2000n, @@ -132,7 +132,7 @@ describe('Asset Freeze', () => { test('should validate unfreezing the sender themselves', () => { const senderAddress = 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA' const transaction: Transaction = { - transactionType: TransactionType.AssetFreeze, + type: TransactionType.AssetFreeze, sender: senderAddress, firstValid: 1000n, lastValid: 2000n, diff --git a/packages/transact/tests/asset_transfer.test.ts b/packages/transact/tests/asset_transfer.test.ts index 9dc4da10..c5961fb1 100644 --- a/packages/transact/tests/asset_transfer.test.ts +++ b/packages/transact/tests/asset_transfer.test.ts @@ -70,7 +70,7 @@ describe('AssetTransfer', () => { describe('Asset Transfer Validation', () => { test('should throw error when asset ID is zero', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetTransfer, + type: TransactionType.AssetTransfer, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -86,7 +86,7 @@ describe('AssetTransfer', () => { test('should validate valid asset transfer transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetTransfer, + type: TransactionType.AssetTransfer, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -103,7 +103,7 @@ describe('AssetTransfer', () => { test('should validate asset opt-in transaction', () => { const senderAddress = 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA' const transaction: Transaction = { - transactionType: TransactionType.AssetTransfer, + type: TransactionType.AssetTransfer, sender: senderAddress, firstValid: 1000n, lastValid: 2000n, @@ -119,7 +119,7 @@ describe('AssetTransfer', () => { test('should validate asset transfer with clawback', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetTransfer, + type: TransactionType.AssetTransfer, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', // Clawback address firstValid: 1000n, lastValid: 2000n, @@ -136,7 +136,7 @@ describe('AssetTransfer', () => { test('should validate asset opt-out transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetTransfer, + type: TransactionType.AssetTransfer, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -153,7 +153,7 @@ describe('AssetTransfer', () => { test('should validate asset transfer with both clawback and close remainder', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetTransfer, + type: TransactionType.AssetTransfer, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -172,7 +172,7 @@ describe('AssetTransfer', () => { test('should validate asset transfer to self', () => { const senderAddress = 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA' const transaction: Transaction = { - transactionType: TransactionType.AssetTransfer, + type: TransactionType.AssetTransfer, sender: senderAddress, firstValid: 1000n, lastValid: 2000n, @@ -188,7 +188,7 @@ describe('AssetTransfer', () => { test('should validate asset close-out transaction (zero amount with close remainder)', () => { const transaction: Transaction = { - transactionType: TransactionType.AssetTransfer, + type: TransactionType.AssetTransfer, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, diff --git a/packages/transact/tests/common.ts b/packages/transact/tests/common.ts index c4429785..5c138d70 100644 --- a/packages/transact/tests/common.ts +++ b/packages/transact/tests/common.ts @@ -92,7 +92,7 @@ const defaultReviver = (key: string, value: unknown) => { } } - if (key === 'transactionType') { + if (key === 'type') { return transactionTypes[value as keyof typeof transactionTypes] as TransactionType } diff --git a/packages/transact/tests/key_registration.test.ts b/packages/transact/tests/key_registration.test.ts index 08e6ec46..390544cc 100644 --- a/packages/transact/tests/key_registration.test.ts +++ b/packages/transact/tests/key_registration.test.ts @@ -73,7 +73,7 @@ describe('Key Registration', () => { describe('Online Key Registration Validation', () => { test('should throw error when vote key is missing for online registration', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -92,7 +92,7 @@ describe('Key Registration', () => { test('should throw error when selection key is missing for online registration', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -111,7 +111,7 @@ describe('Key Registration', () => { test('should throw error when state proof key is missing for online registration', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -130,7 +130,7 @@ describe('Key Registration', () => { test('should throw error when vote first is missing for online registration', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -149,7 +149,7 @@ describe('Key Registration', () => { test('should throw error when vote last is missing for online registration', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -168,7 +168,7 @@ describe('Key Registration', () => { test('should throw error when vote key dilution is missing for online registration', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -187,7 +187,7 @@ describe('Key Registration', () => { test('should throw error when vote first is equal to vote last', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -206,7 +206,7 @@ describe('Key Registration', () => { test('should throw error when vote first is greater than vote last', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -225,7 +225,7 @@ describe('Key Registration', () => { test('should throw error when non participation is set for online registration', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -247,7 +247,7 @@ describe('Key Registration', () => { test('should throw multiple errors for online registration with multiple missing fields', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -278,7 +278,7 @@ describe('Key Registration', () => { test('should validate valid online key registration transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -297,7 +297,7 @@ describe('Key Registration', () => { test('should validate online key registration with non participation false', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -319,7 +319,7 @@ describe('Key Registration', () => { describe('Offline Key Registration Validation', () => { test('should validate offline key registration (no fields)', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -333,7 +333,7 @@ describe('Key Registration', () => { test('should validate offline key registration with non participation', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, @@ -347,7 +347,7 @@ describe('Key Registration', () => { test('should validate offline key registration with non participation false', () => { const transaction: Transaction = { - transactionType: TransactionType.KeyRegistration, + type: TransactionType.KeyRegistration, sender: '424ZV7KBBUJ52DUKP2KLQ6I5GBOHKBXOW7Q7UQIOOYNDWYRM4EKOSMVVRI', firstValid: 1000n, lastValid: 2000n, diff --git a/packages/transact/tests/payment.test.ts b/packages/transact/tests/payment.test.ts index b6701aa5..e588612a 100644 --- a/packages/transact/tests/payment.test.ts +++ b/packages/transact/tests/payment.test.ts @@ -70,7 +70,7 @@ describe('Payment', () => { describe('Payment Transaction Validation', () => { test('should validate valid payment transaction', () => { const transaction: Transaction = { - transactionType: TransactionType.Payment, + type: TransactionType.Payment, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -85,7 +85,7 @@ describe('Payment', () => { test('should validate payment transaction with zero amount', () => { const transaction: Transaction = { - transactionType: TransactionType.Payment, + type: TransactionType.Payment, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -100,7 +100,7 @@ describe('Payment', () => { test('should validate payment transaction with close remainder', () => { const transaction: Transaction = { - transactionType: TransactionType.Payment, + type: TransactionType.Payment, sender: 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA', firstValid: 1000n, lastValid: 2000n, @@ -117,7 +117,7 @@ describe('Payment', () => { test('should validate self-payment transaction', () => { const senderAddress = 'XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA' const transaction: Transaction = { - transactionType: TransactionType.Payment, + type: TransactionType.Payment, sender: senderAddress, firstValid: 1000n, lastValid: 2000n, diff --git a/packages/transact/tests/test_data.json b/packages/transact/tests/test_data.json index 2deb929f..7df33c21 100644 --- a/packages/transact/tests/test_data.json +++ b/packages/transact/tests/test_data.json @@ -108,7 +108,7 @@ "lastValid": 21039300, "note": [0, 0, 0, 0, 0, 15, 66, 64], "sender": "KVAGZI3WJI36TTTKJUI36ECGP3NHGR5VBJNIXG3DROHPGH2XFC36D4HENE", - "transactionType": "AppCall" + "type": "AppCall" }, "unsignedBytes": [ 84, 88, 141, 164, 97, 112, 97, 97, 148, 196, 4, 109, 105, 110, 116, 196, 8, 0, 0, 0, 0, 0, 15, 66, 64, 196, 15, 115, 101, 99, 117, @@ -349,7 +349,7 @@ "lastValid": 21039057, "note": [78, 70, 68, 32, 82, 101, 103, 105, 115, 116, 114, 121, 32, 67, 111, 110, 116, 114, 97, 99, 116], "sender": "3Y62HTJ4WYSIEKC74XE3F2JFCS7774EN3CYNUHQCEFIN7QBYFAWLKE5MFY", - "transactionType": "AppCall" + "type": "AppCall" }, "unsignedBytes": [ 84, 88, 140, 164, 97, 112, 97, 112, 197, 4, 170, 6, 32, 10, 1, 0, 2, 8, 4, 6, 16, 10, 5, 3, 38, 3, 6, 105, 46, 97, 112, 112, 115, 8, @@ -481,7 +481,7 @@ "genesisId": "mainnet-v1.0", "lastValid": 39724798, "sender": "H3OQEQIIC35RZTJNU5A75LT4PCTUCF3VKVEQTSXAJMUGNTRUKEKI4QSRW4", - "transactionType": "AppCall" + "type": "AppCall" }, "unsignedBytes": [ 84, 88, 139, 164, 97, 112, 97, 110, 5, 164, 97, 112, 97, 115, 145, 206, 50, 184, 18, 152, 164, 97, 112, 97, 116, 147, 196, 32, 96, @@ -1434,7 +1434,7 @@ 75, 72, 78, 75, 78, 82, 88, 54, 54, 78, 69, 90, 73, 84, 85, 76, 77 ], "sender": "3Y62HTJ4WYSIEKC74XE3F2JFCS7774EN3CYNUHQCEFIN7QBYFAWLKE5MFY", - "transactionType": "AppCall" + "type": "AppCall" }, "unsignedBytes": [ 84, 88, 141, 164, 97, 112, 97, 97, 145, 196, 16, 116, 101, 97, 108, 115, 99, 114, 105, 112, 116, 45, 100, 117, 109, 109, 121, 164, 97, @@ -1858,7 +1858,7 @@ 105, 115, 101, 34, 125 ], "sender": "EHYQCYHUC6CIWZLBX5TDTLVJ4SSVE4RRTMKFDCG4Z4Q7QSQ2XWIQPMKBPU", - "transactionType": "AssetConfig" + "type": "AssetConfig" }, "unsignedBytes": [ 84, 88, 138, 164, 97, 112, 97, 114, 130, 161, 109, 196, 32, 33, 241, 1, 96, 244, 23, 132, 139, 101, 97, 191, 102, 57, 174, 169, 228, @@ -2029,7 +2029,7 @@ 34, 125 ], "sender": "KPVZ66IFE7KHQ6623XHTPVS3IL7BXBE3HXQG35J65CVDA54VLRPP4SVOU4", - "transactionType": "AssetConfig" + "type": "AssetConfig" }, "unsignedBytes": [ 84, 88, 137, 164, 97, 112, 97, 114, 136, 162, 97, 110, 174, 70, 114, 97, 99, 99, 116, 97, 108, 32, 84, 111, 107, 101, 110, 162, 97, @@ -2119,7 +2119,7 @@ "lastValid": 6355623, "note": [125, 38, 141, 238, 86, 74, 14, 133], "sender": "MBX2M6J44LQ22L3FROYRBKUAG4FWENPSLPTI7EBR4ECQ2APDMI6XTENHWQ", - "transactionType": "AssetConfig" + "type": "AssetConfig" }, "unsignedBytes": [ 84, 88, 136, 164, 99, 97, 105, 100, 206, 0, 14, 0, 55, 163, 102, 101, 101, 205, 3, 232, 162, 102, 118, 206, 0, 96, 246, 191, 162, 103, @@ -2212,7 +2212,7 @@ "lastValid": 37464562, "note": [78, 70, 84, 32, 102, 114, 101, 101, 122, 101, 100, 32, 98, 121, 32, 108, 111, 102, 116, 121, 46, 97, 105], "sender": "E4A6FVIHXSZ3F7QXRCOTYDDILVQYEBFH56HYDIIYX4SVXS2QX5GUTBVZHY", - "transactionType": "AssetFreeze" + "type": "AssetFreeze" }, "unsignedBytes": [ 84, 88, 140, 164, 97, 102, 114, 122, 195, 164, 102, 97, 100, 100, 196, 32, 202, 105, 187, 232, 58, 131, 118, 26, 5, 9, 247, 19, 158, @@ -2295,7 +2295,7 @@ "lastValid": 3278583, "note": [182, 30, 9, 15, 17, 81, 57, 12], "sender": "WLH5LELVSEVQL45LBRQYCLJAX6KQPGWUY5WHJXVRV2NPYZUBQAFPH22Q7A", - "transactionType": "AssetFreeze" + "type": "AssetFreeze" }, "unsignedBytes": [ 84, 88, 137, 164, 102, 97, 100, 100, 196, 32, 206, 33, 127, 135, 62, 89, 166, 63, 208, 82, 250, 123, 26, 144, 10, 61, 18, 245, 108, @@ -2440,7 +2440,7 @@ }, "lastValid": 48023111, "sender": "GAU5WA6DT2EPFS6LKOA333BQP67NXIHZ7JPOOHMZWJDPZRL4XMHDDDUCKA", - "transactionType": "Heartbeat" + "type": "Heartbeat" }, "unsignedBytes": [ 84, 88, 134, 162, 102, 118, 206, 2, 220, 198, 61, 162, 103, 104, 196, 32, 72, 99, 181, 24, 164, 179, 200, 78, 200, 16, 242, 45, 79, @@ -2525,7 +2525,7 @@ }, "lastValid": 3322800, "sender": "4UMX2FKZ636VEL74WR66Z5PSRVDC2QAH6GRPP2DTVSBPPADBDY2JB3PN2U", - "transactionType": "KeyRegistration" + "type": "KeyRegistration" }, "unsignedBytes": [ 84, 88, 135, 163, 102, 101, 101, 205, 3, 232, 162, 102, 118, 206, 0, 50, 175, 200, 162, 103, 104, 196, 32, 72, 99, 181, 24, 164, 179, @@ -2594,7 +2594,7 @@ "keyRegistration": {}, "lastValid": 52557882, "sender": "W5LKXE4BG7OND7KPGSXPDOOYQDITPNS7NSDU7672TO6I4RDNSEFWXRPISQ", - "transactionType": "KeyRegistration" + "type": "KeyRegistration" }, "unsignedBytes": [ 84, 88, 134, 163, 102, 101, 101, 205, 3, 232, 162, 102, 118, 206, 3, 33, 244, 82, 162, 103, 104, 196, 32, 72, 99, 181, 24, 164, 179, @@ -2699,7 +2699,7 @@ }, "lastValid": 53288880, "sender": "PKASUHJJ7HALD6BXBIOLQTRFHAP6HF2TAYQ734E325FGDRB66EE6MYQGTM", - "transactionType": "KeyRegistration" + "type": "KeyRegistration" }, "unsignedBytes": [ 84, 88, 140, 163, 102, 101, 101, 206, 0, 30, 132, 128, 162, 102, 118, 206, 3, 45, 27, 200, 162, 103, 104, 196, 32, 72, 99, 181, 24, @@ -2785,7 +2785,7 @@ "genesisId": "testnet-v1.0", "lastValid": 51183872, "sender": "JB3K6HTAXODO4THESLNYTSG6GQUFNEVIQG7A6ZYVDACR6WA3ZF52TKU5NA", - "transactionType": "AssetTransfer" + "type": "AssetTransfer" }, "unsignedBytes": [ 84, 88, 137, 164, 97, 114, 99, 118, 196, 32, 72, 118, 175, 30, 96, 187, 134, 238, 76, 228, 146, 219, 137, 200, 222, 52, 40, 86, 146, @@ -2866,7 +2866,7 @@ "receiver": "VXH5UP6JLU2CGIYPUFZ4Z5OTLJCLMA5EXD3YHTMVNDE5P7ILZ324FSYSPQ" }, "sender": "RIMARGKZU46OZ77OLPDHHPUJ7YBSHRTCYMQUC64KZCCMESQAFQMYU6SL2Q", - "transactionType": "Payment" + "type": "Payment" }, "unsignedBytes": [ 84, 88, 137, 163, 97, 109, 116, 206, 0, 1, 138, 136, 163, 102, 101, 101, 205, 3, 232, 162, 102, 118, 206, 3, 5, 0, 212, 163, 103, 101, @@ -21107,7 +21107,7 @@ "signedWeight": 7580024302929212 } }, - "transactionType": "StateProof" + "type": "StateProof" }, "unsignedBytes": [ 84, 88, 135, 162, 102, 118, 206, 1, 111, 184, 129, 162, 103, 104, 196, 32, 72, 99, 181, 24, 164, 179, 200, 78, 200, 16, 242, 45, 79, diff --git a/packages/transact/tests/transaction_asserts.ts b/packages/transact/tests/transaction_asserts.ts index d01ab40a..24ebfa6c 100644 --- a/packages/transact/tests/transaction_asserts.ts +++ b/packages/transact/tests/transaction_asserts.ts @@ -18,11 +18,10 @@ import { TransactionTestData } from './common' export const assertExample = async (label: string, testData: TransactionTestData) => { const signedTxn: SignedTransaction = { - transaction: testData.transaction, + txn: testData.transaction, signature: await ed.signAsync(encodeTransaction(testData.transaction), testData.signingPrivateKey), } const encodedSignedTxn = encodeSignedTransaction(signedTxn) - expect(encodedSignedTxn, label).toEqual(testData.signedBytes) } @@ -32,7 +31,7 @@ export const assertTransactionId = (label: string, testData: TransactionTestData } export const assertEncodedTransactionType = (label: string, testData: TransactionTestData) => { - expect(getEncodedTransactionType(testData.unsignedBytes), label).toBe(testData.transaction.transactionType) + expect(getEncodedTransactionType(testData.unsignedBytes), label).toBe(testData.transaction.type) } export const assertDecodeWithoutPrefix = (label: string, testData: TransactionTestData) => { @@ -48,7 +47,7 @@ export const assertDecodeWithPrefix = (label: string, testData: TransactionTestD export const assertEncodeWithAuthAddress = async (label: string, testData: TransactionTestData) => { const sig = await ed.signAsync(testData.unsignedBytes, testData.signingPrivateKey) const signedTxn: SignedTransaction = { - transaction: testData.transaction, + txn: testData.transaction, signature: sig, authAddress: testData.rekeyedSenderAuthAddress, } @@ -60,7 +59,7 @@ export const assertEncodeWithAuthAddress = async (label: string, testData: Trans export const assertEncodeWithSignature = async (label: string, testData: TransactionTestData) => { const sig = await ed.signAsync(testData.unsignedBytes, testData.signingPrivateKey) const signedTxn: SignedTransaction = { - transaction: testData.transaction, + txn: testData.transaction, signature: sig, } const encodedSignedTxn = encodeSignedTransaction(signedTxn) @@ -96,7 +95,7 @@ export const assertMultisigExample = async (label: string, testData: Transaction const multisigSignature = mergeMultisignatures(multisigSignature0, multisigSignature1) const signedTxn: SignedTransaction = { - transaction: testData.transaction, + txn: testData.transaction, multiSignature: multisigSignature, } const encodedSignedTxn = encodeSignedTransaction(signedTxn) diff --git a/packages/transact/tests/transaction_group.test.ts b/packages/transact/tests/transaction_group.test.ts index 96265029..3803ad21 100644 --- a/packages/transact/tests/transaction_group.test.ts +++ b/packages/transact/tests/transaction_group.test.ts @@ -68,7 +68,7 @@ describe('Transaction Group', () => { const signedGroupedTxs = groupedTxs.map((tx, i) => { return { - transaction: tx, + txn: tx, signature: txSignatures[i], } as SignedTransaction }) @@ -79,7 +79,7 @@ describe('Transaction Group', () => { for (let i = 0; i < encodedSignedGroupedTxs.length; i++) { expect(encodedSignedGroupedTxs[i]).toEqual( encodeSignedTransaction({ - transaction: groupedTxs[i], + txn: groupedTxs[i], signature: txSignatures[i], }), ) diff --git a/packages/transact/tsconfig.build.json b/packages/transact/tsconfig.build.json deleted file mode 100644 index 0e149d39..00000000 --- a/packages/transact/tsconfig.build.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["src/**/*.ts"], - "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "tests/**/*.*"] -} diff --git a/packages/transact/tsconfig.json b/packages/transact/tsconfig.json index 71b6c1f8..079567a1 100644 --- a/packages/transact/tsconfig.json +++ b/packages/transact/tsconfig.json @@ -1,8 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "dist", - "tsBuildInfoFile": "build/.tsbuildinfo" + "outDir": "dist" }, "include": ["src/**/*.ts", "tests/**/*.ts"] } diff --git a/rolldown.config.ts b/rolldown.config.ts index 15632104..65a04e42 100644 --- a/rolldown.config.ts +++ b/rolldown.config.ts @@ -2,6 +2,6 @@ import pkg from './package.json' with { type: 'json' } import createConfig from './rolldown' export default createConfig( - [...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {}), /^algosdk\/*/], + [...Object.keys(pkg.dependencies || {})], ['src/index.ts', 'src/testing/index.ts', 'src/types/*.ts', '!src/types/*.spec.ts'], ) diff --git a/src/account/account.ts b/src/account/account.ts index c46ff105..b96a836d 100644 --- a/src/account/account.ts +++ b/src/account/account.ts @@ -1,4 +1,7 @@ -import algosdk, { Address } from 'algosdk' +import { Account as AccountInformation, AlgodClient } from '@algorandfoundation/algokit-algod-client' +import type { Account } from '@algorandfoundation/sdk' +import * as algosdk from '@algorandfoundation/sdk' +import { Address, Kmd, MultisigMetadata, TransactionSigner } from '@algorandfoundation/sdk' import { getSenderAddress } from '../transaction/transaction' import { AccountAssetInformation, MultisigAccount, SigningAccount, TransactionSignerAccount } from '../types/account' import { AccountManager } from '../types/account-manager' @@ -6,12 +9,6 @@ import { AlgorandClient } from '../types/algorand-client' import { AlgoAmount } from '../types/amount' import { ClientManager } from '../types/client-manager' import { SendTransactionFrom } from '../types/transaction' -import Account = algosdk.Account -import Algodv2 = algosdk.Algodv2 -import Kmd = algosdk.Kmd -import MultisigMetadata = algosdk.MultisigMetadata -import TransactionSigner = algosdk.TransactionSigner -import AccountInformationModel = algosdk.modelsv2.Account /** * @deprecated Use `algorand.account.multisig(multisigParams, signingAccounts)` or `new MultisigAccount(multisigParams, signingAccounts)` instead. @@ -96,7 +93,7 @@ export function randomAccount(): Account { */ export async function mnemonicAccountFromEnvironment( account: string | { name: string; fundWith?: AlgoAmount }, - algod: Algodv2, + algod: AlgodClient, kmdClient?: Kmd, ): Promise { return ( @@ -129,14 +126,6 @@ export function getAccountAddressAsString(addressEncodedInB64: string): string { return algosdk.encodeAddress(Buffer.from(addressEncodedInB64, 'base64')) } -export type NumberConverter = { [key in keyof T]: ToNumberIfExtends } -type ToNumberIfExtends = K extends E ? number : K -/** @deprecated Account information at a given round. */ -export type AccountInformation = Omit, 'getEncodingSchema' | 'toEncodingData' | 'authAddr'> & { - /** (spend) the address against which signing should be checked. If empty, the address of the current account is used. This field can be updated in any transaction by setting the RekeyTo field. */ - authAddr?: string -} - /** * @deprecated Use `algorand.account.getInformation(sender)` or `new AccountManager(clientManager).getInformation(sender)` instead. * @@ -153,31 +142,8 @@ export type AccountInformation = Omit, * @param algod The algod instance * @returns The account information */ -export async function getAccountInformation(sender: string | SendTransactionFrom, algod: Algodv2): Promise { - const account = await algod.accountInformation(getSenderAddress(sender)).do() - - return { - ...account, - address: account.address.toString(), - authAddr: account.authAddr ? account.authAddr.toString() : undefined, - // None of these can practically overflow 2^53 - amount: Number(account.amount), - amountWithoutPendingRewards: Number(account.amountWithoutPendingRewards), - minBalance: Number(account.minBalance), - pendingRewards: Number(account.pendingRewards), - rewards: Number(account.rewards), - round: Number(account.round), - totalAppsOptedIn: Number(account.totalAppsOptedIn), - totalAssetsOptedIn: Number(account.totalAssetsOptedIn), - totalCreatedApps: Number(account.totalCreatedApps), - totalCreatedAssets: Number(account.totalCreatedAssets), - appsTotalExtraPages: account.appsTotalExtraPages !== undefined ? Number(account.appsTotalExtraPages) : undefined, - rewardBase: account.rewardBase !== undefined ? Number(account.rewardBase) : undefined, - totalBoxBytes: account.totalBoxBytes !== undefined ? Number(account.totalBoxBytes) : undefined, - totalBoxes: account.totalBoxes !== undefined ? Number(account.totalBoxes) : undefined, - lastHeartbeat: account.lastHeartbeat !== undefined ? Number(account.lastHeartbeat) : undefined, - lastProposed: account.lastProposed !== undefined ? Number(account.lastProposed) : undefined, - } +export async function getAccountInformation(sender: string | SendTransactionFrom, algod: AlgodClient): Promise { + return await algod.accountInformation(getSenderAddress(sender)) } /** @@ -201,7 +167,7 @@ export async function getAccountInformation(sender: string | SendTransactionFrom export async function getAccountAssetInformation( sender: string | SendTransactionFrom, assetId: number | bigint, - algod: Algodv2, + algod: AlgodClient, ): Promise { return AlgorandClient.fromClients({ algod }).asset.getAccountInformation(getSenderAddress(sender), BigInt(assetId)) } diff --git a/src/account/get-account.ts b/src/account/get-account.ts index 7d054392..ec75cbde 100644 --- a/src/account/get-account.ts +++ b/src/account/get-account.ts @@ -1,12 +1,10 @@ -import algosdk from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' +import { type Account, Kmd } from '@algorandfoundation/sdk' import { AccountConfig, SigningAccount } from '../types/account' import { AccountManager } from '../types/account-manager' import { AlgoAmount } from '../types/amount' import { ClientManager } from '../types/client-manager' import { getAccountConfigFromEnvironment } from './get-account-config-from-environment' -import Account = algosdk.Account -import Algodv2 = algosdk.Algodv2 -import Kmd = algosdk.Kmd /** @deprecated use `algorand.account.fromEnvironment()` instead * @@ -40,7 +38,7 @@ import Kmd = algosdk.Kmd */ export async function getAccount( account: { name: string; fundWith?: AlgoAmount } | string, - algod: Algodv2, + algod: AlgodClient, kmdClient?: Kmd, ): Promise @@ -67,7 +65,7 @@ export async function getAccount( */ export async function getAccount( account: { config: AccountConfig; fundWith?: AlgoAmount }, - algod: Algodv2, + algod: AlgodClient, kmdClient?: Kmd, ): Promise @@ -104,7 +102,7 @@ export async function getAccount( */ export async function getAccount( account: { name: string; fundWith?: AlgoAmount } | { config: AccountConfig; fundWith?: AlgoAmount } | string, - algod: Algodv2, + algod: AlgodClient, kmdClient?: Kmd, ): Promise { let name: string diff --git a/src/account/get-dispenser-account.ts b/src/account/get-dispenser-account.ts index 04349f95..59e9c213 100644 --- a/src/account/get-dispenser-account.ts +++ b/src/account/get-dispenser-account.ts @@ -1,10 +1,8 @@ -import algosdk from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' +import { Kmd } from '@algorandfoundation/sdk' import { AccountManager } from '../types/account-manager' import { ClientManager } from '../types/client-manager' -import Algodv2 = algosdk.Algodv2 -import Kmd = algosdk.Kmd - /** * @deprecated Use `algorand.account.dispenserFromEnvironment()` or `new AccountManager(clientManager).dispenserFromEnvironment()` instead * @@ -16,6 +14,6 @@ import Kmd = algosdk.Kmd * @param algod An algod client * @param kmd A KMD client, if not specified then a default KMD client will be loaded from environment variables */ -export async function getDispenserAccount(algod: Algodv2, kmd?: Kmd) { +export async function getDispenserAccount(algod: AlgodClient, kmd?: Kmd) { return new AccountManager(new ClientManager({ algod, kmd })).dispenserFromEnvironment() } diff --git a/src/account/mnemonic-account.ts b/src/account/mnemonic-account.ts index ffad831f..50806d3f 100644 --- a/src/account/mnemonic-account.ts +++ b/src/account/mnemonic-account.ts @@ -1,5 +1,5 @@ -import algosdk from 'algosdk' -import Account = algosdk.Account +import * as algosdk from '@algorandfoundation/sdk' +import type { Account } from '@algorandfoundation/sdk' /** * @deprecated Use `algorand.account.fromMnemonic(mnemonicSecret)` or `algosdk.mnemonicToSecretKey(mnemonicSecret)` instead. diff --git a/src/app-client.ts b/src/app-client.ts index 54f48914..4bb2cab1 100644 --- a/src/app-client.ts +++ b/src/app-client.ts @@ -1,6 +1,5 @@ -import algosdk from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' import { AppSpecAppDetails, AppSpecAppDetailsByCreatorAndName, AppSpecAppDetailsById, ApplicationClient } from './types/app-client' -import Algodv2 = algosdk.Algodv2 /** * @deprecated Use `AppClient` instead e.g. via `algorand.client.getAppClientById` or @@ -37,7 +36,7 @@ import Algodv2 = algosdk.Algodv2 * * @returns The application client */ -export function getAppClient(appDetails: AppSpecAppDetails, algod: Algodv2) { +export function getAppClient(appDetails: AppSpecAppDetails, algod: AlgodClient) { return new ApplicationClient(appDetails, algod) } @@ -63,7 +62,7 @@ export function getAppClient(appDetails: AppSpecAppDetails, algod: Algodv2) { * * @returns The application client */ -export function getAppClientById(appDetails: AppSpecAppDetailsById, algod: Algodv2) { +export function getAppClientById(appDetails: AppSpecAppDetailsById, algod: AlgodClient) { return new ApplicationClient({ ...appDetails, resolveBy: 'id' }, algod) } @@ -90,6 +89,6 @@ export function getAppClientById(appDetails: AppSpecAppDetailsById, algod: Algod * * @returns The application client */ -export function getAppClientByCreatorAndName(appDetails: AppSpecAppDetailsByCreatorAndName, algod: Algodv2) { +export function getAppClientByCreatorAndName(appDetails: AppSpecAppDetailsByCreatorAndName, algod: AlgodClient) { return new ApplicationClient({ ...appDetails, resolveBy: 'creatorAndName' }, algod) } diff --git a/src/app-deploy.spec.ts b/src/app-deploy.spec.ts index fe636789..b6a0b3ef 100644 --- a/src/app-deploy.spec.ts +++ b/src/app-deploy.spec.ts @@ -1,4 +1,5 @@ -import { getApplicationAddress } from 'algosdk' +import { getTransactionId } from '@algorandfoundation/algokit-transact' +import { getApplicationAddress } from '@algorandfoundation/sdk' import invariant from 'tiny-invariant' import { afterEach, beforeEach, describe, expect, test } from 'vitest' import { getTestingAppCreateParams, getTestingAppDeployParams } from '../tests/example-contracts/testing-app/contract' @@ -20,10 +21,10 @@ describe('deploy-app', () => { const name = 'MY_APP' test('Created app is retrieved by name with deployment metadata', async () => { - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const creationMetadata = { name, version: '1.0', updatable: true, deletable: false } const app1 = await algorand.send.appCreate(await getTestingAppCreateParams(testAccount, creationMetadata)) - await waitForIndexer() + await waitForIndexerTransaction(getTransactionId(app1.transaction)) const apps = await algorand.appDeployer.getCreatorAppsByName(testAccount) @@ -42,12 +43,12 @@ describe('deploy-app', () => { }) test('Latest created app is retrieved', async () => { - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const creationMetadata = { name, version: '1.0', updatable: true, deletable: false } const app1 = await algorand.send.appCreate({ ...(await getTestingAppCreateParams(testAccount, creationMetadata)), lease: '1' }) const app2 = await algorand.send.appCreate({ ...(await getTestingAppCreateParams(testAccount, creationMetadata)), lease: '2' }) const app3 = await algorand.send.appCreate({ ...(await getTestingAppCreateParams(testAccount, creationMetadata)), lease: '3' }) - await waitForIndexer() + await waitForIndexerTransaction(getTransactionId(app3.transaction)) const apps = await algorand.appDeployer.getCreatorAppsByName(testAccount) @@ -57,7 +58,7 @@ describe('deploy-app', () => { }) test('Created, updated and deleted apps are retrieved by name with deployment metadata', async () => { - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const creationMetadata = { name, version: '1.0', updatable: true, deletable: true } const name2 = 'APP_2' @@ -68,8 +69,8 @@ describe('deploy-app', () => { const updateMetadata = { name, version: '2.0', updatable: false, deletable: false } const update1 = await algorand.send.appUpdate({ ...(await getTestingAppCreateParams(testAccount, updateMetadata)), appId: app1.appId }) - const _delete3 = await algorand.send.appDelete({ appId: app3.appId, sender: testAccount }) - await waitForIndexer() + const delete3 = await algorand.send.appDelete({ appId: app3.appId, sender: testAccount }) + await waitForIndexerTransaction(getTransactionId(delete3.transaction)) const apps = await algorand.appDeployer.getCreatorAppsByName(testAccount) @@ -106,7 +107,7 @@ describe('deploy-app', () => { invariant('transaction' in result) invariant(result.confirmation) - expect(result.appId).toBe(BigInt(result.confirmation.applicationIndex!)) + expect(result.appId).toBe(BigInt(result.confirmation.appId!)) expect(result.appAddress).toEqual(getApplicationAddress(result.appId)) expect(result.createdMetadata).toEqual(deployment.metadata) expect(result.createdRound).toBe(BigInt(result.confirmation.confirmedRound!)) @@ -150,14 +151,17 @@ describe('deploy-app', () => { }) test('Deploy update to updatable updated app', async () => { - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const metadata = getMetadata({ updatable: true }) const deployment1 = await getTestingAppDeployParams({ sender: testAccount, metadata: metadata, }) const result1 = await algorand.appDeployer.deploy(deployment1) - await waitForIndexer() + if (result1.operationPerformed !== 'nothing') { + await waitForIndexerTransaction(getTransactionId(result1.transaction)) + } + logging.testLogger.clear() const deployment2 = await getTestingAppDeployParams({ @@ -190,14 +194,16 @@ describe('deploy-app', () => { }) test('Deploy update to immutable updated app fails', async () => { - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const metadata = getMetadata({ updatable: false }) const deployment1 = await getTestingAppDeployParams({ sender: testAccount, metadata: metadata, }) const result1 = await algorand.appDeployer.deploy(deployment1) - await waitForIndexer() + if (result1.operationPerformed !== 'nothing') { + await waitForIndexerTransaction(getTransactionId(result1.transaction)) + } logging.testLogger.clear() const deployment2 = await getTestingAppDeployParams({ @@ -226,14 +232,16 @@ describe('deploy-app', () => { }) test('Deploy failure for updated app fails if onupdate = Fail', async () => { - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const metadata = getMetadata() const deployment1 = await getTestingAppDeployParams({ sender: testAccount, metadata: metadata, }) const result1 = await algorand.appDeployer.deploy(deployment1) - await waitForIndexer() + if (result1.operationPerformed !== 'nothing') { + await waitForIndexerTransaction(getTransactionId(result1.transaction)) + } logging.testLogger.clear() const deployment2 = await getTestingAppDeployParams({ @@ -257,14 +265,16 @@ describe('deploy-app', () => { }) test('Deploy replacement to deletable, updated app', async () => { - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const metadata = getMetadata({ deletable: true }) const deployment1 = await getTestingAppDeployParams({ sender: testAccount, metadata: metadata, }) const result1 = await algorand.appDeployer.deploy(deployment1) - await waitForIndexer() + if (result1.operationPerformed !== 'nothing') { + await waitForIndexerTransaction(getTransactionId(result1.transaction)) + } logging.testLogger.clear() const deployment2 = await getTestingAppDeployParams({ @@ -301,7 +311,7 @@ describe('deploy-app', () => { test('Deploy failure for replacement of permanent, updated app', async () => { Config.configure({ debug: false }) // Remove noise from snapshot - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const metadata = getMetadata({ deletable: false }) const deployment1 = (await getTestingAppDeployParams({ sender: testAccount, @@ -309,7 +319,9 @@ describe('deploy-app', () => { })) as AppDeployParams const result1 = await algorand.appDeployer.deploy(deployment1) - await waitForIndexer() + if (result1.operationPerformed !== 'nothing') { + await waitForIndexerTransaction(getTransactionId(result1.transaction)) + } logging.testLogger.clear() const deployment2 = (await getTestingAppDeployParams({ @@ -339,14 +351,16 @@ describe('deploy-app', () => { }) test('Deploy replacement of deletable schema broken app', async () => { - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const metadata = getMetadata({ deletable: true }) const deployment1 = await getTestingAppDeployParams({ sender: testAccount, metadata: metadata, }) const result1 = await algorand.appDeployer.deploy(deployment1) - await waitForIndexer() + if (result1.operationPerformed !== 'nothing') { + await waitForIndexerTransaction(getTransactionId(result1.transaction)) + } logging.testLogger.clear() const deployment2 = await getTestingAppDeployParams({ @@ -383,7 +397,7 @@ describe('deploy-app', () => { test('Deploy replacement to schema broken, permanent app fails', async () => { Config.configure({ debug: false }) // Remove noise from snapshot - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const metadata = getMetadata({ deletable: false }) const deployment1 = (await getTestingAppDeployParams({ sender: testAccount, @@ -391,7 +405,9 @@ describe('deploy-app', () => { })) as AppDeployParams const result1 = await algorand.appDeployer.deploy(deployment1) - await waitForIndexer() + if (result1.operationPerformed !== 'nothing') { + await waitForIndexerTransaction(getTransactionId(result1.transaction)) + } logging.testLogger.clear() const deployment2 = (await getTestingAppDeployParams({ @@ -421,14 +437,16 @@ describe('deploy-app', () => { }) test('Deploy failure for replacement of schema broken app fails if onSchemaBreak = Fail', async () => { - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const metadata = getMetadata() const deployment1 = await getTestingAppDeployParams({ sender: testAccount, metadata: metadata, }) const result1 = await algorand.appDeployer.deploy(deployment1) - await waitForIndexer() + if (result1.operationPerformed !== 'nothing') { + await waitForIndexerTransaction(getTransactionId(result1.transaction)) + } logging.testLogger.clear() const deployment2 = await getTestingAppDeployParams({ @@ -454,13 +472,15 @@ describe('deploy-app', () => { }) test('Do nothing if deploying app with no changes', async () => { - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const deployment = await getTestingAppDeployParams({ sender: testAccount, metadata: getMetadata(), }) const initialDeployment = await algorand.appDeployer.deploy(deployment) - await waitForIndexer() + if (initialDeployment.operationPerformed !== 'nothing') { + await waitForIndexerTransaction(getTransactionId(initialDeployment.transaction)) + } logging.testLogger.clear() const result = await algorand.appDeployer.deploy(deployment) @@ -487,14 +507,16 @@ describe('deploy-app', () => { }) test('Deploy append for schema broken app if onSchemaBreak = AppendApp', async () => { - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const metadata = getMetadata() const deployment1 = await getTestingAppDeployParams({ sender: testAccount, metadata: metadata, }) const result1 = await algorand.appDeployer.deploy(deployment1) - await waitForIndexer() + if (result1.operationPerformed !== 'nothing') { + await waitForIndexerTransaction(getTransactionId(result1.transaction)) + } logging.testLogger.clear() const deployment2 = await getTestingAppDeployParams({ @@ -527,14 +549,16 @@ describe('deploy-app', () => { }) test('Deploy append for update app if onUpdate = AppendApp', async () => { - const { algorand, testAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, waitForIndexerTransaction } = localnet.context const metadata = getMetadata() const deployment1 = await getTestingAppDeployParams({ sender: testAccount, metadata: metadata, }) const result1 = await algorand.appDeployer.deploy(deployment1) - await waitForIndexer() + if (result1.operationPerformed !== 'nothing') { + await waitForIndexerTransaction(getTransactionId(result1.transaction)) + } logging.testLogger.clear() const deployment2 = await getTestingAppDeployParams({ diff --git a/src/app-deploy.ts b/src/app-deploy.ts index 36d3e8a6..d3bc4046 100644 --- a/src/app-deploy.ts +++ b/src/app-deploy.ts @@ -1,4 +1,7 @@ -import algosdk, { Address } from 'algosdk' +import { AlgodClient, ApplicationStateSchema } from '@algorandfoundation/algokit-algod-client' +import { OnApplicationComplete } from '@algorandfoundation/algokit-transact' +import * as algosdk from '@algorandfoundation/sdk' +import { Address, Indexer } from '@algorandfoundation/sdk' import { compileTeal, getAppOnCompleteAction } from './app' import { _getAppArgsForABICall, _getBoxReference } from './transaction/legacy-bridge' import { getSenderAddress, getSenderTransactionSigner } from './transaction/transaction' @@ -27,9 +30,6 @@ import { TransactionComposer, } from './types/composer' import { Arc2TransactionNote, ConfirmedTransactionResult, ConfirmedTransactionResults, SendTransactionFrom } from './types/transaction' -import Algodv2 = algosdk.Algodv2 -import Indexer = algosdk.Indexer -import modelsv2 = algosdk.modelsv2 /** * @deprecated Use `algorand.appDeployer.deploy` instead. @@ -50,7 +50,7 @@ import modelsv2 = algosdk.modelsv2 */ export async function deployApp( deployment: AppDeploymentParams, - algod: Algodv2, + algod: AlgodClient, indexer?: Indexer, ): Promise< Partial & @@ -72,7 +72,7 @@ export async function deployApp( algod, getSigner: () => getSenderTransactionSigner(deployment.from), getSuggestedParams: async () => - deployment.transactionParams ? { ...deployment.transactionParams } : await algod.getTransactionParams().do(), + deployment.transactionParams ? { ...deployment.transactionParams } : await algod.transactionParams(), appManager, }) const deployer = new AppDeployer( @@ -90,15 +90,15 @@ export async function deployApp( assetReferences: deployment.createArgs?.assets?.map((a) => BigInt(a)), boxReferences: deployment.createArgs?.boxes ?.map(_getBoxReference) - ?.map((r) => ({ appId: BigInt(r.appIndex), name: r.name }) satisfies BoxReference), + ?.map((r) => ({ appId: BigInt(r.appId), name: r.name }) satisfies BoxReference), lease: deployment.createArgs?.lease, rekeyTo: deployment.createArgs?.rekeyTo ? getSenderAddress(deployment.createArgs?.rekeyTo) : undefined, staticFee: deployment.fee, maxFee: deployment.maxFee, extraProgramPages: deployment.schema.extraPages, onComplete: getAppOnCompleteAction(deployment.createOnCompleteAction) as Exclude< - algosdk.OnApplicationComplete, - algosdk.OnApplicationComplete.ClearStateOC + OnApplicationComplete, + OnApplicationComplete.ClearState >, schema: deployment.schema, } satisfies Partial @@ -112,12 +112,12 @@ export async function deployApp( assetReferences: deployment.updateArgs?.assets?.map((a) => BigInt(a)), boxReferences: deployment.updateArgs?.boxes ?.map(_getBoxReference) - ?.map((r) => ({ appId: BigInt(r.appIndex), name: r.name }) satisfies BoxReference), + ?.map((r) => ({ appId: BigInt(r.appId), name: r.name }) satisfies BoxReference), lease: deployment.updateArgs?.lease, rekeyTo: deployment.updateArgs?.rekeyTo ? getSenderAddress(deployment.updateArgs?.rekeyTo) : undefined, staticFee: deployment.fee, maxFee: deployment.maxFee, - onComplete: algosdk.OnApplicationComplete.UpdateApplicationOC, + onComplete: OnApplicationComplete.UpdateApplication, } satisfies Partial const deleteParams = { @@ -127,12 +127,12 @@ export async function deployApp( assetReferences: deployment.deleteArgs?.assets?.map((a) => BigInt(a)), boxReferences: deployment.deleteArgs?.boxes ?.map(_getBoxReference) - ?.map((r) => ({ appId: BigInt(r.appIndex), name: r.name }) satisfies BoxReference), + ?.map((r) => ({ appId: BigInt(r.appId), name: r.name }) satisfies BoxReference), lease: deployment.deleteArgs?.lease, rekeyTo: deployment.deleteArgs?.rekeyTo ? getSenderAddress(deployment.deleteArgs?.rekeyTo) : undefined, staticFee: deployment.fee, maxFee: deployment.maxFee, - onComplete: algosdk.OnApplicationComplete.DeleteApplicationOC, + onComplete: OnApplicationComplete.DeleteApplication, } satisfies Partial const encoder = new TextEncoder() @@ -226,7 +226,7 @@ export async function deployApp( * @param after The new schema * @returns Whether or not there is a breaking change */ -export function isSchemaIsBroken(before: modelsv2.ApplicationStateSchema, after: modelsv2.ApplicationStateSchema) { +export function isSchemaIsBroken(before: ApplicationStateSchema, after: ApplicationStateSchema) { return before.numByteSlice < after.numByteSlice || before.numUint < after.numUint } @@ -325,7 +325,7 @@ export function performTemplateSubstitution(tealCode: string, templateParams?: T */ export async function performTemplateSubstitutionAndCompile( tealCode: string, - algod: Algodv2, + algod: AlgodClient, templateParams?: TealTemplateParams, deploymentMetadata?: AppDeployMetadata, ): Promise { diff --git a/src/app.spec.ts b/src/app.spec.ts index b56f4992..98448d95 100644 --- a/src/app.spec.ts +++ b/src/app.spec.ts @@ -1,4 +1,4 @@ -import algosdk from 'algosdk' +import * as algosdk from '@algorandfoundation/sdk' import { afterEach, beforeEach, describe, expect, test } from 'vitest' import { getTestingAppContract } from '../tests/example-contracts/testing-app/contract' import { algoKitLogCaptureFixture, algorandFixture } from './testing' @@ -24,7 +24,7 @@ describe('app', () => { expect(app.appId).toBeGreaterThan(0) expect(app.appAddress).toEqual(algosdk.getApplicationAddress(app.appId)) expect(app.confirmation).toBeTruthy() - expect(BigInt(app.confirmation?.applicationIndex ?? 0)).toBe(app.appId) + expect(BigInt(app.confirmation?.appId ?? 0)).toBe(app.appId) }) test('appCreate with rekey performs rekey', async () => { diff --git a/src/app.ts b/src/app.ts index bacca6c5..6abe1b26 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,4 +1,7 @@ -import algosdk from 'algosdk' +import { AlgodClient, EvalDelta, PendingTransactionResponse, TealValue } from '@algorandfoundation/algokit-algod-client' +import { OnApplicationComplete, BoxReference as TransactBoxReference } from '@algorandfoundation/algokit-transact' +import * as algosdk from '@algorandfoundation/sdk' +import { ABIMethod, ABIMethodParams, ABIValue, Address } from '@algorandfoundation/sdk' import { _getAppArgsForABICall, _getBoxReference, legacySendAppTransactionBridge } from './transaction/legacy-bridge' import { encodeLease, getSenderAddress } from './transaction/transaction' import { @@ -24,13 +27,6 @@ import { import { AppManager } from './types/app-manager' import { SendTransactionFrom } from './types/transaction' import { toNumber } from './util' -import ABIMethod = algosdk.ABIMethod -import ABIMethodParams = algosdk.ABIMethodParams -import ABIValue = algosdk.ABIValue -import Address = algosdk.Address -import Algodv2 = algosdk.Algodv2 -import modelsv2 = algosdk.modelsv2 -import OnApplicationComplete = algosdk.OnApplicationComplete /** * @deprecated Use `algorand.send.appCreate()` / `algorand.createTransaction.appCreate()` / `algorand.send.appCreateMethodCall()` @@ -43,10 +39,10 @@ import OnApplicationComplete = algosdk.OnApplicationComplete */ export async function createApp( create: CreateAppParams, - algod: Algodv2, + algod: AlgodClient, ): Promise & AppCallTransactionResult & AppReference> { const onComplete = getAppOnCompleteAction(create.onCompleteAction) - if (onComplete === algosdk.OnApplicationComplete.ClearStateOC) { + if (onComplete === OnApplicationComplete.ClearState) { throw new Error('Cannot create an app with on-complete action of ClearState') } @@ -103,7 +99,7 @@ export async function createApp( */ export async function updateApp( update: UpdateAppParams, - algod: Algodv2, + algod: AlgodClient, ): Promise & AppCallTransactionResult> { return update.args?.method ? await legacySendAppTransactionBridge( @@ -114,7 +110,7 @@ export async function updateApp( { appId: BigInt(update.appId), sender: getSenderAddress(update.from), - onComplete: algosdk.OnApplicationComplete.UpdateApplicationOC, + onComplete: OnApplicationComplete.UpdateApplication, approvalProgram: update.approvalProgram, clearStateProgram: update.clearStateProgram, method: update.args.method instanceof ABIMethod ? update.args.method : new ABIMethod(update.args.method), @@ -130,7 +126,7 @@ export async function updateApp( { appId: BigInt(update.appId), sender: getSenderAddress(update.from), - onComplete: algosdk.OnApplicationComplete.UpdateApplicationOC, + onComplete: OnApplicationComplete.UpdateApplication, approvalProgram: update.approvalProgram, clearStateProgram: update.clearStateProgram, }, @@ -140,38 +136,38 @@ export async function updateApp( } /** - * @deprecated Use `algosdk.OnApplicationComplete` directly instead. + * @deprecated Use `OnApplicationComplete` directly instead. * - * Returns a `algosdk.OnApplicationComplete` for the given onCompleteAction. + * Returns a `OnApplicationComplete` for the given onCompleteAction. * - * If given `undefined` will return `OnApplicationComplete.NoOpOC`. + * If given `undefined` will return `OnApplicationComplete.NoOp`. * - * If given an `AppCallType` will convert the string enum to the correct underlying `algosdk.OnApplicationComplete`. + * If given an `AppCallType` will convert the string enum to the correct underlying `OnApplicationComplete`. * * @param onCompletionAction The on completion action - * @returns The `algosdk.OnApplicationComplete` + * @returns The `OnApplicationComplete` */ export function getAppOnCompleteAction(onCompletionAction?: AppCallType | OnApplicationComplete) { switch (onCompletionAction) { case undefined: case 'no_op': - case OnApplicationComplete.NoOpOC: - return OnApplicationComplete.NoOpOC + case OnApplicationComplete.NoOp: + return OnApplicationComplete.NoOp case 'opt_in': - case OnApplicationComplete.OptInOC: - return OnApplicationComplete.OptInOC + case OnApplicationComplete.OptIn: + return OnApplicationComplete.OptIn case 'close_out': - case OnApplicationComplete.CloseOutOC: - return OnApplicationComplete.CloseOutOC + case OnApplicationComplete.CloseOut: + return OnApplicationComplete.CloseOut case 'clear_state': - case OnApplicationComplete.ClearStateOC: - return OnApplicationComplete.ClearStateOC + case OnApplicationComplete.ClearState: + return OnApplicationComplete.ClearState case 'update_application': - case OnApplicationComplete.UpdateApplicationOC: - return OnApplicationComplete.UpdateApplicationOC + case OnApplicationComplete.UpdateApplication: + return OnApplicationComplete.UpdateApplication case 'delete_application': - case OnApplicationComplete.DeleteApplicationOC: - return OnApplicationComplete.DeleteApplicationOC + case OnApplicationComplete.DeleteApplication: + return OnApplicationComplete.DeleteApplication } } @@ -184,12 +180,12 @@ export function getAppOnCompleteAction(onCompletionAction?: AppCallType | OnAppl * @param algod An algod client * @returns The result of the call */ -export async function callApp(call: AppCallParams, algod: Algodv2): Promise { +export async function callApp(call: AppCallParams, algod: AlgodClient): Promise { const onComplete = getAppOnCompleteAction(call.callType) - if (onComplete === algosdk.OnApplicationComplete.UpdateApplicationOC) { + if (onComplete === OnApplicationComplete.UpdateApplication) { throw new Error('Cannot execute an app call with on-complete action of Update') } - if (call.args?.method && onComplete === algosdk.OnApplicationComplete.ClearStateOC) { + if (call.args?.method && onComplete === OnApplicationComplete.ClearState) { throw new Error('Cannot execute an ABI method call with on-complete action of ClearState') } @@ -232,7 +228,7 @@ export async function callApp(call: AppCallParams, algod: Algodv2): Promise { +export async function getAppBoxNames(appId: number | bigint, algod: AlgodClient): Promise { return new AppManager(algod).getBoxNames(BigInt(appId)) } @@ -285,7 +281,11 @@ export async function getAppBoxNames(appId: number | bigint, algod: Algodv2): Pr * @param algod An algod client instance * @returns The current box value as a byte array */ -export async function getAppBoxValue(appId: number | bigint, boxName: string | Uint8Array | BoxName, algod: Algodv2): Promise { +export async function getAppBoxValue( + appId: number | bigint, + boxName: string | Uint8Array | BoxName, + algod: AlgodClient, +): Promise { return new AppManager(algod).getBoxValue(BigInt(appId), typeof boxName !== 'string' && 'name' in boxName ? boxName.nameRaw : boxName) } @@ -297,7 +297,11 @@ export async function getAppBoxValue(appId: number | bigint, boxName: string | U * @param algod An algod client instance * @returns The current box values as a byte array in the same order as the passed in box names */ -export async function getAppBoxValues(appId: number, boxNames: (string | Uint8Array | BoxName)[], algod: Algodv2): Promise { +export async function getAppBoxValues( + appId: number, + boxNames: (string | Uint8Array | BoxName)[], + algod: AlgodClient, +): Promise { return new AppManager(algod).getBoxValues( BigInt(appId), boxNames.map((b) => (typeof b !== 'string' && 'name' in b ? b.nameRaw : b)), @@ -311,7 +315,7 @@ export async function getAppBoxValues(appId: number, boxNames: (string | Uint8Ar * @param algod An algod client instance * @returns The current box value as an ABI value */ -export async function getAppBoxValueFromABIType(request: BoxValueRequestParams, algod: Algodv2): Promise { +export async function getAppBoxValueFromABIType(request: BoxValueRequestParams, algod: AlgodClient): Promise { return new AppManager(algod).getBoxValueFromABIType({ appId: BigInt(request.appId), boxName: typeof request.boxName !== 'string' && 'name' in request.boxName ? request.boxName.nameRaw : request.boxName, @@ -326,7 +330,7 @@ export async function getAppBoxValueFromABIType(request: BoxValueRequestParams, * @param algod An algod client instance * @returns The current box values as an ABI value in the same order as the passed in box names */ -export async function getAppBoxValuesFromABIType(request: BoxValuesRequestParams, algod: Algodv2): Promise { +export async function getAppBoxValuesFromABIType(request: BoxValuesRequestParams, algod: AlgodClient): Promise { return new AppManager(algod).getBoxValuesFromABIType({ appId: BigInt(request.appId), boxNames: request.boxNames.map((b) => (typeof b !== 'string' && 'name' in b ? b.nameRaw : b)), @@ -342,7 +346,7 @@ export async function getAppBoxValuesFromABIType(request: BoxValuesRequestParams * @param state A `global-state`, `local-state`, `global-state-deltas` or `local-state-deltas` * @returns An object keyeed by the UTF-8 representation of the key with various parsings of the values */ -export function decodeAppState(state: { key: string; value: modelsv2.TealValue | modelsv2.EvalDelta }[]): AppState { +export function decodeAppState(state: { key: string; value: TealValue | EvalDelta }[]): AppState { return AppManager.decodeAppState(state.map(({ key, value }) => ({ key: Buffer.from(key, 'utf-8'), value }))) } @@ -386,7 +390,7 @@ export async function getAppArgsForABICall(args: ABIAppCallArgs, from: SendTrans * @param box The box to return a reference for * @returns The box reference ready to pass into a `Transaction` */ -export function getBoxReference(box: BoxIdentifier | BoxReference | algosdk.BoxReference): algosdk.BoxReference { +export function getBoxReference(box: BoxIdentifier | BoxReference | TransactBoxReference): TransactBoxReference { return _getBoxReference(box) } @@ -403,8 +407,8 @@ function _getAccountAddress(account: string | Address) { * @param algod An algod client * @returns The data about the app */ -export async function getAppById(appId: number | bigint, algod: Algodv2) { - return await algod.getApplicationByID(toNumber(appId)).do() +export async function getAppById(appId: number | bigint, algod: AlgodClient) { + return await algod.getApplicationById(toNumber(appId)) } /** @@ -416,7 +420,7 @@ export async function getAppById(appId: number | bigint, algod: Algodv2) { * @param tealCode The TEAL code * @returns The information about the compiled file */ -export async function compileTeal(tealCode: string, algod: Algodv2): Promise { +export async function compileTeal(tealCode: string, algod: AlgodClient): Promise { return await new AppManager(algod).compileTeal(tealCode) } diff --git a/src/asset.ts b/src/asset.ts index 0e18e2e2..8d12f464 100644 --- a/src/asset.ts +++ b/src/asset.ts @@ -1,11 +1,10 @@ -import algosdk from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' import { encodeTransactionNote, getSenderAddress } from './transaction' import { legacySendTransactionBridge } from './transaction/legacy-bridge' import { AlgorandClient } from './types/algorand-client' import { AssetBulkOptInOutParams, AssetOptInParams, AssetOptOutParams, CreateAssetParams } from './types/asset' import { AssetCreateParams, AssetOptInParams as NewAssetOptInParams, AssetOptOutParams as NewAssetOptOutParams } from './types/composer' import { SendTransactionResult } from './types/transaction' -import Algodv2 = algosdk.Algodv2 /** * @deprecated use `algorand.send.assetCreate()` / `algorand.createTransaction.assetCreate()` instead @@ -22,8 +21,8 @@ import Algodv2 = algosdk.Algodv2 */ export async function createAsset( create: CreateAssetParams, - algod: Algodv2, -): Promise { + algod: AlgodClient, +): Promise { const params: AssetCreateParams = { sender: getSenderAddress(create.creator), total: BigInt(create.total), @@ -48,7 +47,7 @@ export async function createAsset( params, (client) => client.assetCreate, (client) => client.assetCreate, - )) as SendTransactionResult & { confirmation: { assetIndex: number | bigint } } + )) as SendTransactionResult & { confirmation: { assetId: number | bigint } } } /** @@ -64,7 +63,7 @@ export async function createAsset( * await algokit.assetOptIn({ account, assetId }, algod) * ``` */ -export async function assetOptIn(optIn: AssetOptInParams, algod: Algodv2): Promise { +export async function assetOptIn(optIn: AssetOptInParams, algod: AlgodClient): Promise { const params: NewAssetOptInParams = { assetId: BigInt(optIn.assetId), sender: getSenderAddress(optIn.account), @@ -95,8 +94,8 @@ export async function assetOptIn(optIn: AssetOptInParams, algod: Algodv2): Promi * await algokit.assetOptOut({ account, assetId, assetCreatorAddress }, algod) * ``` */ -export async function assetOptOut(optOut: AssetOptOutParams, algod: Algodv2): Promise { - const assetCreatorAddress = optOut.assetCreatorAddress ?? (await algod.getAssetByID(optOut.assetId).do()).params.creator +export async function assetOptOut(optOut: AssetOptOutParams, algod: AlgodClient): Promise { + const assetCreatorAddress = optOut.assetCreatorAddress ?? (await algod.getAssetById(optOut.assetId)).params.creator const params: NewAssetOptOutParams = { assetId: BigInt(optOut.assetId), @@ -122,12 +121,12 @@ export async function assetOptOut(optOut: AssetOptOutParams, algod: Algodv2): Pr * Opt in to a list of assets on the Algorand blockchain. * * @param optIn - The bulk opt-in request. - * @param algod - An instance of the Algodv2 class from the `algosdk` library. + * @param algod - An instance of the AlgodClient class. * @returns A record object where the keys are the asset IDs and the values are the corresponding transaction IDs for successful opt-ins. * @throws If there is an error during the opt-in process. * @example algokit.bulkOptIn({ account: account, assetIds: [12345, 67890] }, algod) */ -export async function assetBulkOptIn(optIn: AssetBulkOptInOutParams, algod: Algodv2): Promise> { +export async function assetBulkOptIn(optIn: AssetBulkOptInOutParams, algod: AlgodClient): Promise> { const result = await AlgorandClient.fromClients({ algod }) .setSignerFromAccount(optIn.account) .asset.bulkOptIn(getSenderAddress(optIn.account), optIn.assetIds.map(BigInt), { @@ -149,12 +148,12 @@ export async function assetBulkOptIn(optIn: AssetBulkOptInOutParams, algod: Algo * Opt out of multiple assets in Algorand blockchain. * * @param optOut The bulk opt-out request. - * @param algod - An instance of the Algodv2 client used to interact with the Algorand blockchain. + * @param algod - An instance of the AlgodClient used to interact with the Algorand blockchain. * @returns A record object containing asset IDs as keys and their corresponding transaction IDs as values. * @throws If there is an error during the opt-out process. * @example algokit.bulkOptOut({ account: account, assetIds: [12345, 67890] }, algod) */ -export async function assetBulkOptOut(optOut: AssetBulkOptInOutParams, algod: Algodv2): Promise> { +export async function assetBulkOptOut(optOut: AssetBulkOptInOutParams, algod: AlgodClient): Promise> { const result = await AlgorandClient.fromClients({ algod }) .setSignerFromAccount(optOut.account) .asset.bulkOptOut(getSenderAddress(optOut.account), optOut.assetIds.map(BigInt), { diff --git a/src/indexer-lookup.spec.ts b/src/indexer-lookup.spec.ts index 19479e24..6f450543 100644 --- a/src/indexer-lookup.spec.ts +++ b/src/indexer-lookup.spec.ts @@ -1,4 +1,5 @@ -import { Address } from 'algosdk' +import { getTransactionId } from '@algorandfoundation/algokit-transact' +import { Address } from '@algorandfoundation/sdk' import { beforeEach, describe, expect, test } from 'vitest' import { getTestingAppContract } from '../tests/example-contracts/testing-app/contract' import * as indexer from './indexer-lookup' @@ -18,9 +19,9 @@ describe('indexer-lookup', () => { } test('Transaction is found by id', async () => { - const { algorand, waitForIndexer } = localnet.context + const { algorand, waitForIndexerTransaction } = localnet.context const { transaction } = await sendTestTransaction() - await waitForIndexer() + await waitForIndexerTransaction(transaction.txID()) const txn = await algorand.client.indexer.lookupTransactionByID(transaction.txID()).do() @@ -38,7 +39,7 @@ describe('indexer-lookup', () => { }, 20_000) test('Transactions are searched with pagination', async () => { - const { algorand, testAccount, generateAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, generateAccount, waitForIndexerTransaction } = localnet.context const secondAccount = await generateAccount({ initialFunds: (1).algo(), suppressLog: true, @@ -46,7 +47,7 @@ describe('indexer-lookup', () => { const { transaction: transaction1 } = await sendTestTransaction((1).microAlgo()) const { transaction: transaction2 } = await sendTestTransaction((2).microAlgo()) await sendTestTransaction((1).microAlgo(), secondAccount) - await waitForIndexer() + await waitForIndexerTransaction(getTransactionId(transaction2)) const transactions = await indexer.searchTransactions( algorand.client.indexer, @@ -55,11 +56,13 @@ describe('indexer-lookup', () => { ) expect(transactions.currentRound).toBeGreaterThan(0n) - expect(transactions.transactions.map((t) => t.id).sort()).toEqual([transaction1.txID(), transaction2.txID()].sort()) + expect(transactions.transactions.map((t) => t.id).sort()).toEqual( + [getTransactionId(transaction1), getTransactionId(transaction2)].sort(), + ) }, 20_000) test('Application create transactions are found by creator with pagination', async () => { - const { algorand, testAccount, generateAccount, waitForIndexer } = localnet.context + const { algorand, testAccount, generateAccount, waitForIndexerTransaction } = localnet.context const secondAccount = await generateAccount({ initialFunds: (1).algo(), suppressLog: true, @@ -76,7 +79,7 @@ describe('indexer-lookup', () => { const { result: app1 } = await factory.send.bare.create() const { result: app2 } = await factory.send.bare.create({ deployTimeParams: { VALUE: 2 } }) await factory.send.bare.create({ sender: secondAccount }) - await waitForIndexer() + await waitForIndexerTransaction(getTransactionId(app2.transaction)) const apps = await indexer.lookupAccountCreatedApplicationByAddress(algorand.client.indexer, testAccount, true, 1) diff --git a/src/indexer-lookup.ts b/src/indexer-lookup.ts index 8a9d259f..84f43b8b 100644 --- a/src/indexer-lookup.ts +++ b/src/indexer-lookup.ts @@ -1,6 +1,6 @@ -import algosdk, { Address } from 'algosdk' +import * as algosdk from '@algorandfoundation/sdk' +import { Address, Indexer } from '@algorandfoundation/sdk' import { LookupAssetHoldingsOptions } from './types/indexer' -import Indexer = algosdk.Indexer export type SearchForTransactions = ReturnType const DEFAULT_INDEXER_MAX_API_RESOURCES_PER_ACCOUNT = 1000 //MaxAPIResourcesPerAccount: This is the default maximum, though may be provider specific diff --git a/src/localnet/get-kmd-wallet-account.ts b/src/localnet/get-kmd-wallet-account.ts index 97840f28..6c0d766a 100644 --- a/src/localnet/get-kmd-wallet-account.ts +++ b/src/localnet/get-kmd-wallet-account.ts @@ -1,9 +1,7 @@ -import algosdk from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' +import { type Account, Kmd } from '@algorandfoundation/sdk' import { ClientManager } from '../types/client-manager' import { KmdAccountManager } from '../types/kmd-account-manager' -import Account = algosdk.Account -import Algodv2 = algosdk.Algodv2 -import Kmd = algosdk.Kmd /** * @deprecated use `algorand.account.kmd.getWalletAccount(name, predicate)` or `new KMDAccountManager(clientManager).getWalletAccount(name, predicate)` instead. @@ -30,7 +28,7 @@ export async function getKmdWalletAccount( // eslint-disable-next-line @typescript-eslint/no-explicit-any predicate?: (account: Record) => boolean }, - algod: Algodv2, + algod: AlgodClient, kmdClient?: Kmd, ): Promise { return ( diff --git a/src/localnet/get-localnet-dispenser-account.ts b/src/localnet/get-localnet-dispenser-account.ts index b6f28223..1aff7f68 100644 --- a/src/localnet/get-localnet-dispenser-account.ts +++ b/src/localnet/get-localnet-dispenser-account.ts @@ -1,9 +1,8 @@ -import algosdk from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' +import { type Account, Kmd } from '@algorandfoundation/sdk' import { AccountManager } from '../types/account-manager' import { ClientManager } from '../types/client-manager' -import Account = algosdk.Account -import Algodv2 = algosdk.Algodv2 -import Kmd = algosdk.Kmd + /** * @deprecated Use `algorand.account.kmd.getLocalNetDispenserAccount()` instead. * @@ -12,6 +11,6 @@ import Kmd = algosdk.Kmd * @param algod An algod client * @param kmd A KMD client, if not specified then a default KMD client will be loaded from environment variables */ -export async function getLocalNetDispenserAccount(algod: Algodv2, kmd?: Kmd): Promise { +export async function getLocalNetDispenserAccount(algod: AlgodClient, kmd?: Kmd): Promise { return (await new AccountManager(new ClientManager({ algod, kmd })).kmd.getLocalNetDispenserAccount()).account } diff --git a/src/localnet/get-or-create-kmd-wallet-account.ts b/src/localnet/get-or-create-kmd-wallet-account.ts index 13638a8c..55cda426 100644 --- a/src/localnet/get-or-create-kmd-wallet-account.ts +++ b/src/localnet/get-or-create-kmd-wallet-account.ts @@ -1,10 +1,8 @@ -import algosdk from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' +import { type Account, Kmd } from '@algorandfoundation/sdk' import { AlgoAmount } from '../types/amount' import { ClientManager } from '../types/client-manager' import { KmdAccountManager } from '../types/kmd-account-manager' -import Account = algosdk.Account -import Algodv2 = algosdk.Algodv2 -import Kmd = algosdk.Kmd /** * @deprecated use `algorand.account.kmd.getOrCreateWalletAccount(name, fundWith)` or `new KMDAccountManager(clientManager).getOrCreateWalletAccount(name, fundWith)` instead. @@ -27,7 +25,7 @@ import Kmd = algosdk.Kmd */ export async function getOrCreateKmdWalletAccount( walletAccount: { name: string; fundWith?: AlgoAmount }, - algod: Algodv2, + algod: AlgodClient, kmdClient?: Kmd, ): Promise { return ( diff --git a/src/localnet/is-localnet.ts b/src/localnet/is-localnet.ts index d5f475cd..8346d4ed 100644 --- a/src/localnet/is-localnet.ts +++ b/src/localnet/is-localnet.ts @@ -1,11 +1,10 @@ -import algosdk from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' import { ClientManager } from '../types/client-manager' -import Algodv2 = algosdk.Algodv2 /** @deprecated Use `await algorand.client.isLocalNet()` or `await new ClientManager({ algod }).isLocalNet()` instead. * * Returns true if the algod client is pointing to a LocalNet Algorand network */ -export async function isLocalNet(algod: Algodv2): Promise { +export async function isLocalNet(algod: AlgodClient): Promise { return await new ClientManager({ algod }).isLocalNet() } diff --git a/src/network-client.ts b/src/network-client.ts index a98b66ba..e064d7ab 100644 --- a/src/network-client.ts +++ b/src/network-client.ts @@ -1,9 +1,7 @@ -import algosdk from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' +import { Indexer, Kmd } from '@algorandfoundation/sdk' import { ClientManager } from './types/client-manager' import { AlgoClientConfig, AlgoConfig } from './types/network-client' -import Algodv2 = algosdk.Algodv2 -import Indexer = algosdk.Indexer -import Kmd = algosdk.Kmd /** * @deprecated Use `ClientManager.getConfigFromEnvironmentOrLocalNet()` instead. @@ -85,7 +83,7 @@ export function getDefaultLocalNetConfig(configOrPort: 'algod' | 'indexer' | 'km * await algod.healthCheck().do() * ``` */ -export function getAlgoClient(config?: AlgoClientConfig): Algodv2 { +export function getAlgoClient(config?: AlgoClientConfig): AlgodClient { return config ? ClientManager.getAlgodClient(config) : ClientManager.getAlgodClientFromEnvironment() } @@ -146,11 +144,11 @@ export function getAlgoKmdClient(config?: AlgoClientConfig): Kmd { } /** @deprecated Use `await algorand.client.isTestNet()` or `await new ClientManager({ algod }).isTestNet()` instead. */ -export async function isTestNet(algod: Algodv2): Promise { +export async function isTestNet(algod: AlgodClient): Promise { return await new ClientManager({ algod }).isTestNet() } /** @deprecated Use `await algorand.client.isMainNet()` or `await new ClientManager({ algod }).isMainNet()` instead. */ -export async function isMainNet(algod: Algodv2): Promise { +export async function isMainNet(algod: AlgodClient): Promise { return await new ClientManager({ algod }).isMainNet() } diff --git a/src/testing/_asset.ts b/src/testing/_asset.ts index 2adbd371..ba9d352b 100644 --- a/src/testing/_asset.ts +++ b/src/testing/_asset.ts @@ -1,4 +1,4 @@ -import { Address } from 'algosdk' +import { Address } from '@algorandfoundation/sdk' import { AlgorandClient } from '../types/algorand-client' export async function generateTestAsset(algorand: AlgorandClient, sender: Address | string, total?: number) { diff --git a/src/testing/account.ts b/src/testing/account.ts index 39d48af1..cfa499b5 100644 --- a/src/testing/account.ts +++ b/src/testing/account.ts @@ -1,10 +1,10 @@ -import algosdk, { Address } from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' +import type { Account } from '@algorandfoundation/sdk' +import * as algosdk from '@algorandfoundation/sdk' +import { Address, Kmd } from '@algorandfoundation/sdk' import { AlgorandClient, Config } from '../' import { TransactionSignerAccount } from '../types/account' import { GetTestAccountParams } from '../types/testing' -import Account = algosdk.Account -import Algodv2 = algosdk.Algodv2 -import Kmd = algosdk.Kmd /** * @deprecated Use `getTestAccount(params, algorandClient)` instead. The `algorandClient` object can be created using `AlgorandClient.fromClients({ algod, kmd })`. @@ -20,7 +20,7 @@ import Kmd = algosdk.Kmd */ export async function getTestAccount( params: GetTestAccountParams, - algod: Algodv2, + algod: AlgodClient, kmd?: Kmd, ): Promise
/** @@ -38,7 +38,7 @@ export async function getTestAccount( ): Promise
export async function getTestAccount( { suppressLog, initialFunds, accountGetter }: GetTestAccountParams, - algodOrAlgorandClient: Algodv2 | AlgorandClient, + algodOrAlgorandClient: AlgodClient | AlgorandClient, kmd?: Kmd, ): Promise
{ const algorand = diff --git a/src/testing/fixtures/algorand-fixture.ts b/src/testing/fixtures/algorand-fixture.ts index 8149d832..3d4d785a 100644 --- a/src/testing/fixtures/algorand-fixture.ts +++ b/src/testing/fixtures/algorand-fixture.ts @@ -42,7 +42,7 @@ import { TransactionLogger } from '../transaction-logger' * @example With config * ```typescript * const fixture = algorandFixture({ - * algod: new Algodv2('localhost', 12345, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'), + * algod: new AlgodClient({ baseUrl: 'http://localhost:12345', headers: { 'X-Algo-API-Token': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' } }), * // ... * }) * @@ -89,9 +89,10 @@ export function algorandFixture(fixtureConfig?: AlgorandFixtureConfig, config?: const newScope = async () => { Config.configure({ debug: true }) const transactionLogger = new TransactionLogger() - const transactionLoggerAlgod = transactionLogger.capture(algod) + // TODO: implement the logic for wait for indexer + // const transactionLoggerAlgod = transactionLogger.capture(algod) - algorand = AlgorandClient.fromClients({ algod: transactionLoggerAlgod, indexer, kmd }).setSuggestedParamsCacheTimeout(0) + algorand = AlgorandClient.fromClients({ algod: algod, indexer, kmd }).setSuggestedParamsCacheTimeout(0) const testAccount = await getTestAccount({ initialFunds: fixtureConfig?.testAccountFunding ?? algos(10), suppressLog: true }, algorand) algorand.setSignerFromAccount(testAccount) @@ -103,7 +104,7 @@ export function algorandFixture(fixtureConfig?: AlgorandFixtureConfig, config?: } context = { algorand, - algod: transactionLoggerAlgod, + algod: algod, indexer: indexer, kmd: kmd, testAccount, diff --git a/src/testing/test-logger.ts b/src/testing/test-logger.ts index 9ec5578b..ccadf634 100644 --- a/src/testing/test-logger.ts +++ b/src/testing/test-logger.ts @@ -1,3 +1,4 @@ +import { getTransactionId } from '@algorandfoundation/algokit-transact' import { Logger } from '../types/logging' import { LogSnapshotConfig } from '../types/testing' import { asJson } from '../util' @@ -48,7 +49,7 @@ export class TestLogger implements Logger { const { transactions: transactionIds, accounts, apps } = config ?? {} let snapshot = this.capturedLogs.filter(config?.filterPredicate ?? (() => true)).join('\n') transactionIds?.forEach( - (txn, id) => (snapshot = snapshot.replace(new RegExp(typeof txn === 'string' ? txn : txn.txID(), 'g'), `TXID_${id + 1}`)), + (txn, id) => (snapshot = snapshot.replace(new RegExp(typeof txn === 'string' ? txn : getTransactionId(txn), 'g'), `TXID_${id + 1}`)), ) accounts?.forEach( (sender, id) => diff --git a/src/testing/transaction-logger.ts b/src/testing/transaction-logger.ts index d2a282ce..ea2df291 100644 --- a/src/testing/transaction-logger.ts +++ b/src/testing/transaction-logger.ts @@ -1,12 +1,12 @@ -import algosdk from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' +import { decodeSignedTransaction, getTransactionId } from '@algorandfoundation/algokit-transact' +import * as algosdk from '@algorandfoundation/sdk' +import { Indexer } from '@algorandfoundation/sdk' import { Config } from '../config' import { runWhenIndexerCaughtUp } from './indexer' -import Algodv2 = algosdk.Algodv2 -import decodeSignedTransaction = algosdk.decodeSignedTransaction -import Indexer = algosdk.Indexer /** - * Allows you to keep track of Algorand transaction IDs by wrapping an `Algodv2` in a proxy. + * Allows you to keep track of Algorand transaction IDs by wrapping an `AlgodClient` in a proxy. * Useful for automated tests. */ export class TransactionLogger { @@ -16,9 +16,9 @@ export class TransactionLogger { private _pushTxn(stxn: Uint8Array) { const decoded = decodeSignedTransaction(stxn) if (decoded.txn.lastValid > (this._latestLastValidRound ?? BigInt(0))) { - this._latestLastValidRound = BigInt(decoded.txn.lastValid) + this._latestLastValidRound = decoded.txn.lastValid } - this._sentTransactionIds.push(decoded.txn.txID()) + this._sentTransactionIds.push(getTransactionId(decoded.txn)) } /** @@ -46,13 +46,13 @@ export class TransactionLogger { } } - /** Return a proxy that wraps the given Algodv2 with this transaction logger. + /** Return a proxy that wraps the given AlgodClient with this transaction logger. * - * @param algod The `Algodv2` to wrap - * @returns The wrapped `Algodv2`, any transactions sent using this algod instance will be logged by this transaction logger + * @param algod The `AlgodClient` to wrap + * @returns The wrapped `AlgodClient`, any transactions sent using this algod instance will be logged by this transaction logger */ - capture(algod: Algodv2): Algodv2 { - return new Proxy(algod, new TransactionLoggingAlgodv2ProxyHandler(this)) + capture(algod: AlgodClient): AlgodClient { + return new Proxy(algod, new TransactionLoggingAlgodClientProxyHandler(this)) } /** Wait until all logged transactions IDs appear in the given `Indexer`. */ @@ -78,19 +78,32 @@ export class TransactionLogger { } } -class TransactionLoggingAlgodv2ProxyHandler implements ProxyHandler { +class TransactionLoggingAlgodClientProxyHandler implements ProxyHandler { private transactionLogger: TransactionLogger constructor(transactionLogger: TransactionLogger) { this.transactionLogger = transactionLogger } + // TODO: PD - restore this logic // eslint-disable-next-line @typescript-eslint/no-explicit-any - get(target: Algodv2, property: string | symbol, receiver: any) { - if (property === 'sendRawTransaction') { - return (stxOrStxs: Uint8Array | Uint8Array[]) => { + get(target: AlgodClient, property: string | symbol, receiver: any) { + if (property === 'rawTransaction') { + return ({ body: stxOrStxs }: { body: Uint8Array | Uint8Array[] }) => { this.transactionLogger.logRawTransaction(stxOrStxs) - return target[property].call(receiver, stxOrStxs) + + let forPosting = stxOrStxs + if (Array.isArray(stxOrStxs)) { + if (!stxOrStxs.every((a) => a instanceof Uint8Array)) { + throw new TypeError('Array elements must be byte arrays') + } + // Flatten into a single Uint8Array + forPosting = algosdk.concatArrays(...stxOrStxs) + } else if (!(forPosting instanceof Uint8Array)) { + throw new TypeError('Argument must be byte array') + } + + return target[property].call(receiver, { body: forPosting }) } } // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/src/transaction/legacy-bridge.ts b/src/transaction/legacy-bridge.ts index 87970336..eedc47e0 100644 --- a/src/transaction/legacy-bridge.ts +++ b/src/transaction/legacy-bridge.ts @@ -1,4 +1,7 @@ -import algosdk from 'algosdk' +import { AlgodClient, TransactionParams } from '@algorandfoundation/algokit-algod-client' +import { BoxReference as TransactBoxReference, Transaction } from '@algorandfoundation/algokit-transact' +import * as algosdk from '@algorandfoundation/sdk' +import { ABIMethod } from '@algorandfoundation/sdk' import { AlgorandClientTransactionCreator } from '../types/algorand-client-transaction-creator' import { AlgorandClientTransactionSender } from '../types/algorand-client-transaction-sender' import { ABIAppCallArgs, BoxIdentifier as LegacyBoxIdentifier, BoxReference as LegacyBoxReference, RawAppCallArgs } from '../types/app' @@ -24,15 +27,13 @@ import { SendTransactionParams, SendTransactionResult, TransactionNote, + TransactionWrapper, } from '../types/transaction' import { encodeLease, encodeTransactionNote, getSenderAddress, getSenderTransactionSigner } from './transaction' -import Algodv2 = algosdk.Algodv2 -import Transaction = algosdk.Transaction -import ABIMethod = algosdk.ABIMethod /** @deprecated Bridges between legacy `sendTransaction` behaviour and new `AlgorandClient` behaviour. */ export async function legacySendTransactionBridge( - algod: Algodv2, + algod: AlgodClient, from: SendTransactionFrom, sendParams: SendTransactionParams, params: T, @@ -40,14 +41,14 @@ export async function legacySendTransactionBridge (params: T) => Promise) | ((c: AlgorandClientTransactionCreator) => (params: T) => Promise), send: (c: AlgorandClientTransactionSender) => (params: T & SendParams) => Promise, - suggestedParams?: algosdk.SuggestedParams, -): Promise<(SendTransactionResult | TResult) & { transactions: Transaction[] }> { + suggestedParams?: TransactionParams, +): Promise<(SendTransactionResult | TResult) & { transactions: TransactionWrapper[] }> { const appManager = new AppManager(algod) const newGroup = () => new TransactionComposer({ algod, getSigner: () => getSenderTransactionSigner(from), - getSuggestedParams: async () => (suggestedParams ? { ...suggestedParams } : await algod.getTransactionParams().do()), + getSuggestedParams: async () => (suggestedParams ? { ...suggestedParams } : await algod.transactionParams()), appManager, }) const transactionSender = new AlgorandClientTransactionSender(newGroup, new AssetManager(algod, newGroup), appManager) @@ -78,7 +79,7 @@ export async function legacySendTransactionBridge sendParams.atc!['methodCalls'].set(i + baseIndex, m)) } } - return { transaction: txns.at(-1)!, transactions: txns } + return { transaction: new TransactionWrapper(txns.at(-1)!), transactions: txns.map((t) => new TransactionWrapper(t)) } } return { ...(await send(transactionSender)({ ...sendParams, ...params })) } @@ -97,7 +98,7 @@ export async function legacySendAppTransactionBridge< | AppCallMethodCall, TResult extends SendSingleTransactionResult, >( - algod: Algodv2, + algod: AlgodClient, from: SendTransactionFrom, appArgs: RawAppCallArgs | ABIAppCallArgs | undefined, sendParams: SendTransactionParams & { note?: TransactionNote }, @@ -106,8 +107,8 @@ export async function legacySendAppTransactionBridge< | ((c: AlgorandClientTransactionCreator) => (params: T) => Promise) | ((c: AlgorandClientTransactionCreator) => (params: T) => Promise), send: (c: AlgorandClientTransactionSender) => (params: T & SendParams) => Promise, - suggestedParams?: algosdk.SuggestedParams, -): Promise<(SendTransactionResult | TResult) & { transactions: Transaction[] }> { + suggestedParams?: TransactionParams, +): Promise<(SendTransactionResult | TResult) & { transactions: TransactionWrapper[] }> { const encoder = new TextEncoder() const paramsWithAppArgs = { @@ -115,7 +116,7 @@ export async function legacySendAppTransactionBridge< accountReferences: appArgs?.accounts?.map((a) => (typeof a === 'string' ? a : algosdk.encodeAddress(a.publicKey))), appReferences: appArgs?.apps?.map((a) => BigInt(a)), assetReferences: appArgs?.assets?.map((a) => BigInt(a)), - boxReferences: appArgs?.boxes?.map(_getBoxReference)?.map((r) => ({ appId: BigInt(r.appIndex), name: r.name }) satisfies BoxReference), + boxReferences: appArgs?.boxes?.map(_getBoxReference)?.map((r) => ({ appId: BigInt(r.appId), name: r.name }) satisfies BoxReference), lease: appArgs?.lease, rekeyTo: appArgs?.rekeyTo ? getSenderAddress(appArgs?.rekeyTo) : undefined, args: appArgs @@ -149,7 +150,7 @@ export async function _getAppArgsForABICall(args: ABIAppCallArgs, from: SendTran ? { txn: (await a).transaction, signer } : 'transaction' in a ? { txn: a.transaction, signer: 'signer' in a ? getSenderTransactionSigner(a.signer) : signer } - : 'txID' in a + : 'type' in a ? { txn: a, signer } : a }), @@ -173,21 +174,23 @@ function _getAccountAddress(account: string | algosdk.Address) { } /** @deprecated */ -export function _getBoxReference(box: LegacyBoxIdentifier | LegacyBoxReference | algosdk.BoxReference): algosdk.BoxReference { +export function _getBoxReference(box: LegacyBoxIdentifier | LegacyBoxReference | TransactBoxReference): TransactBoxReference { const encoder = new TextEncoder() - if (typeof box === 'object' && 'appIndex' in box) { - return box + const toBytes = (boxIdentifier: string | Uint8Array | SendTransactionFrom): Uint8Array => { + return typeof boxIdentifier === 'string' + ? encoder.encode(boxIdentifier) + : 'length' in boxIdentifier + ? boxIdentifier + : algosdk.decodeAddress(getSenderAddress(boxIdentifier)).publicKey } - const ref = typeof box === 'object' && 'appId' in box ? box : { appId: 0, name: box } - return { - appIndex: ref.appId, - name: - typeof ref.name === 'string' - ? encoder.encode(ref.name) - : 'length' in ref.name - ? ref.name - : algosdk.decodeAddress(getSenderAddress(ref.name)).publicKey, - } as algosdk.BoxReference + if (typeof box === 'object' && 'appId' in box) { + return { + appId: BigInt(box.appId), + name: toBytes(box.name), + } + } + + return { appId: 0n, name: toBytes(box) } } diff --git a/src/transaction/perform-atomic-transaction-composer-simulate.ts b/src/transaction/perform-atomic-transaction-composer-simulate.ts index 4c9a9cc6..d32a9032 100644 --- a/src/transaction/perform-atomic-transaction-composer-simulate.ts +++ b/src/transaction/perform-atomic-transaction-composer-simulate.ts @@ -1,7 +1,11 @@ -import algosdk, { SignedTransaction, decodeMsgpack } from 'algosdk' -import Algodv2 = algosdk.Algodv2 -import AtomicTransactionComposer = algosdk.AtomicTransactionComposer -import modelsv2 = algosdk.modelsv2 +import { + AlgodClient, + SimulateRequest, + SimulateRequestTransactionGroup, + SimulateTraceConfig, +} from '@algorandfoundation/algokit-algod-client' +import { EMPTY_SIGNATURE } from '@algorandfoundation/algokit-common' +import { AtomicTransactionComposer } from '@algorandfoundation/sdk' /** * Performs a simulation of the transactions loaded into the given AtomicTransactionComposer. @@ -13,30 +17,32 @@ import modelsv2 = algosdk.modelsv2 */ export async function performAtomicTransactionComposerSimulate( atc: AtomicTransactionComposer, - algod: Algodv2, - options?: Omit[0], 'txnGroups'>, + algod: AlgodClient, + options?: Omit, ) { - const unsignedTransactionsSigners = atc.buildGroup() - const decodedSignedTransactions = unsignedTransactionsSigners.map((ts) => algosdk.encodeUnsignedSimulateTransaction(ts.txn)) + const transactionsWithSigners = atc.buildGroup() - const simulateRequest = new modelsv2.SimulateRequest({ + const simulateRequest = { ...(options ?? { allowEmptySignatures: true, fixSigners: true, allowMoreLogging: true, - execTraceConfig: new modelsv2.SimulateTraceConfig({ + execTraceConfig: { enable: true, scratchChange: true, stackChange: true, stateChange: true, - }), + } satisfies SimulateTraceConfig, }), txnGroups: [ - new modelsv2.SimulateRequestTransactionGroup({ - txns: decodedSignedTransactions.map((txn) => decodeMsgpack(txn, SignedTransaction)), - }), + { + txns: transactionsWithSigners.map((txn) => ({ + txn: txn.txn, + signature: EMPTY_SIGNATURE, + })), + } satisfies SimulateRequestTransactionGroup, ], - }) - const simulateResult = await algod.simulateTransactions(simulateRequest).do() + } satisfies SimulateRequest + const simulateResult = await algod.simulateTransaction({ body: simulateRequest }) return simulateResult } diff --git a/src/transaction/transaction.spec.ts b/src/transaction/transaction.spec.ts index 03934eac..ff9fc488 100644 --- a/src/transaction/transaction.spec.ts +++ b/src/transaction/transaction.spec.ts @@ -1,4 +1,6 @@ -import algosdk, { ABIMethod, ABIType, Account, Address } from 'algosdk' +import { OnApplicationComplete } from '@algorandfoundation/algokit-transact' +import * as algosdk from '@algorandfoundation/sdk' +import { ABIMethod, ABIType, Account, Address } from '@algorandfoundation/sdk' import invariant from 'tiny-invariant' import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'vitest' import { APP_SPEC as nestedContractAppSpec } from '../../tests/example-contracts/client/TestContractClient' @@ -899,7 +901,7 @@ const resourcePopulationTests = (version: 8 | 9) => () => { const result = await appClient.send.call({ method: 'addressBalance', args: [algosdk.generateAccount().addr.toString()], - onComplete: algosdk.OnApplicationComplete.NoOpOC, + onComplete: OnApplicationComplete.NoOp, }) // Ensure the transaction was not sent via simulate @@ -965,8 +967,8 @@ describe('Resource population: Mixed', () => { .addAppCallMethodCall(await v9Client.params.call({ method: 'addressBalance', args: [acct.addr.toString()], sender: testAccount })) .send({ populateAppCallResources: true }) - const v8CallAccts = transactions[0].applicationCall?.accounts ?? [] - const v9CallAccts = transactions[1].applicationCall?.accounts ?? [] + const v8CallAccts = transactions[0].appCall?.accountReferences ?? [] + const v9CallAccts = transactions[1].appCall?.accountReferences ?? [] expect(v8CallAccts.length + v9CallAccts.length).toBe(1) }) @@ -989,8 +991,8 @@ describe('Resource population: Mixed', () => { ) .send({ populateAppCallResources: true }) - const v8CallApps = transactions[0].applicationCall?.foreignApps ?? [] - const v9CallAccts = transactions[1].applicationCall?.accounts ?? [] + const v8CallApps = transactions[0].appCall?.appReferences ?? [] + const v9CallAccts = transactions[1].appCall?.accountReferences ?? [] expect(v8CallApps!.length + v9CallAccts!.length).toBe(1) }) @@ -1099,7 +1101,7 @@ describe('Resource population: meta', () => { }) const res = await externalClient.send.call({ method: 'senderAssetBalance' }) - expect(res.transaction.applicationCall?.accounts?.length || 0).toBe(0) + expect(res.transaction.appCall?.accountReferences?.length || 0).toBe(0) }) test('rekeyed account', async () => { @@ -1120,7 +1122,7 @@ describe('Resource population: meta', () => { method: 'senderAssetBalance', }) - expect(res.transaction.applicationCall?.accounts?.length || 0).toBe(0) + expect(res.transaction.appCall?.accountReferences?.length || 0).toBe(0) }) test('create box in new app', async () => { @@ -1134,9 +1136,9 @@ describe('Resource population: meta', () => { staticFee: (4_000).microAlgo(), }) - const boxRef = result.transaction.applicationCall?.boxes?.[0] + const boxRef = result.transaction.appCall?.boxReferences?.[0] expect(boxRef).toBeDefined() - expect(boxRef?.appIndex).toBe(0n) + expect(boxRef?.appId).toBe(0n) }) test('order is deterministic', async () => { @@ -1191,20 +1193,20 @@ describe('Resource population: meta', () => { for (const txnWithSigner of populatedAtc.buildGroup()) { const txn = txnWithSigner.txn - for (const acct of txn.applicationCall?.accounts ?? []) { + for (const acct of txn.appCall?.accountReferences ?? []) { resources.push(acct.toString()) } - for (const asset of txn.applicationCall?.foreignAssets ?? []) { + for (const asset of txn.appCall?.assetReferences ?? []) { resources.push(asset.toString()) } - for (const app of txn.applicationCall?.foreignApps ?? []) { + for (const app of txn.appCall?.appReferences ?? []) { resources.push(app.toString()) } - for (const box of txn.applicationCall?.boxes ?? []) { - resources.push(`${box.appIndex}-${box.name.toString()}`) + for (const box of txn.appCall?.boxReferences ?? []) { + resources.push(`${box.appId}-${box.name.toString()}`) } } @@ -1354,7 +1356,7 @@ describe('access references', () => { method: 'addressBalance', args: [alice], populateAppCallResources: false, - accessReferences: [{ address: alice }], + accessReferences: [{ address: alice.toString() }], }) }) @@ -1375,7 +1377,7 @@ describe('access references', () => { populateAppCallResources: false, accountReferences: [alice, ...(await getTestAccounts(8))], }), - ).rejects.toThrow(/max number of accounts is 8/) + ).rejects.toThrow(/Account references cannot exceed 8 refs, got 9/) }) test('up to 16 access addresses can be used', async () => { @@ -1383,7 +1385,7 @@ describe('access references', () => { method: 'addressBalance', args: [alice], populateAppCallResources: false, - accessReferences: [{ address: alice }, ...(await getTestAccounts(15)).map((a) => ({ address: a }))], + accessReferences: [{ address: alice.toString() }, ...(await getTestAccounts(15)).map((a) => ({ address: a.toString() }))], }) }) @@ -1393,7 +1395,7 @@ describe('access references', () => { method: 'addressBalance', args: [alice], populateAppCallResources: false, - accessReferences: [{ address: alice }, ...(await getTestAccounts(16)).map((a) => ({ address: a }))], + accessReferences: [{ address: alice.toString() }, ...(await getTestAccounts(16)).map((a) => ({ address: a.toString() }))], }), ).rejects.toThrow(/max number of references is 16/) }) @@ -1434,7 +1436,7 @@ describe('access references', () => { method: 'hasAsset', args: [alice], populateAppCallResources: false, - accessReferences: [{ holding: { address: alice, assetId } }], + accessReferences: [{ holding: { address: alice.toString(), assetId: assetId } }], }) }) @@ -1447,7 +1449,7 @@ describe('access references', () => { method: 'externalLocal', args: [alice], populateAppCallResources: false, - accessReferences: [{ locals: { address: alice, appId: externalClient.appId } }], + accessReferences: [{ locals: { address: alice.toString(), appId: externalClient.appId } }], }) }) }) diff --git a/src/transaction/transaction.ts b/src/transaction/transaction.ts index 9f1808fb..a4b9a625 100644 --- a/src/transaction/transaction.ts +++ b/src/transaction/transaction.ts @@ -1,12 +1,15 @@ -import algosdk, { - ABIMethod, - ABIReturnType, - Address, - ApplicationTransactionFields, - stringifyJSON, - TransactionBoxReference, - TransactionType, -} from 'algosdk' +import { + AlgodClient, + ApplicationLocalReference, + AssetHoldingReference, + BoxReference, + PendingTransactionResponse, + SimulateRequest, +} from '@algorandfoundation/algokit-algod-client' +import type { AppCallTransactionFields } from '@algorandfoundation/algokit-transact' +import { Transaction, TransactionType, encodeTransaction, getTransactionId } from '@algorandfoundation/algokit-transact' +import * as algosdk from '@algorandfoundation/sdk' +import { ABIMethod, ABIReturnType, Address, AtomicTransactionComposer, TransactionSigner, stringifyJSON } from '@algorandfoundation/sdk' import { Buffer } from 'buffer' import { Config } from '../config' import { AlgoAmount } from '../types/amount' @@ -23,16 +26,19 @@ import { TransactionGroupToSend, TransactionNote, TransactionToSign, + TransactionWrapper, + wrapPendingTransactionResponse, } from '../types/transaction' -import { asJson, convertAbiByteArrays, convertABIDecodedBigIntToNumber, toNumber } from '../util' +import { asJson, convertABIDecodedBigIntToNumber, convertAbiByteArrays, toNumber } from '../util' import { performAtomicTransactionComposerSimulate } from './perform-atomic-transaction-composer-simulate' -import Algodv2 = algosdk.Algodv2 -import AtomicTransactionComposer = algosdk.AtomicTransactionComposer -import modelsv2 = algosdk.modelsv2 -import SuggestedParams = algosdk.SuggestedParams -import Transaction = algosdk.Transaction -import TransactionSigner = algosdk.TransactionSigner -import TransactionWithSigner = algosdk.TransactionWithSigner + +// Type aliases for compatibility +type ApplicationTransactionFields = AppCallTransactionFields + +export interface TransactionWithSigner { + txn: Transaction + signer: TransactionSigner +} export const MAX_TRANSACTION_GROUP_SIZE = 16 export const MAX_APP_CALL_FOREIGN_REFERENCES = 8 @@ -191,7 +197,7 @@ export const getSenderTransactionSigner = memoize(function (sender: SendTransact */ export const signTransaction = async (transaction: Transaction, signer: SendTransactionFrom) => { return 'sk' in signer - ? transaction.signTxn(signer.sk) + ? algosdk.signTransaction(transaction, signer.sk).blob : 'lsig' in signer ? algosdk.signLogicSigTransactionObject(transaction, signer).blob : 'sign' in signer @@ -218,7 +224,7 @@ export const sendTransaction = async function ( from: SendTransactionFrom sendParams?: SendTransactionParams }, - algod: Algodv2, + algod: AlgodClient, ): Promise { const { transaction, from, sendParams } = send const { skipSending, skipWaiting, fee, maxFee, suppressLog, maxRoundsToWaitForConfirmation, atc } = sendParams ?? {} @@ -227,11 +233,11 @@ export const sendTransaction = async function ( if (atc) { atc.addTransaction({ txn: transaction, signer: getSenderTransactionSigner(from) }) - return { transaction } + return { transaction: new TransactionWrapper(transaction) } } if (skipSending) { - return { transaction } + return { transaction: new TransactionWrapper(transaction) } } let txnToSend = transaction @@ -239,7 +245,7 @@ export const sendTransaction = async function ( const populateAppCallResources = sendParams?.populateAppCallResources ?? Config.populateAppCallResources // Populate resources if the transaction is an appcall and populateAppCallResources wasn't explicitly set to false - if (txnToSend.type === algosdk.TransactionType.appl && populateAppCallResources) { + if (txnToSend.type === TransactionType.AppCall && populateAppCallResources) { const newAtc = new AtomicTransactionComposer() newAtc.addTransaction({ txn: txnToSend, signer: getSenderTransactionSigner(from) }) const atc = await prepareGroupForSending(newAtc, algod, { ...sendParams, populateAppCallResources }) @@ -248,16 +254,21 @@ export const sendTransaction = async function ( const signedTransaction = await signTransaction(txnToSend, from) - await algod.sendRawTransaction(signedTransaction).do() + await algod.rawTransaction({ body: signedTransaction }) - Config.getLogger(suppressLog).verbose(`Sent transaction ID ${txnToSend.txID()} ${txnToSend.type} from ${getSenderAddress(from)}`) + Config.getLogger(suppressLog).verbose( + `Sent transaction ID ${getTransactionId(txnToSend)} ${txnToSend.type} from ${getSenderAddress(from)}`, + ) - let confirmation: modelsv2.PendingTransactionResponse | undefined = undefined + let confirmation: PendingTransactionResponse | undefined = undefined if (!skipWaiting) { - confirmation = await waitForConfirmation(txnToSend.txID(), maxRoundsToWaitForConfirmation ?? 5, algod) + confirmation = await waitForConfirmation(getTransactionId(txnToSend), maxRoundsToWaitForConfirmation ?? 5, algod) } - return { transaction: txnToSend, confirmation } + return { + transaction: new TransactionWrapper(txnToSend), + confirmation: confirmation ? wrapPendingTransactionResponse(confirmation) : undefined, + } } /** @@ -275,16 +286,16 @@ export const sendTransaction = async function ( */ async function getGroupExecutionInfo( atc: algosdk.AtomicTransactionComposer, - algod: algosdk.Algodv2, + algod: AlgodClient, sendParams: SendParams, additionalAtcContext?: AdditionalAtomicTransactionComposerContext, ) { - const simulateRequest = new algosdk.modelsv2.SimulateRequest({ + const simulateRequest: SimulateRequest = { txnGroups: [], allowUnnamedResources: true, allowEmptySignatures: true, fixSigners: true, - }) + } const nullSigner = algosdk.makeEmptyTransactionSigner() @@ -294,7 +305,7 @@ async function getGroupExecutionInfo( emptySignerAtc['transactions'].forEach((t: algosdk.TransactionWithSigner, i: number) => { t.signer = nullSigner - if (sendParams.coverAppCallInnerTransactionFees && t.txn.type === TransactionType.appl) { + if (sendParams.coverAppCallInnerTransactionFees && t.txn.type === TransactionType.AppCall) { if (!additionalAtcContext?.suggestedParams) { throw Error(`Please provide additionalAtcContext.suggestedParams when coverAppCallInnerTransactionFees is enabled`) } @@ -358,23 +369,23 @@ async function getGroupExecutionInfo( return { groupUnnamedResourcesAccessed: sendParams.populateAppCallResources ? sortedResources : undefined, txns: groupResponse.txnResults.map((txn, i) => { - const originalTxn = atc['transactions'][i].txn as algosdk.Transaction + const originalTxn = atc['transactions'][i].txn as Transaction let requiredFeeDelta = 0n if (sendParams.coverAppCallInnerTransactionFees) { // Min fee calc is lifted from algosdk https://github.com/algorand/js-algorand-sdk/blob/6973ff583b243ddb0632e91f4c0383021430a789/src/transaction.ts#L710 // 75 is the number of bytes added to a txn after signing it - const parentPerByteFee = perByteTxnFee * BigInt(originalTxn.toByte().length + 75) + const parentPerByteFee = perByteTxnFee * BigInt(encodeTransaction(originalTxn).length + 75) const parentMinFee = parentPerByteFee < minTxnFee ? minTxnFee : parentPerByteFee - const parentFeeDelta = parentMinFee - originalTxn.fee - if (originalTxn.type === TransactionType.appl) { - const calculateInnerFeeDelta = (itxns: algosdk.modelsv2.PendingTransactionResponse[], acc: bigint = 0n): bigint => { + const parentFeeDelta = parentMinFee - (originalTxn.fee ?? 0n) + if (originalTxn.type === TransactionType.AppCall) { + const calculateInnerFeeDelta = (itxns: PendingTransactionResponse[], acc: bigint = 0n): bigint => { // Surplus inner transaction fees do not pool up to the parent transaction. // Additionally surplus inner transaction fees only pool from sibling transactions that are sent prior to a given inner transaction, hence why we iterate in reverse order. return itxns.reverse().reduce((acc, itxn) => { const currentFeeDelta = (itxn.innerTxns && itxn.innerTxns.length > 0 ? calculateInnerFeeDelta(itxn.innerTxns, acc) : acc) + - (minTxnFee - itxn.txn.txn.fee) // Inner transactions don't require per byte fees + (minTxnFee - (itxn.txn.txn.fee ?? 0n)) // Inner transactions don't require per byte fees return currentFeeDelta < 0n ? 0n : currentFeeDelta }, acc) } @@ -411,7 +422,7 @@ async function getGroupExecutionInfo( * See https://github.com/algorand/go-algorand/pull/5684 * */ -export async function populateAppCallResources(atc: algosdk.AtomicTransactionComposer, algod: algosdk.Algodv2) { +export async function populateAppCallResources(atc: algosdk.AtomicTransactionComposer, algod: AlgodClient) { return await prepareGroupForSending(atc, algod, { populateAppCallResources: true }) } @@ -432,7 +443,7 @@ export async function populateAppCallResources(atc: algosdk.AtomicTransactionCom */ export async function prepareGroupForSending( atc: algosdk.AtomicTransactionComposer, - algod: algosdk.Algodv2, + algod: AlgodClient, sendParams: SendParams, additionalAtcContext?: AdditionalAtomicTransactionComposerContext, ) { @@ -448,7 +459,7 @@ export async function prepareGroupForSending( const immutableFee = maxFee !== undefined && maxFee === txnInGroup.fee // Because we don't alter non app call transaction, they take priority const priorityMultiplier = - txn.requiredFeeDelta > 0n && (immutableFee || txnInGroup.type !== algosdk.TransactionType.appl) ? 1_000n : 1n + txn.requiredFeeDelta > 0n && (immutableFee || txnInGroup.type !== TransactionType.AppCall) ? 1_000n : 1n return { ...txn, @@ -491,15 +502,15 @@ export async function prepareGroupForSending( ) : [0n, new Map()] - const appCallHasAccessReferences = (txn: algosdk.Transaction) => { - return txn.type === TransactionType.appl && txn.applicationCall?.access && txn.applicationCall?.access.length > 0 + const appCallHasAccessReferences = (txn: Transaction) => { + return txn.type === TransactionType.AppCall && txn.appCall?.accessReferences && txn.appCall?.accessReferences.length > 0 } const indexesWithAccessReferences: number[] = [] executionInfo.txns.forEach(({ unnamedResourcesAccessed: r }, i) => { // Populate Transaction App Call Resources - if (sendParams.populateAppCallResources && group[i].txn.type === TransactionType.appl) { + if (sendParams.populateAppCallResources && group[i].txn.type === TransactionType.AppCall) { const hasAccessReferences = appCallHasAccessReferences(group[i].txn) if (hasAccessReferences && (r || executionInfo.groupUnnamedResourcesAccessed)) { @@ -512,20 +523,20 @@ export async function prepareGroupForSending( if (r.assetHoldings) throw Error('Unexpected asset holding at the transaction level') // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(group[i].txn as any)['applicationCall'] = { - ...group[i].txn.applicationCall, - accounts: [...(group[i].txn?.applicationCall?.accounts ?? []), ...(r.accounts ?? [])], - foreignApps: [...(group[i].txn?.applicationCall?.foreignApps ?? []), ...(r.apps ?? [])], - foreignAssets: [...(group[i].txn?.applicationCall?.foreignAssets ?? []), ...(r.assets ?? [])], - boxes: [...(group[i].txn?.applicationCall?.boxes ?? []), ...(r.boxes ?? [])], + ;(group[i].txn as any)['appCall'] = { + ...group[i].txn.appCall, + accountReferences: [...(group[i].txn?.appCall?.accountReferences ?? []), ...(r.accounts ?? [])], + appReferences: [...(group[i].txn?.appCall?.appReferences ?? []), ...(r.apps ?? [])], + assetReferences: [...(group[i].txn?.appCall?.assetReferences ?? []), ...(r.assets ?? [])], + boxReferences: [...(group[i].txn?.appCall?.boxReferences ?? [])], } satisfies Partial - const accounts = group[i].txn.applicationCall?.accounts?.length ?? 0 + const accounts = group[i].txn.appCall?.accountReferences?.length ?? 0 if (accounts > MAX_APP_CALL_ACCOUNT_REFERENCES) throw Error(`Account reference limit of ${MAX_APP_CALL_ACCOUNT_REFERENCES} exceeded in transaction ${i}`) - const assets = group[i].txn.applicationCall?.foreignAssets?.length ?? 0 - const apps = group[i].txn.applicationCall?.foreignApps?.length ?? 0 - const boxes = group[i].txn.applicationCall?.boxes?.length ?? 0 + const assets = group[i].txn.appCall?.assetReferences?.length ?? 0 + const apps = group[i].txn.appCall?.appReferences?.length ?? 0 + const boxes = group[i].txn.appCall?.boxReferences?.length ?? 0 if (accounts + assets + apps + boxes > MAX_APP_CALL_FOREIGN_REFERENCES) { throw Error(`Resource reference limit of ${MAX_APP_CALL_FOREIGN_REFERENCES} exceeded in transaction ${i}`) } @@ -537,10 +548,10 @@ export async function prepareGroupForSending( const additionalTransactionFee = additionalTransactionFees.get(i) if (additionalTransactionFee !== undefined) { - if (group[i].txn.type !== algosdk.TransactionType.appl) { + if (group[i].txn.type !== TransactionType.AppCall) { throw Error(`An additional fee of ${additionalTransactionFee} µALGO is required for non app call transaction ${i}`) } - const transactionFee = group[i].txn.fee + additionalTransactionFee + const transactionFee = (group[i].txn.fee ?? 0n) + additionalTransactionFee const maxFee = additionalAtcContext?.maxFees?.get(i)?.microAlgo if (maxFee === undefined || transactionFee > maxFee) { throw Error( @@ -562,40 +573,33 @@ export async function prepareGroupForSending( const populateGroupResource = ( txns: algosdk.TransactionWithSigner[], - reference: - | string - | algosdk.modelsv2.BoxReference - | algosdk.modelsv2.ApplicationLocalReference - | algosdk.modelsv2.AssetHoldingReference - | bigint - | number - | Address, + reference: string | BoxReference | ApplicationLocalReference | AssetHoldingReference | bigint | number | Address, type: 'account' | 'assetHolding' | 'appLocal' | 'app' | 'box' | 'asset', ): void => { const isApplBelowLimit = (t: algosdk.TransactionWithSigner) => { - if (t.txn.type !== algosdk.TransactionType.appl) return false + if (t.txn.type !== TransactionType.AppCall) return false if (appCallHasAccessReferences(t.txn)) return false - const accounts = t.txn.applicationCall?.accounts?.length ?? 0 - const assets = t.txn.applicationCall?.foreignAssets?.length ?? 0 - const apps = t.txn.applicationCall?.foreignApps?.length ?? 0 - const boxes = t.txn.applicationCall?.boxes?.length ?? 0 + const accounts = t.txn.appCall?.accountReferences?.length ?? 0 + const assets = t.txn.appCall?.assetReferences?.length ?? 0 + const apps = t.txn.appCall?.appReferences?.length ?? 0 + const boxes = t.txn.appCall?.boxReferences?.length ?? 0 return accounts + assets + apps + boxes < MAX_APP_CALL_FOREIGN_REFERENCES } // If this is a asset holding or app local, first try to find a transaction that already has the account available if (type === 'assetHolding' || type === 'appLocal') { - const { account } = reference as algosdk.modelsv2.ApplicationLocalReference | algosdk.modelsv2.AssetHoldingReference + const { account } = reference as ApplicationLocalReference | AssetHoldingReference let txnIndex = txns.findIndex((t) => { if (!isApplBelowLimit(t)) return false return ( // account is in the foreign accounts array - t.txn.applicationCall?.accounts?.map((a) => a.toString()).includes(account.toString()) || + t.txn.appCall?.accountReferences?.map((a) => a.toString()).includes(account.toString()) || // account is available as an app account - t.txn.applicationCall?.foreignApps?.map((a) => algosdk.getApplicationAddress(a).toString()).includes(account.toString()) || + t.txn.appCall?.appReferences?.map((a) => algosdk.getApplicationAddress(a).toString()).includes(account.toString()) || // account is available since it's in one of the fields Object.values(t.txn).some((f) => stringifyJSON(f, (_, v) => (v instanceof Address ? v.toString() : v))?.includes(account.toString()), @@ -605,18 +609,18 @@ export async function prepareGroupForSending( if (txnIndex > -1) { if (type === 'assetHolding') { - const { asset } = reference as algosdk.modelsv2.AssetHoldingReference + const { asset } = reference as AssetHoldingReference // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(txns[txnIndex].txn as any)['applicationCall'] = { - ...txns[txnIndex].txn.applicationCall, - foreignAssets: [...(txns[txnIndex].txn?.applicationCall?.foreignAssets ?? []), ...[asset]], + ;(txns[txnIndex].txn as any)['appCall'] = { + ...txns[txnIndex].txn.appCall, + assetReferences: [...(txns[txnIndex].txn?.appCall?.assetReferences ?? []), ...[asset]], } satisfies Partial } else { - const { app } = reference as algosdk.modelsv2.ApplicationLocalReference + const { app } = reference as ApplicationLocalReference // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(txns[txnIndex].txn as any)['applicationCall'] = { - ...txns[txnIndex].txn.applicationCall, - foreignApps: [...(txns[txnIndex].txn?.applicationCall?.foreignApps ?? []), ...[app]], + ;(txns[txnIndex].txn as any)['appCall'] = { + ...txns[txnIndex].txn.appCall, + appReferences: [...(txns[txnIndex].txn?.appCall?.appReferences ?? []), ...[app]], } satisfies Partial } return @@ -627,24 +631,24 @@ export async function prepareGroupForSending( if (!isApplBelowLimit(t)) return false // check if there is space in the accounts array - if ((t.txn.applicationCall?.accounts?.length ?? 0) >= MAX_APP_CALL_ACCOUNT_REFERENCES) return false + if ((t.txn.appCall?.accountReferences?.length ?? 0) >= MAX_APP_CALL_ACCOUNT_REFERENCES) return false if (type === 'assetHolding') { - const { asset } = reference as algosdk.modelsv2.AssetHoldingReference - return t.txn.applicationCall?.foreignAssets?.includes(asset) + const { asset } = reference as AssetHoldingReference + return t.txn.appCall?.assetReferences?.includes(asset) } else { - const { app } = reference as algosdk.modelsv2.ApplicationLocalReference - return t.txn.applicationCall?.foreignApps?.includes(app) || t.txn.applicationCall?.appIndex === app + const { app } = reference as ApplicationLocalReference + return t.txn.appCall?.appReferences?.includes(app) || t.txn.appCall?.appId === app } }) if (txnIndex > -1) { - const { account } = reference as algosdk.modelsv2.AssetHoldingReference | algosdk.modelsv2.ApplicationLocalReference + const { account } = reference as AssetHoldingReference | ApplicationLocalReference // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(txns[txnIndex].txn as any)['applicationCall'] = { - ...txns[txnIndex].txn.applicationCall, - accounts: [...(txns[txnIndex].txn?.applicationCall?.accounts ?? []), ...[account]], + ;(txns[txnIndex].txn as any)['appCall'] = { + ...txns[txnIndex].txn.appCall, + accountReferences: [...(txns[txnIndex].txn?.appCall?.accountReferences ?? []), ...[account]], } satisfies Partial return @@ -653,20 +657,20 @@ export async function prepareGroupForSending( // If this is a box, first try to find a transaction that already has the app available if (type === 'box') { - const { app, name } = reference as algosdk.modelsv2.BoxReference + const { app, name } = reference as BoxReference const txnIndex = txns.findIndex((t) => { if (!isApplBelowLimit(t)) return false // If the app is in the foreign array OR the app being called, then we know it's available - return t.txn.applicationCall?.foreignApps?.includes(app) || t.txn.applicationCall?.appIndex === app + return t.txn.appCall?.appReferences?.includes(app) || t.txn.appCall?.appId === app }) if (txnIndex > -1) { // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(txns[txnIndex].txn as any)['applicationCall'] = { - ...txns[txnIndex].txn.applicationCall, - boxes: [...(txns[txnIndex].txn?.applicationCall?.boxes ?? []), ...[{ appIndex: app, name } satisfies TransactionBoxReference]], + ;(txns[txnIndex].txn as any)['appCall'] = { + ...txns[txnIndex].txn.appCall, + boxReferences: [...(txns[txnIndex].txn?.appCall?.boxReferences ?? []), ...[{ appId: app, name: name }]], } satisfies Partial return @@ -675,15 +679,15 @@ export async function prepareGroupForSending( // Find the txn index to put the reference(s) const txnIndex = txns.findIndex((t) => { - if (t.txn.type !== algosdk.TransactionType.appl) return false + if (t.txn.type !== TransactionType.AppCall) return false if (appCallHasAccessReferences(t.txn)) return false - const accounts = t.txn.applicationCall?.accounts?.length ?? 0 + const accounts = t.txn.appCall?.accountReferences?.length ?? 0 if (type === 'account') return accounts < MAX_APP_CALL_ACCOUNT_REFERENCES - const assets = t.txn.applicationCall?.foreignAssets?.length ?? 0 - const apps = t.txn.applicationCall?.foreignApps?.length ?? 0 - const boxes = t.txn.applicationCall?.boxes?.length ?? 0 + const assets = t.txn.appCall?.assetReferences?.length ?? 0 + const apps = t.txn.appCall?.appReferences?.length ?? 0 + const boxes = t.txn.appCall?.boxReferences?.length ?? 0 // If we're adding local state or asset holding, we need space for the acocunt and the other reference if (type === 'assetHolding' || type === 'appLocal') { @@ -691,7 +695,7 @@ export async function prepareGroupForSending( } // If we're adding a box, we need space for both the box ref and the app ref - if (type === 'box' && BigInt((reference as algosdk.modelsv2.BoxReference).app) !== BigInt(0)) { + if (type === 'box' && BigInt((reference as BoxReference).app) !== BigInt(0)) { return accounts + assets + apps + boxes < MAX_APP_CALL_FOREIGN_REFERENCES - 1 } @@ -704,56 +708,56 @@ export async function prepareGroupForSending( if (type === 'account') { // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(txns[txnIndex].txn as any)['applicationCall'] = { - ...txns[txnIndex].txn.applicationCall, - accounts: [...(txns[txnIndex].txn?.applicationCall?.accounts ?? []), ...[reference as Address]], + ;(txns[txnIndex].txn as any)['appCall'] = { + ...txns[txnIndex].txn.appCall, + accountReferences: [...(txns[txnIndex].txn?.appCall?.accountReferences ?? []), ...[(reference as Address).toString()]], } satisfies Partial } else if (type === 'app') { // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(txns[txnIndex].txn as any)['applicationCall'] = { - ...txns[txnIndex].txn.applicationCall, - foreignApps: [ - ...(txns[txnIndex].txn?.applicationCall?.foreignApps ?? []), + ;(txns[txnIndex].txn as any)['appCall'] = { + ...txns[txnIndex].txn.appCall, + appReferences: [ + ...(txns[txnIndex].txn?.appCall?.appReferences ?? []), ...[typeof reference === 'bigint' ? reference : BigInt(reference as number)], ], } satisfies Partial } else if (type === 'box') { - const { app, name } = reference as algosdk.modelsv2.BoxReference + const { app, name } = reference as BoxReference // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(txns[txnIndex].txn as any)['applicationCall'] = { - ...txns[txnIndex].txn.applicationCall, - boxes: [...(txns[txnIndex].txn?.applicationCall?.boxes ?? []), ...[{ appIndex: app, name } satisfies TransactionBoxReference]], + ;(txns[txnIndex].txn as any)['appCall'] = { + ...txns[txnIndex].txn.appCall, + boxReferences: [...(txns[txnIndex].txn?.appCall?.boxReferences ?? []), ...[{ appId: app, name }]], } satisfies Partial if (app.toString() !== '0') { // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(txns[txnIndex].txn as any)['applicationCall'] = { - ...txns[txnIndex].txn.applicationCall, - foreignApps: [...(txns[txnIndex].txn?.applicationCall?.foreignApps ?? []), ...[app]], + ;(txns[txnIndex].txn as any)['appCall'] = { + ...txns[txnIndex].txn.appCall, + appReferences: [...(txns[txnIndex].txn?.appCall?.appReferences ?? []), ...[app]], } satisfies Partial } } else if (type === 'assetHolding') { - const { asset, account } = reference as algosdk.modelsv2.AssetHoldingReference + const { asset, account } = reference as AssetHoldingReference // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(txns[txnIndex].txn as any)['applicationCall'] = { - ...txns[txnIndex].txn.applicationCall, - foreignAssets: [...(txns[txnIndex].txn?.applicationCall?.foreignAssets ?? []), ...[asset]], - accounts: [...(txns[txnIndex].txn?.applicationCall?.accounts ?? []), ...[account]], + ;(txns[txnIndex].txn as any)['appCall'] = { + ...txns[txnIndex].txn.appCall, + assetReferences: [...(txns[txnIndex].txn?.appCall?.assetReferences ?? []), ...[asset]], + accountReferences: [...(txns[txnIndex].txn?.appCall?.accountReferences ?? []), ...[account]], } satisfies Partial } else if (type === 'appLocal') { - const { app, account } = reference as algosdk.modelsv2.ApplicationLocalReference + const { app, account } = reference as ApplicationLocalReference // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(txns[txnIndex].txn as any)['applicationCall'] = { - ...txns[txnIndex].txn.applicationCall, - foreignApps: [...(txns[txnIndex].txn?.applicationCall?.foreignApps ?? []), ...[app]], - accounts: [...(txns[txnIndex].txn?.applicationCall?.accounts ?? []), ...[account]], + ;(txns[txnIndex].txn as any)['appCall'] = { + ...txns[txnIndex].txn.appCall, + appReferences: [...(txns[txnIndex].txn?.appCall?.appReferences ?? []), ...[app]], + accountReferences: [...(txns[txnIndex].txn?.appCall?.accountReferences ?? []), ...[account]], } satisfies Partial } else if (type === 'asset') { // eslint-disable-next-line @typescript-eslint/no-explicit-any - ;(txns[txnIndex].txn as any)['applicationCall'] = { - ...txns[txnIndex].txn.applicationCall, - foreignAssets: [ - ...(txns[txnIndex].txn?.applicationCall?.foreignAssets ?? []), + ;(txns[txnIndex].txn as any)['appCall'] = { + ...txns[txnIndex].txn.appCall, + assetReferences: [ + ...(txns[txnIndex].txn?.appCall?.assetReferences ?? []), ...[typeof reference === 'bigint' ? reference : BigInt(reference as number)], ], } satisfies Partial @@ -803,7 +807,7 @@ export async function prepareGroupForSending( if (g.extraBoxRefs) { for (let i = 0; i < g.extraBoxRefs; i += 1) { - const ref = new algosdk.modelsv2.BoxReference({ app: 0, name: new Uint8Array(0) }) + const ref: BoxReference = { app: 0n, name: new Uint8Array(0) } populateGroupResource(group, ref, 'box') } } @@ -818,6 +822,7 @@ export async function prepareGroupForSending( }) newAtc['methodCalls'] = atc['methodCalls'] + return newAtc } @@ -827,7 +832,7 @@ export async function prepareGroupForSending( * @param algod An algod client * @returns An object with transaction IDs, transactions, group transaction ID (`groupTransactionId`) if more than 1 transaction sent, and (if `skipWaiting` is `false` or unset) confirmation (`confirmation`) */ -export const sendAtomicTransactionComposer = async function (atcSend: AtomicTransactionComposerToSend, algod: Algodv2) { +export const sendAtomicTransactionComposer = async function (atcSend: AtomicTransactionComposerToSend, algod: AlgodClient) { const { atc: givenAtc, sendParams, additionalAtcContext, ...executeParams } = atcSend let atc: AtomicTransactionComposer @@ -843,7 +848,7 @@ export const sendAtomicTransactionComposer = async function (atcSend: AtomicTran if ( (populateAppCallResources || coverAppCallInnerTransactionFees) && - transactionsWithSigner.map((t) => t.txn.type).includes(algosdk.TransactionType.appl) + transactionsWithSigner.map((t) => t.txn.type).includes(TransactionType.AppCall) ) { atc = await prepareGroupForSending( givenAtc, @@ -869,7 +874,7 @@ export const sendAtomicTransactionComposer = async function (atcSend: AtomicTran Config.getLogger(executeParams?.suppressLog ?? sendParams?.suppressLog).debug( `Transaction IDs (${groupId})`, - transactionsToSend.map((t) => t.txID()), + transactionsToSend.map((t) => getTransactionId(t)), ) } @@ -891,36 +896,34 @@ export const sendAtomicTransactionComposer = async function (atcSend: AtomicTran ) } else { Config.getLogger(executeParams?.suppressLog ?? sendParams?.suppressLog).verbose( - `Sent transaction ID ${transactionsToSend[0].txID()} ${transactionsToSend[0].type} from ${transactionsToSend[0].sender.toString()}`, + `Sent transaction ID ${getTransactionId(transactionsToSend[0])} ${transactionsToSend[0].type} from ${transactionsToSend[0].sender}`, ) } - let confirmations: modelsv2.PendingTransactionResponse[] | undefined = undefined + let confirmations: PendingTransactionResponse[] | undefined = undefined if (!sendParams?.skipWaiting) { - confirmations = await Promise.all(transactionsToSend.map(async (t) => await algod.pendingTransactionInformation(t.txID()).do())) + confirmations = await Promise.all(transactionsToSend.map(async (t) => await algod.pendingTransactionInformation(getTransactionId(t)))) } const methodCalls = [...(atc['methodCalls'] as Map).values()] return { - groupId, - confirmations, - txIds: transactionsToSend.map((t) => t.txID()), - transactions: transactionsToSend, + groupId: groupId!, + confirmations: (confirmations ?? []).map(wrapPendingTransactionResponse), + txIds: transactionsToSend.map((t) => getTransactionId(t)), + transactions: transactionsToSend.map((t) => new TransactionWrapper(t)), returns: result.methodResults.map((r, i) => getABIReturnValue(r, methodCalls[i]!.returns.type)), - } as SendAtomicTransactionComposerResults + } satisfies SendAtomicTransactionComposerResults // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (e: any) { + // TODO: PD - look into error handling here again, it's possible that we don't need this comment anymore // Create a new error object so the stack trace is correct (algosdk throws an error with a more limited stack trace) + + const errorMessage = e.body?.message ?? e.message ?? 'Received error executing Atomic Transaction Composer' // eslint-disable-next-line @typescript-eslint/no-explicit-any - const err = new Error(typeof e === 'object' ? e?.message : 'Received error executing Atomic Transaction Composer') as any as any + const err = new Error(errorMessage) as any err.cause = e if (typeof e === 'object') { - // Remove headers as it doesn't have anything useful. - delete e.response?.headers - err.response = e.response - // body property very noisy - if (e.response && 'body' in e.response) delete err.response.body err.name = e.name } @@ -941,7 +944,7 @@ export const sendAtomicTransactionComposer = async function (atcSend: AtomicTran if (simulate && simulate.txnGroups[0].failedAt) { for (const txn of simulate.txnGroups[0].txnResults) { err.traces.push({ - trace: txn.execTrace?.toEncodingData(), + trace: undefined, // TODO: PD - need to encode txn.execTrace?.toEncodingData(), SimulationTransactionExecTrace appBudget: txn.appBudgetConsumed, logicSigBudget: txn.logicSigBudgetConsumed, logs: txn.txnResult.logs, @@ -957,7 +960,7 @@ export const sendAtomicTransactionComposer = async function (atcSend: AtomicTran } // Attach the sent transactions so we can use them in error transformers - err.sentTransactions = atc.buildGroup().map((t) => t.txn) + err.sentTransactions = atc.buildGroup().map((t) => new TransactionWrapper(t.txn)) throw err } } @@ -1000,7 +1003,7 @@ export function getABIReturnValue(result: algosdk.ABIResult, type: ABIReturnType * @param algod An algod client * @returns An object with transaction IDs, transactions, group transaction ID (`groupTransactionId`) if more than 1 transaction sent, and (if `skipWaiting` is `false` or unset) confirmation (`confirmation`) */ -export const sendGroupOfTransactions = async function (groupSend: TransactionGroupToSend, algod: Algodv2) { +export const sendGroupOfTransactions = async function (groupSend: TransactionGroupToSend, algod: AlgodClient) { const { transactions, signer, sendParams } = groupSend const defaultTransactionSigner = signer ? getSenderTransactionSigner(signer) : undefined @@ -1016,7 +1019,9 @@ export const sendGroupOfTransactions = async function (groupSend: TransactionGro const txn = 'then' in t ? (await t).transaction : t if (!signer) { - throw new Error(`Attempt to send transaction ${txn.txID()} as part of a group transaction, but no signer parameter was provided.`) + throw new Error( + `Attempt to send transaction ${getTransactionId(txn)} as part of a group transaction, but no signer parameter was provided.`, + ) } return { @@ -1047,14 +1052,14 @@ export const sendGroupOfTransactions = async function (groupSend: TransactionGro export const waitForConfirmation = async function ( transactionId: string, maxRoundsToWait: number | bigint, - algod: Algodv2, -): Promise { + algod: AlgodClient, +): Promise { if (maxRoundsToWait < 0) { throw new Error(`Invalid timeout, received ${maxRoundsToWait}, expected > 0`) } // Get current round - const status = await algod.status().do() + const status = await algod.getStatus() if (status === undefined) { throw new Error('Unable to get node status') } @@ -1064,7 +1069,7 @@ export const waitForConfirmation = async function ( let currentRound = startRound while (currentRound < startRound + BigInt(maxRoundsToWait)) { try { - const pendingInfo = await algod.pendingTransactionInformation(transactionId).do() + const pendingInfo = await algod.pendingTransactionInformation(transactionId) if (pendingInfo !== undefined) { const confirmedRound = pendingInfo.confirmedRound @@ -1078,14 +1083,15 @@ export const waitForConfirmation = async function ( } } } - } catch (e: unknown) { - if ((e as Error).name === 'URLTokenBaseHTTPError') { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { + if ('status' in e && e.status === 404) { currentRound++ continue } } - await algod.statusAfterBlock(toNumber(currentRound)).do() + await algod.waitForBlock(toNumber(currentRound)) currentRound++ } @@ -1101,16 +1107,16 @@ export const waitForConfirmation = async function ( * @param transaction The transaction to cap or suggested params object about to be used to create a transaction * @param maxAcceptableFee The maximum acceptable fee to pay */ -export function capTransactionFee(transaction: algosdk.Transaction | SuggestedParams, maxAcceptableFee: AlgoAmount) { +export function capTransactionFee(transaction: Transaction | algosdk.SdkTransactionParams, maxAcceptableFee: AlgoAmount) { // If a flat fee hasn't already been defined if (!('flatFee' in transaction) || !transaction.flatFee) { // Once a transaction has been constructed by algosdk, transaction.fee indicates what the total transaction fee // Will be based on the current suggested fee-per-byte value. - if (transaction.fee > maxAcceptableFee.microAlgo) { + if ((transaction.fee ?? 0n) > maxAcceptableFee.microAlgo) { throw new Error( `Cancelled transaction due to high network congestion fees. Algorand suggested fees would cause this transaction to cost ${transaction.fee} µALGO. Cap for this transaction is ${maxAcceptableFee.microAlgo} µALGO.`, ) - } else if (transaction.fee > 1_000_000) { + } else if ((transaction.fee ?? 0n) > 1_000_000) { Config.logger.warn(`Algorand network congestion fees are in effect. This transaction will incur a fee of ${transaction.fee} µALGO.`) } @@ -1128,13 +1134,13 @@ export function capTransactionFee(transaction: algosdk.Transaction | SuggestedPa * @param transaction The transaction or suggested params * @param feeControl The fee control parameters */ -export function controlFees( +export function controlFees( transaction: T, feeControl: { fee?: AlgoAmount; maxFee?: AlgoAmount }, ) { const { fee, maxFee } = feeControl if (fee) { - transaction.fee = Number(fee.microAlgo) + transaction.fee = fee.microAlgo if ('flatFee' in transaction) { transaction.flatFee = true } @@ -1155,18 +1161,22 @@ export function controlFees( * @param algod Algod algod * @returns The suggested transaction parameters */ -export async function getTransactionParams(params: SuggestedParams | undefined, algod: Algodv2): Promise { +export async function getTransactionParams( + params: algosdk.SdkTransactionParams | undefined, + algod: AlgodClient, +): Promise { if (params) { return { ...params } } - const p = await algod.getTransactionParams().do() + const p = await algod.transactionParams() return { fee: p.fee, - firstValid: p.firstValid, - lastValid: p.lastValid, - genesisID: p.genesisID, + firstRound: p.lastRound, + lastRound: p.lastRound + 1000n, + genesisId: p.genesisId, genesisHash: p.genesisHash, minFee: p.minFee, + consensusVersion: p.consensusVersion, } } diff --git a/src/transfer/transfer-algos.ts b/src/transfer/transfer-algos.ts index 6e11e7f4..96419759 100644 --- a/src/transfer/transfer-algos.ts +++ b/src/transfer/transfer-algos.ts @@ -1,10 +1,9 @@ -import algosdk from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' import { legacySendTransactionBridge } from '../transaction/legacy-bridge' import { encodeTransactionNote, getSenderAddress } from '../transaction/transaction' import { PaymentParams } from '../types/composer' import { SendTransactionResult } from '../types/transaction' import { AlgoTransferParams } from '../types/transfer' -import Algodv2 = algosdk.Algodv2 /** * @deprecated Use `algorand.send.payment()` / `algorand.createTransaction.payment()` instead @@ -19,7 +18,7 @@ import Algodv2 = algosdk.Algodv2 * await algokit.transferAlgos({ from, to, amount: algokit.algo(1) }, algod) * ``` */ -export async function transferAlgos(transfer: AlgoTransferParams, algod: Algodv2): Promise { +export async function transferAlgos(transfer: AlgoTransferParams, algod: AlgodClient): Promise { const params: PaymentParams = { sender: getSenderAddress(transfer.from), receiver: getSenderAddress(transfer.to), diff --git a/src/transfer/transfer.ts b/src/transfer/transfer.ts index 46fe10cc..4fcb1377 100644 --- a/src/transfer/transfer.ts +++ b/src/transfer/transfer.ts @@ -1,12 +1,11 @@ -import algosdk from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' +import { Kmd } from '@algorandfoundation/sdk' import { legacySendTransactionBridge } from '../transaction/legacy-bridge' import { encodeTransactionNote, getSenderAddress } from '../transaction/transaction' import { AlgorandClient } from '../types/algorand-client' import { TestNetDispenserApiClient } from '../types/dispenser-client' import { SendTransactionResult } from '../types/transaction' import { AlgoRekeyParams, EnsureFundedParams, EnsureFundedReturnType, TransferAssetParams } from '../types/transfer' -import Algodv2 = algosdk.Algodv2 -import Kmd = algosdk.Kmd /** * @deprecated Use `algorand.account.ensureFunded()` / `algorand.account.ensureFundedFromEnvironment()` @@ -17,7 +16,7 @@ import Kmd = algosdk.Kmd * https://dev.algorand.co/concepts/smart-contracts/costs-constraints#mbr * * @param funding The funding configuration of type `EnsureFundedParams`, including the account to fund, minimum spending balance, and optional parameters. If you set `useDispenserApi` to true, you must also set `ALGOKIT_DISPENSER_ACCESS_TOKEN` in your environment variables. - * @param algod An instance of the Algodv2 client. + * @param algod An instance of the AlgodClient client. * @param kmd An optional instance of the Kmd client. * @returns * - `EnsureFundedReturnType` if funds were transferred. @@ -25,7 +24,7 @@ import Kmd = algosdk.Kmd */ export async function ensureFunded( funding: T, - algod: Algodv2, + algod: AlgodClient, kmd?: Kmd, ): Promise { const algorand = AlgorandClient.fromClients({ algod, kmd }) @@ -87,7 +86,7 @@ export async function ensureFunded( * await algokit.transferAsset({ from, to, assetId, amount }, algod) * ``` */ -export async function transferAsset(transfer: TransferAssetParams, algod: Algodv2): Promise { +export async function transferAsset(transfer: TransferAssetParams, algod: AlgodClient): Promise { return legacySendTransactionBridge( algod, transfer.from, @@ -122,7 +121,7 @@ export async function transferAsset(transfer: TransferAssetParams, algod: Algodv * await algokit.rekeyAccount({ from, rekeyTo }, algod) * ``` */ -export async function rekeyAccount(rekey: AlgoRekeyParams, algod: Algodv2): Promise { +export async function rekeyAccount(rekey: AlgoRekeyParams, algod: AlgodClient): Promise { return legacySendTransactionBridge( algod, rekey.from, diff --git a/src/types/account-manager.spec.ts b/src/types/account-manager.spec.ts index f2d62edf..b37f81d4 100644 --- a/src/types/account-manager.spec.ts +++ b/src/types/account-manager.spec.ts @@ -1,4 +1,4 @@ -import algosdk from 'algosdk' +import * as algosdk from '@algorandfoundation/sdk' import { v4 as uuid } from 'uuid' import { beforeEach, describe, expect, test } from 'vitest' import { algo } from '../amount' diff --git a/src/types/account-manager.ts b/src/types/account-manager.ts index 476ae9a8..6415732b 100644 --- a/src/types/account-manager.ts +++ b/src/types/account-manager.ts @@ -1,4 +1,7 @@ -import algosdk, { Address } from 'algosdk' +import { TransactionParams } from '@algorandfoundation/algokit-algod-client' +import type { Account } from '@algorandfoundation/sdk' +import * as algosdk from '@algorandfoundation/sdk' +import { Address, LogicSigAccount, TransactionSigner } from '@algorandfoundation/sdk' import { Config } from '../config' import { calculateFundAmount, memoize } from '../util' import { AccountInformation, DISPENSER_ACCOUNT, MultisigAccount, SigningAccount, TransactionSignerAccount } from './account' @@ -8,9 +11,6 @@ import { CommonTransactionParams, TransactionComposer } from './composer' import { TestNetDispenserApiClient } from './dispenser-client' import { KmdAccountManager } from './kmd-account-manager' import { SendParams, SendSingleTransactionResult } from './transaction' -import LogicSigAccount = algosdk.LogicSigAccount -import Account = algosdk.Account -import TransactionSigner = algosdk.TransactionSigner const address = (address: string | Address) => (typeof address === 'string' ? Address.fromString(address) : address) @@ -62,11 +62,11 @@ export class AccountManager { this._kmdAccountManager = new KmdAccountManager(clientManager) } - private _getComposer(getSuggestedParams?: () => Promise) { + private _getComposer(getSuggestedParams?: () => Promise) { return new TransactionComposer({ algod: this._clientManager.algod, getSigner: this.getSigner.bind(this), - getSuggestedParams: getSuggestedParams ?? (() => this._clientManager.algod.getTransactionParams().do()), + getSuggestedParams: getSuggestedParams ?? (() => this._clientManager.algod.transactionParams()), }) } @@ -239,17 +239,19 @@ export class AccountManager { * @returns The account information */ public async getInformation(sender: string | Address): Promise { + const senderAddress = typeof sender === 'string' ? sender : sender.toString() const { round, lastHeartbeat = undefined, lastProposed = undefined, address, ...account - } = await this._clientManager.algod.accountInformation(sender).do() + } = await this._clientManager.algod.accountInformation(senderAddress) return { ...account, // None of the Number types can practically overflow 2^53 + authAddr: account.authAddr ? Address.fromString(account.authAddr) : undefined, address: Address.fromString(address), balance: AlgoAmount.MicroAlgo(Number(account.amount)), amountWithoutPendingRewards: AlgoAmount.MicroAlgo(Number(account.amountWithoutPendingRewards)), diff --git a/src/types/account.ts b/src/types/account.ts index ee87b8c6..00c534e4 100644 --- a/src/types/account.ts +++ b/src/types/account.ts @@ -1,15 +1,17 @@ -import algosdk, { Address } from 'algosdk' +import { + AccountParticipation, + Application, + ApplicationLocalState, + ApplicationStateSchema, + Asset, + AssetHolding, +} from '@algorandfoundation/algokit-algod-client' +import { Transaction } from '@algorandfoundation/algokit-transact' +import type { Account } from '@algorandfoundation/sdk' +import * as algosdk from '@algorandfoundation/sdk' +import { Address, MultisigMetadata, TransactionSigner } from '@algorandfoundation/sdk' +import { appendSignMultisigTransaction, signMultisigTransaction } from '@algorandfoundation/sdk/src/multisigSigning' import { AlgoAmount } from './amount' -import ApplicationLocalState = algosdk.modelsv2.ApplicationLocalState -import ApplicationStateSchema = algosdk.modelsv2.ApplicationStateSchema -import AssetHolding = algosdk.modelsv2.AssetHolding -import Application = algosdk.modelsv2.Application -import Asset = algosdk.modelsv2.Asset -import AccountParticipation = algosdk.modelsv2.AccountParticipation -import Account = algosdk.Account -import MultisigMetadata = algosdk.MultisigMetadata -import Transaction = algosdk.Transaction -import TransactionSigner = algosdk.TransactionSigner /** * The account name identifier used for fund dispensing in test environments @@ -66,9 +68,9 @@ export class MultisigAccount { let signedTxn = 'sender' in transaction ? undefined : transaction for (const signer of this._signingAccounts) { if (signedTxn) { - signedTxn = algosdk.appendSignMultisigTransaction(signedTxn, this._params, signer.sk).blob + signedTxn = appendSignMultisigTransaction(signedTxn, this._params, signer.sk).blob } else { - signedTxn = algosdk.signMultisigTransaction(transaction as Transaction, this._params, signer.sk).blob + signedTxn = signMultisigTransaction(transaction as Transaction, this._params, signer.sk).blob } } return signedTxn! diff --git a/src/types/algo-http-client-with-retry.ts b/src/types/algo-http-client-with-retry.ts index 214e194d..7dc37a12 100644 --- a/src/types/algo-http-client-with-retry.ts +++ b/src/types/algo-http-client-with-retry.ts @@ -1,5 +1,5 @@ -import { IntDecoding, parseJSON, stringifyJSON } from 'algosdk' -import { BaseHTTPClientResponse, Query, URLTokenBaseHTTPClient } from 'algosdk/client' +import { IntDecoding, parseJSON, stringifyJSON } from '@algorandfoundation/sdk' +import { BaseHTTPClientResponse, Query, URLTokenBaseHTTPClient } from '@algorandfoundation/sdk' import { Config } from '../config' /** A HTTP Client that wraps the Algorand SDK HTTP Client with retries */ diff --git a/src/types/algorand-client-transaction-creator.ts b/src/types/algorand-client-transaction-creator.ts index 50132b1f..f5708048 100644 --- a/src/types/algorand-client-transaction-creator.ts +++ b/src/types/algorand-client-transaction-creator.ts @@ -1,8 +1,6 @@ -import algosdk from 'algosdk' import { BuiltTransactions, TransactionComposer } from './composer' import { Expand } from './expand' - -import Transaction = algosdk.Transaction +import { TransactionWrapper } from './transaction' /** Orchestrates creating transactions for `AlgorandClient`. */ export class AlgorandClientTransactionCreator { @@ -20,11 +18,11 @@ export class AlgorandClientTransactionCreator { this._newGroup = newGroup } - private _transaction(c: (c: TransactionComposer) => (params: T) => TransactionComposer): (params: T) => Promise { + private _transaction(c: (c: TransactionComposer) => (params: T) => TransactionComposer): (params: T) => Promise { return async (params: T) => { const composer = this._newGroup() const result = await c(composer).apply(composer, [params]).buildTransactions() - return result.transactions.at(-1)! + return new TransactionWrapper(result.transactions.at(-1)!) } } @@ -331,7 +329,7 @@ export class AlgorandClientTransactionCreator { * localByteSlices: 4 * }, * extraProgramPages: 1, - * onComplete: algosdk.OnApplicationComplete.OptInOC, + * onComplete: OnApplicationComplete.OptIn, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -368,7 +366,7 @@ export class AlgorandClientTransactionCreator { * sender: 'CREATORADDRESS', * approvalProgram: "TEALCODE", * clearStateProgram: "TEALCODE", - * onComplete: algosdk.OnApplicationComplete.UpdateApplicationOC, + * onComplete: OnApplicationComplete.UpdateApplication, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -403,7 +401,7 @@ export class AlgorandClientTransactionCreator { * ```typescript * await algorand.createTransaction.appDelete({ * sender: 'CREATORADDRESS', - * onComplete: algosdk.OnApplicationComplete.DeleteApplicationOC, + * onComplete: OnApplicationComplete.DeleteApplication, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -438,7 +436,7 @@ export class AlgorandClientTransactionCreator { * ```typescript * await algorand.createTransaction.appCall({ * sender: 'CREATORADDRESS', - * onComplete: algosdk.OnApplicationComplete.OptInOC, + * onComplete: OnApplicationComplete.OptIn, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -494,7 +492,7 @@ export class AlgorandClientTransactionCreator { * localByteSlices: 4 * }, * extraProgramPages: 1, - * onComplete: algosdk.OnApplicationComplete.OptInOC, + * onComplete: OnApplicationComplete.OptIn, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -543,7 +541,7 @@ export class AlgorandClientTransactionCreator { * args: ["arg1_value"], * approvalProgram: "TEALCODE", * clearStateProgram: "TEALCODE", - * onComplete: algosdk.OnApplicationComplete.UpdateApplicationOC, + * onComplete: OnApplicationComplete.UpdateApplication, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -590,7 +588,7 @@ export class AlgorandClientTransactionCreator { * sender: 'CREATORADDRESS', * method: method, * args: ["arg1_value"], - * onComplete: algosdk.OnApplicationComplete.DeleteApplicationOC, + * onComplete: OnApplicationComplete.DeleteApplication, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -637,7 +635,7 @@ export class AlgorandClientTransactionCreator { * sender: 'CREATORADDRESS', * method: method, * args: ["arg1_value"], - * onComplete: algosdk.OnApplicationComplete.OptInOC, + * onComplete: OnApplicationComplete.OptIn, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] diff --git a/src/types/algorand-client-transaction-sender.ts b/src/types/algorand-client-transaction-sender.ts index 43f7734a..e3f16e3c 100644 --- a/src/types/algorand-client-transaction-sender.ts +++ b/src/types/algorand-client-transaction-sender.ts @@ -1,4 +1,6 @@ -import algosdk, { Address } from 'algosdk' +import { Transaction, getTransactionId } from '@algorandfoundation/algokit-transact' +import * as algosdk from '@algorandfoundation/sdk' +import { Address } from '@algorandfoundation/sdk' import { Buffer } from 'buffer' import { Config } from '../config' import { asJson, defaultJsonValueReplacer } from '../util' @@ -19,7 +21,6 @@ import { TransactionComposer, } from './composer' import { SendParams, SendSingleTransactionResult } from './transaction' -import Transaction = algosdk.Transaction const getMethodCallForLog = ({ method, args }: { method: algosdk.ABIMethod; args?: unknown[] }) => { return `${method.name}(${(args ?? []).map((a) => @@ -155,8 +156,8 @@ export class AlgorandClientTransactionSender { return { ...result, - appId: BigInt(result.confirmation.applicationIndex!), - appAddress: algosdk.getApplicationAddress(result.confirmation.applicationIndex!), + appId: BigInt(result.confirmation.appId!), + appAddress: algosdk.getApplicationAddress(result.confirmation.appId!), } } } @@ -203,7 +204,7 @@ export class AlgorandClientTransactionSender { */ payment = this._send((c) => c.addPayment, { preLog: (params, transaction) => - `Sending ${params.amount.microAlgo} µALGO from ${params.sender} to ${params.receiver} via transaction ${transaction.txID()}`, + `Sending ${params.amount.microAlgo} µALGO from ${params.sender} to ${params.receiver} via transaction ${getTransactionId(transaction)}`, }) /** * Create a new Algorand Standard Asset. @@ -255,9 +256,9 @@ export class AlgorandClientTransactionSender { assetCreate = async (params: AssetCreateParams & SendParams) => { const result = await this._send((c) => c.addAssetCreate, { postLog: (params, result) => - `Created asset${params.assetName ? ` ${params.assetName}` : ''}${params.unitName ? ` (${params.unitName})` : ''} with ${params.total} units and ${params.decimals ?? 0} decimals created by ${params.sender} with ID ${result.confirmation.assetIndex} via transaction ${result.txIds.at(-1)}`, + `Created asset${params.assetName ? ` ${params.assetName}` : ''}${params.unitName ? ` (${params.unitName})` : ''} with ${params.total} units and ${params.decimals ?? 0} decimals created by ${params.sender} with ID ${result.confirmation.assetId} via transaction ${result.txIds.at(-1)}`, })(params) - return { ...result, assetId: BigInt(result.confirmation.assetIndex ?? 0) } + return { ...result, assetId: BigInt(result.confirmation.assetId ?? 0) } } /** * Configure an existing Algorand Standard Asset. @@ -302,7 +303,7 @@ export class AlgorandClientTransactionSender { * @returns The result of the asset config transaction and the transaction that was sent */ assetConfig = this._send((c) => c.addAssetConfig, { - preLog: (params, transaction) => `Configuring asset with ID ${params.assetId} via transaction ${transaction.txID()}`, + preLog: (params, transaction) => `Configuring asset with ID ${params.assetId} via transaction ${getTransactionId(transaction)}`, }) /** * Freeze or unfreeze an Algorand Standard Asset for an account. @@ -341,7 +342,7 @@ export class AlgorandClientTransactionSender { * @returns The result of the asset freeze transaction and the transaction that was sent */ assetFreeze = this._send((c) => c.addAssetFreeze, { - preLog: (params, transaction) => `Freezing asset with ID ${params.assetId} via transaction ${transaction.txID()}`, + preLog: (params, transaction) => `Freezing asset with ID ${params.assetId} via transaction ${getTransactionId(transaction)}`, }) /** * Destroys an Algorand Standard Asset. @@ -382,7 +383,7 @@ export class AlgorandClientTransactionSender { * @returns The result of the asset destroy transaction and the transaction that was sent */ assetDestroy = this._send((c) => c.addAssetDestroy, { - preLog: (params, transaction) => `Destroying asset with ID ${params.assetId} via transaction ${transaction.txID()}`, + preLog: (params, transaction) => `Destroying asset with ID ${params.assetId} via transaction ${getTransactionId(transaction)}`, }) /** * Transfer an Algorand Standard Asset. @@ -425,7 +426,7 @@ export class AlgorandClientTransactionSender { */ assetTransfer = this._send((c) => c.addAssetTransfer, { preLog: (params, transaction) => - `Transferring ${params.amount} units of asset with ID ${params.assetId} from ${params.sender} to ${params.receiver} via transaction ${transaction.txID()}`, + `Transferring ${params.amount} units of asset with ID ${params.assetId} from ${params.sender} to ${params.receiver} via transaction ${getTransactionId(transaction)}`, }) /** * Opt an account into an Algorand Standard Asset. @@ -462,7 +463,8 @@ export class AlgorandClientTransactionSender { * @returns The result of the asset opt-in transaction and the transaction that was sent */ assetOptIn = this._send((c) => c.addAssetOptIn, { - preLog: (params, transaction) => `Opting in ${params.sender} to asset with ID ${params.assetId} via transaction ${transaction.txID()}`, + preLog: (params, transaction) => + `Opting in ${params.sender} to asset with ID ${params.assetId} via transaction ${getTransactionId(transaction)}`, }) /** * Opt an account out of an Algorand Standard Asset. @@ -538,7 +540,7 @@ export class AlgorandClientTransactionSender { return await this._send((c) => c.addAssetOptOut, { preLog: (params, transaction) => - `Opting ${params.sender} out of asset with ID ${params.assetId} to creator ${params.creator} via transaction ${transaction.txID()}`, + `Opting ${params.sender} out of asset with ID ${params.assetId} to creator ${params.creator} via transaction ${getTransactionId(transaction)}`, })(params as AssetOptOutParams & SendParams) } /** @@ -565,7 +567,7 @@ export class AlgorandClientTransactionSender { * localByteSlices: 4 * }, * extraProgramPages: 1, - * onComplete: algosdk.OnApplicationComplete.OptInOC, + * onComplete: OnApplicationComplete.OptIn, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -594,7 +596,7 @@ export class AlgorandClientTransactionSender { */ appCreate = this._sendAppCreateCall((c) => c.addAppCreate, { postLog: (params, result) => - `App created by ${params.sender} with ID ${result.confirmation.applicationIndex} via transaction ${result.txIds.at(-1)}`, + `App created by ${params.sender} with ID ${result.confirmation.appId} via transaction ${result.txIds.at(-1)}`, }) /** @@ -613,7 +615,7 @@ export class AlgorandClientTransactionSender { * sender: 'CREATORADDRESS', * approvalProgram: "TEALCODE", * clearStateProgram: "TEALCODE", - * onComplete: algosdk.OnApplicationComplete.UpdateApplicationOC, + * onComplete: OnApplicationComplete.UpdateApplication, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -659,7 +661,7 @@ export class AlgorandClientTransactionSender { * ```typescript * await algorand.send.appDelete({ * sender: 'CREATORADDRESS', - * onComplete: algosdk.OnApplicationComplete.DeleteApplicationOC, + * onComplete: OnApplicationComplete.DeleteApplication, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -705,7 +707,7 @@ export class AlgorandClientTransactionSender { * ```typescript * await algorand.send.appCall({ * sender: 'CREATORADDRESS', - * onComplete: algosdk.OnApplicationComplete.OptInOC, + * onComplete: OnApplicationComplete.OptIn, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -773,7 +775,7 @@ export class AlgorandClientTransactionSender { * localByteSlices: 4 * }, * extraProgramPages: 1, - * onComplete: algosdk.OnApplicationComplete.OptInOC, + * onComplete: OnApplicationComplete.OptIn, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -802,7 +804,7 @@ export class AlgorandClientTransactionSender { */ appCreateMethodCall = this._sendAppCreateCall((c) => c.addAppCreateMethodCall, { postLog: (params, result) => - `App created by ${params.sender} with ID ${result.confirmation.applicationIndex} via transaction ${result.txIds.at(-1)}`, + `App created by ${params.sender} with ID ${result.confirmation.appId} via transaction ${result.txIds.at(-1)}`, }) /** @@ -833,7 +835,7 @@ export class AlgorandClientTransactionSender { * args: ["arg1_value"], * approvalProgram: "TEALCODE", * clearStateProgram: "TEALCODE", - * onComplete: algosdk.OnApplicationComplete.UpdateApplicationOC, + * onComplete: OnApplicationComplete.UpdateApplication, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -891,7 +893,7 @@ export class AlgorandClientTransactionSender { * sender: 'CREATORADDRESS', * method: method, * args: ["arg1_value"], - * onComplete: algosdk.OnApplicationComplete.DeleteApplicationOC, + * onComplete: OnApplicationComplete.DeleteApplication, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -949,7 +951,7 @@ export class AlgorandClientTransactionSender { * sender: 'CREATORADDRESS', * method: method, * args: ["arg1_value"], - * onComplete: algosdk.OnApplicationComplete.OptInOC, + * onComplete: OnApplicationComplete.OptIn, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -1023,7 +1025,7 @@ export class AlgorandClientTransactionSender { * @returns The result of the online key registration transaction and the transaction that was sent */ onlineKeyRegistration = this._send((c) => c.addOnlineKeyRegistration, { - preLog: (params, transaction) => `Registering online key for ${params.sender} via transaction ${transaction.txID()}`, + preLog: (params, transaction) => `Registering online key for ${params.sender} via transaction ${getTransactionId(transaction)}`, }) /** @@ -1056,6 +1058,6 @@ export class AlgorandClientTransactionSender { * @returns The result of the offline key registration transaction and the transaction that was sent */ offlineKeyRegistration = this._send((c) => c.addOfflineKeyRegistration, { - preLog: (params, transaction) => `Registering offline key for ${params.sender} via transaction ${transaction.txID()}`, + preLog: (params, transaction) => `Registering offline key for ${params.sender} via transaction ${getTransactionId(transaction)}`, }) } diff --git a/src/types/algorand-client.asset.spec.ts b/src/types/algorand-client.asset.spec.ts index 79970a61..20a2b16c 100644 --- a/src/types/algorand-client.asset.spec.ts +++ b/src/types/algorand-client.asset.spec.ts @@ -25,7 +25,7 @@ describe('Asset capability', () => { defaultFrozen: true, }) - expect(result.confirmation.assetIndex).toBeGreaterThan(0n) + expect(result.confirmation.assetId).toBeGreaterThan(0n) const assetData = await algorand.asset.getById(result.assetId) expect(assetData.creator).toBe(testAccount.toString()) expect(assetData.total).toBe(1000n) diff --git a/src/types/algorand-client.spec.ts b/src/types/algorand-client.spec.ts index 3c177f07..b8f6147b 100644 --- a/src/types/algorand-client.spec.ts +++ b/src/types/algorand-client.spec.ts @@ -1,14 +1,16 @@ -import algosdk, { Account, Address } from 'algosdk' import { beforeAll, describe, expect, test } from 'vitest' import { APP_SPEC, TestContractClient } from '../../tests/example-contracts/client/TestContractClient' +import * as algosdk from '@algorandfoundation/sdk' +import { Account, Address } from '@algorandfoundation/sdk' import { algorandFixture } from '../testing' import { AlgorandClient } from './algorand-client' import { AlgoAmount } from './amount' import { AppCallMethodCall } from './composer' async function compileProgram(algorand: AlgorandClient, b64Teal: string) { - const teal = new Uint8Array(Buffer.from(b64Teal, 'base64')) - const result = await algorand.client.algod.compile(teal).do() + // Decode the base64-encoded TEAL source code + const tealSource = Buffer.from(b64Teal, 'base64').toString('utf-8') + const result = await algorand.client.algod.tealCompile({ body: tealSource }) return new Uint8Array(Buffer.from(result.result, 'base64')) } @@ -263,7 +265,7 @@ describe('AlgorandClient', () => { sender: alice, assetId: assetId, }) - expect(await algod.accountAssetInformation(alice, Number(assetId)).do()).toBeDefined() + expect(await algod.accountAssetInformation(alice.toString(), Number(assetId))).toBeDefined() }) test('methodCall create', async () => { diff --git a/src/types/algorand-client.transfer.spec.ts b/src/types/algorand-client.transfer.spec.ts index 7a75c3ff..d52f439e 100644 --- a/src/types/algorand-client.transfer.spec.ts +++ b/src/types/algorand-client.transfer.spec.ts @@ -1,4 +1,4 @@ -import algosdk, { TransactionType } from 'algosdk' +import { TransactionType } from '@algorandfoundation/algokit-transact' import invariant from 'tiny-invariant' import { afterEach, beforeEach, describe, expect, test, vitest } from 'vitest' import { algorandFixture } from '../testing' @@ -33,8 +33,7 @@ describe('Transfer capability', () => { const accountInfo = await algorand.account.getInformation(secondAccount) - expect(result.transaction).toBeInstanceOf(algosdk.Transaction) - expect(result.transaction.type).toBe(TransactionType.pay) + expect(result.transaction.type).toBe(TransactionType.Payment) expect(result.confirmation.txn.txn.type).toBe('pay') expect(result.transaction.payment?.amount).toBe(5_000_000n) @@ -118,57 +117,49 @@ describe('Transfer capability', () => { const dummyAssetId = await generateTestAsset(algorand, testAccount, 100) const secondAccount = algorand.account.random() - try { - await algorand.send.assetTransfer({ + await expect( + algorand.send.assetTransfer({ sender: testAccount, receiver: secondAccount, assetId: dummyAssetId, amount: 1n, note: `Transfer 5 assets with id ${dummyAssetId}`, - }) - } catch (e: unknown) { - expect((e as Error).name).toEqual('URLTokenBaseHTTPError') - expect((e as Error).message).toContain('receiver error: must optin') - } + }), + ).rejects.toThrow(/receiver error: must optin/) }, 10e6) test('Transfer ASA, sender is not opted in', async () => { const { algorand, testAccount, generateAccount } = localnet.context - const dummyAssetId = await generateTestAsset(algorand, testAccount, 100) + const assetCreator = await generateAccount({ initialFunds: (1).algo() }) + const dummyAssetId = await generateTestAsset(algorand, assetCreator, 100) const secondAccount = await generateAccount({ initialFunds: (1).algo() }) await algorand.send.assetOptIn({ sender: secondAccount, assetId: dummyAssetId }) - try { - await algorand.send.assetTransfer({ + await expect( + algorand.send.assetTransfer({ sender: testAccount, receiver: secondAccount, assetId: dummyAssetId, amount: 1n, note: `Transfer 5 assets with id ${dummyAssetId}`, - }) - } catch (e: unknown) { - expect((e as Error).name).toEqual('URLTokenBaseHTTPError') - expect((e as Error).message).toContain('sender error: must optin') - } + }), + ).rejects.toThrow(/asset .* missing from/) }, 10e6) test('Transfer ASA, asset doesnt exist', async () => { const { algorand, testAccount, generateAccount } = localnet.context const secondAccount = await generateAccount({ initialFunds: (1).algo() }) - try { - await algorand.send.assetTransfer({ + await expect( + algorand.send.assetTransfer({ sender: testAccount, receiver: secondAccount, assetId: 1n, amount: 5n, note: 'Transfer asset with wrong id', - }) - } catch (e: unknown) { - expect((e as Error).name).toEqual('URLTokenBaseHTTPError') - expect((e as Error).message).toContain('asset 1 missing from') - } + }), + ).rejects.toThrow(/asset 1 missing from/) }, 10e6) test('Transfer ASA, asset is transfered to another account', async () => { diff --git a/src/types/algorand-client.ts b/src/types/algorand-client.ts index 24bd16f2..bf207356 100644 --- a/src/types/algorand-client.ts +++ b/src/types/algorand-client.ts @@ -1,4 +1,7 @@ -import algosdk, { Address } from 'algosdk' +import { TransactionParams } from '@algorandfoundation/algokit-algod-client' +import type { Account } from '@algorandfoundation/sdk' +import * as algosdk from '@algorandfoundation/sdk' +import { Address, LogicSigAccount } from '@algorandfoundation/sdk' import { MultisigAccount, SigningAccount, TransactionSignerAccount } from './account' import { AccountManager } from './account-manager' import { AlgorandClientTransactionCreator } from './algorand-client-transaction-creator' @@ -9,8 +12,6 @@ import { AssetManager } from './asset-manager' import { AlgoSdkClients, ClientManager } from './client-manager' import { ErrorTransformer, TransactionComposer } from './composer' import { AlgoConfig } from './network-client' -import Account = algosdk.Account -import LogicSigAccount = algosdk.LogicSigAccount /** * A client that brokers easy access to Algorand functionality. @@ -24,7 +25,7 @@ export class AlgorandClient { private _transactionSender: AlgorandClientTransactionSender private _transactionCreator: AlgorandClientTransactionCreator - private _cachedSuggestedParams?: algosdk.SuggestedParams + private _cachedSuggestedParams?: TransactionParams private _cachedSuggestedParamsExpiry?: Date private _cachedSuggestedParamsTimeout: number = 3_000 // three seconds @@ -122,7 +123,7 @@ export class AlgorandClient { * const algorand = AlgorandClient.mainNet().setSuggestedParamsCache(suggestedParams, new Date(+new Date() + 3_600_000)) * ``` */ - public setSuggestedParamsCache(suggestedParams: algosdk.SuggestedParams, until?: Date) { + public setSuggestedParamsCache(suggestedParams: TransactionParams, until?: Date) { this._cachedSuggestedParams = suggestedParams this._cachedSuggestedParamsExpiry = until ?? new Date(+new Date() + this._cachedSuggestedParamsTimeout) return this @@ -148,14 +149,14 @@ export class AlgorandClient { * @example * const params = await AlgorandClient.mainNet().getSuggestedParams(); */ - public async getSuggestedParams(): Promise { + public async getSuggestedParams(): Promise { if (this._cachedSuggestedParams && (!this._cachedSuggestedParamsExpiry || this._cachedSuggestedParamsExpiry > new Date())) { return { ...this._cachedSuggestedParams, } } - this._cachedSuggestedParams = await this._clientManager.algod.getTransactionParams().do() + this._cachedSuggestedParams = await this._clientManager.algod.transactionParams() this._cachedSuggestedParamsExpiry = new Date(new Date().getTime() + this._cachedSuggestedParamsTimeout) return { diff --git a/src/types/amount.ts b/src/types/amount.ts index 446eac77..f0f6bdca 100644 --- a/src/types/amount.ts +++ b/src/types/amount.ts @@ -1,4 +1,4 @@ -import algosdk from 'algosdk' +import * as algosdk from '@algorandfoundation/sdk' /** Wrapper class to ensure safe, explicit conversion between µAlgo, Algo and numbers */ export class AlgoAmount { diff --git a/src/types/app-arc56.ts b/src/types/app-arc56.ts index 51359b45..20d8b2ac 100644 --- a/src/types/app-arc56.ts +++ b/src/types/app-arc56.ts @@ -1,4 +1,4 @@ -import algosdk from 'algosdk' +import * as algosdk from '@algorandfoundation/sdk' import { convertAbiByteArrays, convertABIDecodedBigIntToNumber } from '../util' import { ABIReturn } from './app' import { Expand } from './expand' diff --git a/src/types/app-client.spec.ts b/src/types/app-client.spec.ts index 950cc7a2..7ba95292 100644 --- a/src/types/app-client.spec.ts +++ b/src/types/app-client.spec.ts @@ -1,14 +1,7 @@ -import algosdk, { - ABIUintType, - Account, - Address, - Algodv2, - getApplicationAddress, - Indexer, - OnApplicationComplete, - TransactionSigner, - TransactionType, -} from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' +import { OnApplicationComplete, TransactionType } from '@algorandfoundation/algokit-transact' +import * as algosdk from '@algorandfoundation/sdk' +import { ABIUintType, Account, Address, Indexer, TransactionSigner, getApplicationAddress } from '@algorandfoundation/sdk' import invariant from 'tiny-invariant' import { afterEach, beforeAll, beforeEach, describe, expect, test } from 'vitest' import * as algokit from '..' @@ -32,7 +25,7 @@ describe('application-client', () => { appSpec = (await getTestingAppContract()).appSpec }) - const deploy = async (account: Address & Account, algod: Algodv2, indexer: Indexer) => { + const deploy = async (account: Address & Account, algod: AlgodClient, indexer: Indexer) => { const client = algokit.getAppClient( { resolveBy: 'creatorAndName', @@ -75,7 +68,7 @@ describe('application-client', () => { expect(app.appId).toBeGreaterThan(0) expect(app.appAddress).toBe(getApplicationAddress(app.appId).toString()) - expect(app.confirmation?.applicationIndex).toBe(BigInt(app.appId)) + expect(app.confirmation?.appId).toBe(BigInt(app.appId)) expect(app.compiledApproval).toBeTruthy() }) @@ -123,10 +116,10 @@ describe('application-client', () => { }, }) - expect(app.transaction.applicationCall?.onComplete).toBe(OnApplicationComplete.OptInOC) + expect(app.transaction.appCall?.onComplete).toBe(OnApplicationComplete.OptIn) expect(app.appId).toBeGreaterThan(0) expect(app.appAddress).toBe(getApplicationAddress(app.appId).toString()) - expect(app.confirmation?.applicationIndex).toBe(BigInt(app.appId)) + expect(app.confirmation?.appId).toBe(BigInt(app.appId)) }) test('Deploy app - can still deploy when immutable and permanent', async () => { @@ -178,7 +171,7 @@ describe('application-client', () => { invariant(app.operationPerformed === 'create') expect(app.appId).toBeGreaterThan(0) expect(app.appAddress).toBe(getApplicationAddress(app.appId).toString()) - expect(app.confirmation?.applicationIndex).toBe(BigInt(app.appId)) + expect(app.confirmation?.appId).toBe(BigInt(app.appId)) expect(app.compiledApproval).toBeTruthy() }) @@ -210,7 +203,7 @@ describe('application-client', () => { invariant(app.operationPerformed === 'create') expect(app.appId).toBeGreaterThan(0) expect(app.appAddress).toBe(getApplicationAddress(app.appId).toString()) - expect(app.confirmation?.applicationIndex).toBe(BigInt(app.appId)) + expect(app.confirmation?.appId).toBe(BigInt(app.appId)) expect(app.return?.returnValue).toBe('arg_io') }) @@ -288,7 +281,7 @@ describe('application-client', () => { expect(app.createdRound).toBe(createdApp.createdRound) expect(app.updatedRound).not.toBe(app.createdRound) expect(app.updatedRound).toBe(Number(app.confirmation.confirmedRound)) - expect(app.transaction.applicationCall?.onComplete).toBe(OnApplicationComplete.UpdateApplicationOC) + expect(app.transaction.appCall?.onComplete).toBe(OnApplicationComplete.UpdateApplication) expect(app.return?.returnValue).toBe('arg_io') }) @@ -325,8 +318,8 @@ describe('application-client', () => { invariant(app.confirmation) invariant(app.deleteResult) invariant(app.deleteResult.confirmation) - expect(app.deleteResult.transaction.applicationCall?.appIndex).toBe(BigInt(createdApp.appId)) - expect(app.deleteResult.transaction.applicationCall?.onComplete).toBe(OnApplicationComplete.DeleteApplicationOC) + expect(app.deleteResult.transaction.appCall?.appId).toBe(BigInt(createdApp.appId)) + expect(app.deleteResult.transaction.appCall?.onComplete).toBe(OnApplicationComplete.DeleteApplication) }) test('Deploy app - replace (abi)', async () => { @@ -372,8 +365,8 @@ describe('application-client', () => { invariant(app.confirmation) invariant(app.deleteResult) invariant(app.deleteResult.confirmation) - expect(app.deleteResult.transaction.applicationCall?.appIndex).toBe(BigInt(createdApp.appId)) - expect(app.deleteResult.transaction.applicationCall?.onComplete).toBe(OnApplicationComplete.DeleteApplicationOC) + expect(app.deleteResult.transaction.appCall?.appId).toBe(BigInt(createdApp.appId)) + expect(app.deleteResult.transaction.appCall?.onComplete).toBe(OnApplicationComplete.DeleteApplication) expect(app.return?.returnValue).toBe('arg_io') expect(app.deleteReturn?.returnValue).toBe('arg2_io') }) @@ -541,7 +534,7 @@ describe('application-client', () => { }) const encoder = new TextEncoder() - expect(call.transaction.applicationCall?.boxes).toEqual([{ appIndex: 0n, name: encoder.encode('1') }]) + expect(call.transaction.appCall?.boxReferences).toEqual([{ appId: 0n, name: encoder.encode('1') }]) }) test('Construct transaction with abi encoding including transaction', async () => { @@ -738,7 +731,7 @@ describe('application-client', () => { }) expect(result.transaction.payment?.amount).toBe(fundAmount.microAlgo) - expect(result.transaction.type).toBe(TransactionType.pay) + expect(result.transaction.type).toBe(TransactionType.Payment) expect(result.transaction.payment?.receiver?.toString()).toBe(app.appAddress) expect(result.transaction.sender.toString()).toBe(testAccount.toString()) invariant(result.confirmation) @@ -906,7 +899,7 @@ describe('app-client', () => { expect(appClient.appName).toBe('overridden') expect(clonedAppClient.appId).toBe(appClient.appId) expect(clonedAppClient.appName).toBe(appClient.appName) - expect(algosdk.encodeAddress((await clonedAppClient.createTransaction.bare.call()).sender.publicKey)).toBe(testAccount2.addr.toString()) + expect((await clonedAppClient.createTransaction.bare.call()).sender).toBe(testAccount2.addr.toString()) }) test('clone overriding appName', async () => { diff --git a/src/types/app-client.ts b/src/types/app-client.ts index c94bff57..ad6106e6 100644 --- a/src/types/app-client.ts +++ b/src/types/app-client.ts @@ -1,4 +1,18 @@ -import algosdk, { Address } from 'algosdk' +import { AlgodClient, TransactionParams } from '@algorandfoundation/algokit-algod-client' +import { OnApplicationComplete } from '@algorandfoundation/algokit-transact' +import * as algosdk from '@algorandfoundation/sdk' +import { + ABIMethod, + ABIMethodParams, + ABIType, + ABIValue, + Address, + AtomicTransactionComposer, + Indexer, + ProgramSourceMap, + TransactionSigner, + getApplicationAddress, +} from '@algorandfoundation/sdk' import { Buffer } from 'buffer' import { callApp, @@ -46,14 +60,14 @@ import { ABIStruct, Arc56Contract, Arc56Method, + ProgramSourceInfo, + StorageKey, + StorageMap, getABIDecodedValue, getABIEncodedValue, getABITupleFromABIStruct, getArc56Method, getArc56ReturnValue, - ProgramSourceInfo, - StorageKey, - StorageMap, } from './app-arc56' import { AppLookup } from './app-deployer' import { AppManager, BoxIdentifier } from './app-manager' @@ -73,22 +87,18 @@ import { import { Expand } from './expand' import { EventType } from './lifecycle-events' import { LogicError } from './logic-error' -import { SendParams, SendTransactionFrom, SendTransactionParams, TransactionNote } from './transaction' -import ABIMethod = algosdk.ABIMethod -import ABIMethodParams = algosdk.ABIMethodParams -import ABIType = algosdk.ABIType -import ABIValue = algosdk.ABIValue -import Algodv2 = algosdk.Algodv2 -import AtomicTransactionComposer = algosdk.AtomicTransactionComposer -import getApplicationAddress = algosdk.getApplicationAddress -import Indexer = algosdk.Indexer -import OnApplicationComplete = algosdk.OnApplicationComplete -import SourceMap = algosdk.ProgramSourceMap -import SuggestedParams = algosdk.SuggestedParams -import TransactionSigner = algosdk.TransactionSigner +import { + SendParams, + SendTransactionFrom, + SendTransactionParams, + TransactionNote, + TransactionWrapper, + wrapPendingTransactionResponse, + wrapPendingTransactionResponseOptional, +} from './transaction' /** The maximum opcode budget for a simulate call as per https://github.com/algorand/go-algorand/blob/807b29a91c371d225e12b9287c5d56e9b33c4e4c/ledger/simulation/trace.go#L104 */ -const MAX_SIMULATE_OPCODE_BUDGET = 20_000 * 16 +const MAX_SIMULATE_OPCODE_BUDGET = BigInt(20_000 * 16) /** Configuration to resolve app by creator and name `getCreatorAppsByName` */ export type ResolveAppByCreatorAndNameBase = { @@ -127,7 +137,7 @@ export type AppDetailsBase = { /** Default sender to use for transactions issued by this application client */ sender?: SendTransactionFrom /** Default suggested params object to use */ - params?: SuggestedParams + params?: TransactionParams /** Optionally provide any deploy-time parameters to replace in the TEAL code; if specified here will get * used in calls to `deploy`, `create` and `update` unless overridden in those calls */ @@ -184,7 +194,7 @@ export interface AppClientDeployCallInterfaceParams { /** Any args to pass to any create transaction that is issued as part of deployment */ createArgs?: AppClientCallArgs /** Override the on-completion action for the create call; defaults to NoOp */ - createOnCompleteAction?: Exclude | Exclude + createOnCompleteAction?: Exclude | Exclude /** Any args to pass to any update transaction that is issued as part of deployment */ updateArgs?: AppClientCallArgs /** Any args to pass to any delete transaction that is issued as part of deployment */ @@ -235,7 +245,7 @@ export interface AppClientCompilationParams { /** On-complete action parameter for creating a contract using ApplicationClient */ export type AppClientCreateOnComplete = { /** Override the on-completion action for the create call; defaults to NoOp */ - onCompleteAction?: Exclude | Exclude + onCompleteAction?: Exclude | Exclude } /** Parameters for creating a contract using ApplicationClient */ @@ -340,9 +350,9 @@ export interface AppClientParams { /** Optional signer to use as the default signer for default sender calls (if not specified then the signer will be resolved from `AlgorandClient`). */ defaultSigner?: TransactionSigner /** Optional source map for the approval program */ - approvalSourceMap?: SourceMap + approvalSourceMap?: ProgramSourceMap /** Optional source map for the clear state program */ - clearSourceMap?: SourceMap + clearSourceMap?: ProgramSourceMap } /** Parameters to clone an app client */ @@ -351,7 +361,7 @@ export type CloneAppClientParams = Expand + onComplete?: Exclude } /** AppClient common parameters for a bare app call */ @@ -484,8 +494,8 @@ export class AppClient { private _defaultSender?: Address private _defaultSigner?: TransactionSigner - private _approvalSourceMap: SourceMap | undefined - private _clearSourceMap: SourceMap | undefined + private _approvalSourceMap: ProgramSourceMap | undefined + private _clearSourceMap: ProgramSourceMap | undefined private _localStateMethods: (address: string | Address) => ReturnType private _globalStateMethods: ReturnType @@ -903,8 +913,8 @@ export class AppClient { * @param sourceMaps The source maps to import */ public importSourceMaps(sourceMaps: AppSourceMaps) { - this._approvalSourceMap = new SourceMap(sourceMaps.approvalSourceMap) - this._clearSourceMap = new SourceMap(sourceMaps.clearSourceMap) + this._approvalSourceMap = new ProgramSourceMap(sourceMaps.approvalSourceMap) + this._clearSourceMap = new ProgramSourceMap(sourceMaps.clearSourceMap) } /** @@ -975,8 +985,8 @@ export class AppClient { appSpec: Arc56Contract, details: { /** Whether or not the code was running the clear state program (defaults to approval program) */ isClearStateProgram?: boolean - /** Approval program source map */ approvalSourceMap?: SourceMap - /** Clear state program source map */ clearSourceMap?: SourceMap + /** Approval program source map */ approvalSourceMap?: ProgramSourceMap + /** Clear state program source map */ clearSourceMap?: ProgramSourceMap /** program bytes */ program?: Uint8Array /** ARC56 approval source info */ approvalSourceInfo?: ProgramSourceInfo /** ARC56 clear source info */ clearSourceInfo?: ProgramSourceInfo @@ -1198,28 +1208,28 @@ export class AppClient { ...params, ...(await this.compile(params)), }, - OnApplicationComplete.UpdateApplicationOC, + OnApplicationComplete.UpdateApplication, ) as AppUpdateParams }, /** Return params for an opt-in call */ optIn: (params?: AppClientBareCallParams) => { - return this.getBareParams(params, OnApplicationComplete.OptInOC) as AppCallParams + return this.getBareParams(params, OnApplicationComplete.OptIn) as AppCallParams }, /** Return params for a delete call */ delete: (params?: AppClientBareCallParams) => { - return this.getBareParams(params, OnApplicationComplete.DeleteApplicationOC) as AppDeleteParams + return this.getBareParams(params, OnApplicationComplete.DeleteApplication) as AppDeleteParams }, /** Return params for a clear state call */ clearState: (params?: AppClientBareCallParams) => { - return this.getBareParams(params, OnApplicationComplete.ClearStateOC) as AppCallParams + return this.getBareParams(params, OnApplicationComplete.ClearState) as AppCallParams }, /** Return params for a close out call */ closeOut: (params?: AppClientBareCallParams) => { - return this.getBareParams(params, OnApplicationComplete.CloseOutOC) as AppCallParams + return this.getBareParams(params, OnApplicationComplete.CloseOut) as AppCallParams }, /** Return params for a call (defaults to no-op) */ call: (params?: AppClientBareCallParams & CallOnComplete) => { - return this.getBareParams(params, params?.onComplete ?? OnApplicationComplete.NoOpOC) as AppCallParams + return this.getBareParams(params, params?.onComplete ?? OnApplicationComplete.NoOp) as AppCallParams }, } } @@ -1312,7 +1322,7 @@ export class AppClient { ...params, ...(await this.compile(params)), }, - OnApplicationComplete.UpdateApplicationOC, + OnApplicationComplete.UpdateApplication, )) satisfies AppUpdateMethodCall }, /** @@ -1321,7 +1331,7 @@ export class AppClient { * @returns The parameters which can be used to create an opt-in ABI method call */ optIn: async (params: AppClientMethodCallParams) => { - return (await this.getABIParams(params, OnApplicationComplete.OptInOC)) as AppCallMethodCall + return (await this.getABIParams(params, OnApplicationComplete.OptIn)) as AppCallMethodCall }, /** * Return params for an delete ABI call @@ -1329,21 +1339,21 @@ export class AppClient { * @returns The parameters which can be used to create a delete ABI method call */ delete: async (params: AppClientMethodCallParams) => { - return (await this.getABIParams(params, OnApplicationComplete.DeleteApplicationOC)) as AppDeleteMethodCall + return (await this.getABIParams(params, OnApplicationComplete.DeleteApplication)) as AppDeleteMethodCall }, /** Return params for an close out ABI call * @param params The parameters for the close out ABI method call * @returns The parameters which can be used to create a close out ABI method call */ closeOut: async (params: AppClientMethodCallParams) => { - return (await this.getABIParams(params, OnApplicationComplete.CloseOutOC)) as AppCallMethodCall + return (await this.getABIParams(params, OnApplicationComplete.CloseOut)) as AppCallMethodCall }, /** Return params for an ABI call * @param params The parameters for the ABI method call * @returns The parameters which can be used to create an ABI method call */ call: async (params: AppClientMethodCallParams & CallOnComplete) => { - return (await this.getABIParams(params, params.onComplete ?? OnApplicationComplete.NoOpOC)) as AppCallMethodCall + return (await this.getABIParams(params, params.onComplete ?? OnApplicationComplete.NoOp)) as AppCallMethodCall }, } } @@ -1413,7 +1423,7 @@ export class AppClient { call: async (params: AppClientMethodCallParams & CallOnComplete & SendParams) => { // Read-only call - do it via simulate if ( - (params.onComplete === OnApplicationComplete.NoOpOC || !params.onComplete) && + (params.onComplete === OnApplicationComplete.NoOp || !params.onComplete) && getArc56Method(params.method, this._appSpec).method.readonly ) { const readonlyParams = { @@ -1578,7 +1588,7 @@ export class AppClient { } /** Make the given call and catch any errors, augmenting with debugging information before re-throwing. */ - private handleCallErrors = async (e: Error & { sentTransactions?: algosdk.Transaction[] }) => { + private handleCallErrors = async (e: Error & { sentTransactions?: TransactionWrapper[] }) => { // We can't use the app ID in an error to identify new apps, so instead we check the programs // to identify if this is the correct app if (this.appId === 0n) { @@ -1600,8 +1610,8 @@ export class AppClient { } if ( - !programsDefinedAndEqual(txn?.applicationCall?.clearProgram, this._lastCompiled.clear) || - !programsDefinedAndEqual(txn?.applicationCall?.approvalProgram, this._lastCompiled?.approval) + !programsDefinedAndEqual(txn?.appCall?.clearStateProgram, this._lastCompiled.clear) || + !programsDefinedAndEqual(txn?.appCall?.approvalProgram, this._lastCompiled?.approval) ) { return e } @@ -1797,11 +1807,11 @@ export class AppClient { * * Application client - a class that wraps an ARC-0032 app spec and provides high productivity methods to deploy and call the app */ export class ApplicationClient { - private algod: Algodv2 + private algod: AlgodClient private indexer?: algosdk.Indexer private appSpec: AppSpec private sender: SendTransactionFrom | undefined - private params: SuggestedParams | undefined + private params: TransactionParams | undefined private existingDeployments: LegacyAppLookup | undefined private deployTimeParams?: TealTemplateParams @@ -1810,8 +1820,8 @@ export class ApplicationClient { private _creator: string | undefined private _appName: string - private _approvalSourceMap: SourceMap | undefined - private _clearSourceMap: SourceMap | undefined + private _approvalSourceMap: ProgramSourceMap | undefined + private _clearSourceMap: ProgramSourceMap | undefined /** * @deprecated Use `AppClient` instead e.g. via `algorand.client.getAppClientById` or @@ -1823,7 +1833,7 @@ export class ApplicationClient { * @param appDetails The details of the app * @param algod An algod instance */ - constructor(appDetails: AppSpecAppDetails, algod: Algodv2) { + constructor(appDetails: AppSpecAppDetails, algod: AlgodClient) { const { app, sender, params, deployTimeParams, ...appIdentifier } = appDetails this.algod = algod this.appSpec = typeof app == 'string' ? (JSON.parse(app) as AppSpec) : app @@ -1913,8 +1923,8 @@ export class ApplicationClient { * @param sourceMaps The source maps to import */ importSourceMaps(sourceMaps: AppSourceMaps) { - this._approvalSourceMap = new SourceMap(sourceMaps.approvalSourceMap) - this._clearSourceMap = new SourceMap(sourceMaps.clearSourceMap) + this._approvalSourceMap = new ProgramSourceMap(sourceMaps.approvalSourceMap) + this._clearSourceMap = new ProgramSourceMap(sourceMaps.clearSourceMap) } /** @@ -2088,7 +2098,7 @@ export class ApplicationClient { ) if (result.confirmation) { - this._appId = result.confirmation.applicationIndex! + this._appId = result.confirmation.appId! this._appAddress = getApplicationAddress(this._appId).toString() } @@ -2165,10 +2175,10 @@ export class ApplicationClient { } const txns = atc.buildGroup() return { - transaction: txns[txns.length - 1].txn, - confirmation: result.simulateResponse.txnGroups[0].txnResults.at(-1)?.txnResult, - confirmations: result.simulateResponse.txnGroups[0].txnResults.map((t) => t.txnResult), - transactions: txns.map((t) => t.txn), + transaction: new TransactionWrapper(txns[txns.length - 1].txn), + confirmation: wrapPendingTransactionResponseOptional(result.simulateResponse.txnGroups[0].txnResults.at(-1)?.txnResult), + confirmations: result.simulateResponse.txnGroups[0].txnResults.map((t) => wrapPendingTransactionResponse(t.txnResult)), + transactions: txns.map((t) => new TransactionWrapper(t.txn)), return: (result.methodResults?.length ?? 0 > 0) ? (result.methodResults[result.methodResults.length - 1] as ABIReturn) : undefined, } satisfies AppCallTransactionResult } @@ -2230,7 +2240,7 @@ export class ApplicationClient { */ async callOfType( call: AppClientCallParams = {}, - callType: Exclude | Exclude, + callType: Exclude | Exclude, ) { const { sender: callSender, note, sendParams, ...args } = call diff --git a/src/types/app-deployer.ts b/src/types/app-deployer.ts index 11db70a2..f177805b 100644 --- a/src/types/app-deployer.ts +++ b/src/types/app-deployer.ts @@ -1,4 +1,6 @@ -import algosdk, { Address } from 'algosdk' +import { TransactionType } from '@algorandfoundation/algokit-transact' +import * as algosdk from '@algorandfoundation/sdk' +import { Address } from '@algorandfoundation/sdk' import { Config } from '../config' import * as indexer from '../indexer-lookup' import { calculateExtraProgramPages } from '../util' @@ -310,14 +312,14 @@ export class AppDeployer { const deleteTransaction = result.transactions.at(-1)! Config.getLogger(sendParams?.suppressLog).warn( - `Sent transactions ${transaction.txID()} to create app with id ${confirmation.applicationIndex} and ${deleteTransaction.txID()} to delete app with id ${ + `Sent transactions ${transaction.txID()} to create app with id ${confirmation.appId} and ${deleteTransaction.txID()} to delete app with id ${ existingApp.appId } from ${createParams.sender} account.`, ) const appMetadata: AppMetadata = { - appId: BigInt(confirmation.applicationIndex!), - appAddress: algosdk.getApplicationAddress(confirmation.applicationIndex!), + appId: confirmation.appId!, + appAddress: algosdk.getApplicationAddress(confirmation.appId!), ...metadata, createdMetadata: metadata, createdRound: BigInt(confirmation.confirmedRound!), @@ -518,7 +520,7 @@ export class AppDeployer { const appTransactions = await indexer.searchTransactions(this._indexer!, (s) => s .minRound(createdApp.createdAtRound) - .txType(algosdk.TransactionType.appl) + .txType(TransactionType.AppCall) .applicationID(Number(createdApp.id)) .address(creatorAddress) .addressRole('sender') diff --git a/src/types/app-factory-and-client.spec.ts b/src/types/app-factory-and-client.spec.ts index 39eb52b2..c08a0497 100644 --- a/src/types/app-factory-and-client.spec.ts +++ b/src/types/app-factory-and-client.spec.ts @@ -1,4 +1,6 @@ -import algosdk, { ABIUintType, Address, OnApplicationComplete, TransactionSigner, TransactionType, getApplicationAddress } from 'algosdk' +import { OnApplicationComplete, TransactionType } from '@algorandfoundation/algokit-transact' +import * as algosdk from '@algorandfoundation/sdk' +import { ABIUintType, Address, TransactionSigner, getApplicationAddress } from '@algorandfoundation/sdk' import invariant from 'tiny-invariant' import { afterEach, beforeAll, beforeEach, describe, expect, it, test } from 'vitest' import * as algokit from '..' @@ -56,7 +58,7 @@ describe('ARC32: app-factory-and-app-client', () => { expect(app.appId).toBeGreaterThan(0n) expect(app.appAddress).toEqual(getApplicationAddress(app.appId)) - expect(app.confirmation?.applicationIndex ?? 0n).toBe(app.appId) + expect(app.confirmation?.appId ?? 0n).toBe(app.appId) expect(app.compiledApproval).toBeTruthy() }) @@ -81,7 +83,7 @@ describe('ARC32: app-factory-and-app-client', () => { test('Create app with oncomplete overload', async () => { const { result: app } = await factory.send.bare.create({ - onComplete: OnApplicationComplete.OptInOC, + onComplete: OnApplicationComplete.OptIn, updatable: true, deletable: true, deployTimeParams: { @@ -89,10 +91,10 @@ describe('ARC32: app-factory-and-app-client', () => { }, }) - expect(app.transaction.applicationCall?.onComplete).toBe(OnApplicationComplete.OptInOC) + expect(app.transaction.appCall?.onComplete).toBe(OnApplicationComplete.OptIn) expect(app.appId).toBeGreaterThan(0n) expect(app.appAddress).toEqual(getApplicationAddress(app.appId)) - expect(app.confirmation?.applicationIndex ?? 0n).toBe(app.appId) + expect(app.confirmation?.appId ?? 0n).toBe(app.appId) }) test('Deploy app - can still deploy when immutable and permanent', async () => { @@ -117,7 +119,7 @@ describe('ARC32: app-factory-and-app-client', () => { invariant(app.operationPerformed === 'create') expect(app.appId).toBeGreaterThan(0n) expect(app.appAddress).toEqual(getApplicationAddress(app.appId)) - expect(app.confirmation?.applicationIndex ?? 0n).toBe(app.appId) + expect(app.confirmation?.appId ?? 0n).toBe(app.appId) expect(app.compiledApproval).toBeTruthy() expect(app.compiledClear).toBeTruthy() }) @@ -136,7 +138,7 @@ describe('ARC32: app-factory-and-app-client', () => { invariant(app.operationPerformed === 'create') expect(app.appId).toBeGreaterThan(0) expect(app.appAddress).toEqual(getApplicationAddress(app.appId)) - expect(app.confirmation?.applicationIndex ?? 0n).toBe(app.appId) + expect(app.confirmation?.appId ?? 0n).toBe(app.appId) expect(app.return).toBe('arg_io') }) @@ -188,7 +190,7 @@ describe('ARC32: app-factory-and-app-client', () => { expect(app.createdRound).toBe(createdApp.createdRound) expect(app.updatedRound).not.toBe(app.createdRound) expect(app.updatedRound).toBe(app.confirmation.confirmedRound ?? 0n) - expect(app.transaction.applicationCall?.onComplete).toBe(OnApplicationComplete.UpdateApplicationOC) + expect(app.transaction.appCall?.onComplete).toBe(OnApplicationComplete.UpdateApplication) expect(app.return).toBe('arg_io') }) @@ -252,8 +254,8 @@ describe('ARC32: app-factory-and-app-client', () => { invariant(app.confirmation) invariant(app.deleteResult) invariant(app.deleteResult.confirmation) - expect(app.deleteResult.transaction.applicationCall?.appIndex).toBe(createdApp.appId) - expect(app.deleteResult.transaction.applicationCall?.onComplete).toBe(OnApplicationComplete.DeleteApplicationOC) + expect(app.deleteResult.transaction.appCall?.appId).toBe(createdApp.appId) + expect(app.deleteResult.transaction.appCall?.onComplete).toBe(OnApplicationComplete.DeleteApplication) }) test('Deploy app - replace (abi)', async () => { @@ -286,8 +288,8 @@ describe('ARC32: app-factory-and-app-client', () => { invariant(app.confirmation) invariant(app.deleteResult) invariant(app.deleteResult.confirmation) - expect(app.deleteResult.transaction.applicationCall?.appIndex).toBe(createdApp.appId) - expect(app.deleteResult.transaction.applicationCall?.onComplete).toBe(OnApplicationComplete.DeleteApplicationOC) + expect(app.deleteResult.transaction.appCall?.appId).toBe(createdApp.appId) + expect(app.deleteResult.transaction.appCall?.onComplete).toBe(OnApplicationComplete.DeleteApplication) expect(app.return).toBe('arg_io') expect(app.deleteReturn).toBe('arg2_io') }) @@ -415,7 +417,7 @@ describe('ARC32: app-factory-and-app-client', () => { }) const encoder = new TextEncoder() - expect(call.transactions[0].applicationCall?.boxes).toEqual([{ appIndex: 0n, name: encoder.encode('1') }]) + expect(call.transactions[0].appCall?.boxReferences).toEqual([{ appId: 0n, name: encoder.encode('1') }]) const call2 = await client.createTransaction.call({ method: 'call_abi', @@ -423,7 +425,7 @@ describe('ARC32: app-factory-and-app-client', () => { boxReferences: ['1'], }) - expect(call2.transactions[0].applicationCall?.boxes).toEqual([{ appIndex: 0n, name: encoder.encode('1') }]) + expect(call2.transactions[0].appCall?.boxReferences).toEqual([{ appId: 0n, name: encoder.encode('1') }]) }) test('Construct transaction with abi encoding including transaction', async () => { @@ -615,7 +617,7 @@ describe('ARC32: app-factory-and-app-client', () => { }) expect(result.transaction.payment?.amount).toBe(fundAmount.microAlgo) - expect(result.transaction.type).toBe(TransactionType.pay) + expect(result.transaction.type).toBe(TransactionType.Payment) expect(result.transaction.payment?.receiver?.toString()).toBe(app.appAddress.toString()) expect(result.transaction.sender.toString()).toBe(testAccount.toString()) invariant(result.confirmation) @@ -905,7 +907,7 @@ retsub ;({ appClient } = await factory.send.create({ extraFee: algokit.microAlgos(1000), method: 'createApplication', - onComplete: OnApplicationComplete.OptInOC, + onComplete: OnApplicationComplete.OptIn, })) await algorand.account.ensureFunded(appClient.appAddress, testAccount, algokit.microAlgos(251200)) diff --git a/src/types/app-factory.ts b/src/types/app-factory.ts index 445a5b0d..e803316c 100644 --- a/src/types/app-factory.ts +++ b/src/types/app-factory.ts @@ -1,4 +1,5 @@ -import algosdk, { Address } from 'algosdk' +import { OnApplicationComplete } from '@algorandfoundation/algokit-transact' +import { ABIValue, Address, ProgramSourceMap, TransactionSigner } from '@algorandfoundation/sdk' import { TransactionSignerAccount } from './account' import { type AlgorandClient } from './algorand-client' import { @@ -38,10 +39,6 @@ import { AppSpec } from './app-spec' import { AppCreateMethodCall, AppCreateParams, AppMethodCall, AppMethodCallTransactionArgument, CommonAppCallParams } from './composer' import { Expand } from './expand' import { SendParams } from './transaction' -import SourceMap = algosdk.ProgramSourceMap -import OnApplicationComplete = algosdk.OnApplicationComplete -import ABIValue = algosdk.ABIValue -import TransactionSigner = algosdk.TransactionSigner /** Parameters to create an app client */ export interface AppFactoryParams { @@ -100,7 +97,7 @@ export interface AppFactoryParams { /** onComplete parameter for a create app call */ export type CreateOnComplete = { - onComplete?: Exclude + onComplete?: Exclude } /** Specifies a schema used for creating an app */ @@ -178,8 +175,8 @@ export class AppFactory { private _updatable?: boolean private _deletable?: boolean - private _approvalSourceMap: SourceMap | undefined - private _clearSourceMap: SourceMap | undefined + private _approvalSourceMap: ProgramSourceMap | undefined + private _clearSourceMap: ProgramSourceMap | undefined private _paramsMethods: ReturnType @@ -516,8 +513,8 @@ export class AppFactory { * @param sourceMaps The source maps to import */ importSourceMaps(sourceMaps: AppSourceMaps) { - this._approvalSourceMap = new SourceMap(sourceMaps.approvalSourceMap) - this._clearSourceMap = new SourceMap(sourceMaps.clearSourceMap) + this._approvalSourceMap = new ProgramSourceMap(sourceMaps.approvalSourceMap) + this._clearSourceMap = new ProgramSourceMap(sourceMaps.clearSourceMap) } private getDeployTimeControl(control: 'updatable' | 'deletable'): boolean | undefined { @@ -552,16 +549,16 @@ export class AppFactory { approvalProgram: compiled.approvalProgram, clearStateProgram: compiled.clearStateProgram, }, - params.onComplete ?? OnApplicationComplete.NoOpOC, + params.onComplete ?? OnApplicationComplete.NoOp, ) satisfies AppCreateMethodCall }, /** Return params for a deployment update ABI call */ deployUpdate: (params: AppClientMethodCallParams) => { - return this.getABIParams(params, OnApplicationComplete.UpdateApplicationOC) satisfies DeployAppUpdateMethodCall + return this.getABIParams(params, OnApplicationComplete.UpdateApplication) satisfies DeployAppUpdateMethodCall }, /** Return params for a deployment delete ABI call */ deployDelete: (params: AppClientMethodCallParams) => { - return this.getABIParams(params, OnApplicationComplete.DeleteApplicationOC) satisfies DeployAppDeleteMethodCall + return this.getABIParams(params, OnApplicationComplete.DeleteApplication) satisfies DeployAppDeleteMethodCall }, bare: { /** Return params for a create bare call, including deploy-time TEAL template replacements and compilation if provided */ @@ -578,16 +575,16 @@ export class AppFactory { }, ...(await this.compile({ ...params, deployTimeParams: params?.deployTimeParams ?? this._deployTimeParams })), }, - params?.onComplete ?? OnApplicationComplete.NoOpOC, + params?.onComplete ?? OnApplicationComplete.NoOp, ) satisfies AppCreateParams }, /** Return params for a deployment update bare call */ deployUpdate: (params?: AppClientBareCallParams) => { - return this.getBareParams(params, OnApplicationComplete.UpdateApplicationOC) satisfies DeployAppUpdateParams + return this.getBareParams(params, OnApplicationComplete.UpdateApplication) satisfies DeployAppUpdateParams }, /** Return params for a deployment delete bare call */ deployDelete: (params?: AppClientBareCallParams) => { - return this.getBareParams(params, OnApplicationComplete.DeleteApplicationOC) satisfies DeployAppDeleteParams + return this.getBareParams(params, OnApplicationComplete.DeleteApplication) satisfies DeployAppDeleteParams }, }, } diff --git a/src/types/app-manager.ts b/src/types/app-manager.ts index 3dd7905f..8ba48393 100644 --- a/src/types/app-manager.ts +++ b/src/types/app-manager.ts @@ -1,4 +1,7 @@ -import algosdk, { Address, ProgramSourceMap } from 'algosdk' +import { AlgodClient, EvalDelta, PendingTransactionResponse, TealValue } from '@algorandfoundation/algokit-algod-client' +import { BoxReference as TransactionBoxReference } from '@algorandfoundation/algokit-transact' +import * as algosdk from '@algorandfoundation/sdk' +import { Address, ProgramSourceMap } from '@algorandfoundation/sdk' import { getABIReturnValue } from '../transaction/transaction' import { TransactionSignerAccount } from './account' import { @@ -10,7 +13,6 @@ import { type CompiledTeal, type TealTemplateParams, } from './app' -import modelsv2 = algosdk.modelsv2 /** Information about an app. */ export interface AppInformation { @@ -94,54 +96,16 @@ export interface BoxValuesRequestParams { type: algosdk.ABIType } -/** - * Defines a holding by referring to an Address and Asset it belongs to. - */ -export interface HoldingReference { - /** Asset ID for asset in access list. */ - assetId: bigint - /** Address in access list, or the sender of the transaction. */ - address: string | Address -} - -/** - * Defines a local state by referring to an Address and App it belongs to. - */ -export interface LocalsReference { - /** Application ID for app in access list, or zero if referring to the called application. */ - appId: bigint - /** Address in access list, or the sender of the transaction. */ - address: string | Address -} - -/** - * Names a single resource reference. Only one of the fields should be set. - */ -export interface AccessReference { - /** Any account addresses whose balance record is accessible by the executing ApprovalProgram or ClearStateProgram. */ - address?: string | Address - /** Application ID whose GlobalState may be read by the executing ApprovalProgram or ClearStateProgram. */ - appId?: bigint - /** Asset ID whose AssetParams may be read by the executing ApprovalProgram or ClearStateProgram. */ - assetId?: bigint - /** Defines a holding by referring to an Address and Asset it belongs to. */ - holding?: HoldingReference - /** Defines a local state by referring to an Address and App it belongs to. */ - locals?: LocalsReference - /** Defines a box by its name and the application ID it belongs to. */ - box?: BoxReference -} - /** Allows management of application information. */ export class AppManager { - private _algod: algosdk.Algodv2 + private _algod: AlgodClient private _compilationResults: Record = {} /** * Creates an `AppManager` * @param algod An algod instance */ - constructor(algod: algosdk.Algodv2) { + constructor(algod: AlgodClient) { this._algod = algod } @@ -165,13 +129,13 @@ export class AppManager { return this._compilationResults[tealCode] } - const compiled = await this._algod.compile(tealCode).sourcemap(true).do() + const compiled = await this._algod.tealCompile({ body: tealCode, sourcemap: true }) const result = { teal: tealCode, compiled: compiled.result, compiledHash: compiled.hash, compiledBase64ToBytes: new Uint8Array(Buffer.from(compiled.result, 'base64')), - sourceMap: new ProgramSourceMap(JSON.parse(algosdk.encodeJSON(compiled.sourcemap!))), + sourceMap: new ProgramSourceMap(compiled.sourcemap as { version: number; sources: string[]; names: string[]; mappings: string }), } this._compilationResults[tealCode] = result @@ -238,19 +202,25 @@ export class AppManager { * @returns The app information */ public async getById(appId: bigint): Promise { - const app = await this._algod.getApplicationByID(Number(appId)).do() + const app = await this._algod.getApplicationById(appId) + // Convert global state from new format (key: string) to old format (key: Uint8Array) + const convertedGlobalState = (app.params.globalState ?? []).map((kv) => ({ + key: new Uint8Array(Buffer.from(kv.key, 'base64')), + value: kv.value, + })) + return { appId: BigInt(app.id), appAddress: algosdk.getApplicationAddress(app.id), approvalProgram: app.params.approvalProgram, clearStateProgram: app.params.clearStateProgram, - creator: app.params.creator, + creator: Address.fromString(app.params.creator), localInts: Number(app.params.localStateSchema?.numUint ?? 0), localByteSlices: Number(app.params.localStateSchema?.numByteSlice ?? 0), globalInts: Number(app.params.globalStateSchema?.numUint ?? 0), globalByteSlices: Number(app.params.globalStateSchema?.numByteSlice ?? 0), extraProgramPages: Number(app.params.extraProgramPages ?? 0), - globalState: AppManager.decodeAppState(app.params.globalState ?? []), + globalState: AppManager.decodeAppState(convertedGlobalState), } } @@ -280,13 +250,23 @@ export class AppManager { * ``` */ public async getLocalState(appId: bigint, address: Address | string) { - const appInfo = await this._algod.accountApplicationInformation(address, appId).do() + const appInfo = await this._algod.accountApplicationInformation(address.toString(), Number(appId)) - if (!appInfo.appLocalState?.keyValue) { + if (!appInfo.appLocalState) { throw new Error("Couldn't find local state") } - return AppManager.decodeAppState(appInfo.appLocalState.keyValue) + // If keyValue is undefined or empty, return empty state + if (!appInfo.appLocalState.keyValue || appInfo.appLocalState.keyValue.length === 0) { + return {} + } + + const convertedState = appInfo.appLocalState.keyValue.map((kv) => ({ + key: new Uint8Array(Buffer.from(kv.key, 'base64')), + value: kv.value, + })) + + return AppManager.decodeAppState(convertedState) } /** @@ -299,8 +279,8 @@ export class AppManager { * ``` */ public async getBoxNames(appId: bigint): Promise { - const boxResult = await this._algod.getApplicationBoxes(appId).do() - return boxResult.boxes.map((b) => { + const boxResult = await this._algod.getApplicationBoxes(appId) + return boxResult.boxes.map((b: { name: Uint8Array }) => { return { nameRaw: b.name, nameBase64: Buffer.from(b.name).toString('base64'), @@ -321,8 +301,9 @@ export class AppManager { */ public async getBoxValue(appId: bigint, boxName: BoxIdentifier | BoxName): Promise { const boxId = typeof boxName === 'object' && 'nameRaw' in boxName ? boxName.nameRaw : boxName - const name = AppManager.getBoxReference(boxId).name - const boxResult = await this._algod.getApplicationBoxByName(Number(appId), name).do() + const nameBytes = AppManager.getBoxReference(boxId).name + const nameBase64 = Buffer.from(nameBytes).toString('base64') + const boxResult = await this._algod.getApplicationBoxByName(Number(appId), { name: `b64:${nameBase64}` }) return boxResult.value } @@ -378,12 +359,12 @@ export class AppManager { * const boxRef = AppManager.getBoxReference('boxName'); * ``` */ - public static getBoxReference(boxId: BoxIdentifier | BoxReference): algosdk.BoxReference { + public static getBoxReference(boxId: BoxIdentifier | BoxReference): TransactionBoxReference { const ref = typeof boxId === 'object' && 'appId' in boxId ? boxId : { appId: 0n, name: boxId } return { - appIndex: ref.appId, + appId: ref.appId, name: typeof ref.name === 'string' ? new TextEncoder().encode(ref.name) : 'length' in ref.name ? ref.name : ref.name.addr.publicKey, - } as algosdk.BoxReference + } as TransactionBoxReference } /** @@ -396,7 +377,7 @@ export class AppManager { * const stateValues = AppManager.decodeAppState(state); * ``` */ - public static decodeAppState(state: { key: Uint8Array; value: modelsv2.TealValue | modelsv2.EvalDelta }[]): AppState { + public static decodeAppState(state: { key: Uint8Array; value: TealValue | EvalDelta }[]): AppState { const stateValues = {} as AppState // Start with empty set @@ -406,7 +387,7 @@ export class AppManager { const key = Buffer.from(stateVal.key).toString('utf-8') const tealValue = stateVal.value - const dataTypeFlag = 'action' in tealValue ? tealValue.action : tealValue.type + const dataTypeFlag = Number('action' in tealValue ? tealValue.action : tealValue.type) let valueBase64: string let valueRaw: Buffer switch (dataTypeFlag) { @@ -450,7 +431,7 @@ export class AppManager { * ``` */ public static getABIReturn( - confirmation: modelsv2.PendingTransactionResponse | undefined, + confirmation: PendingTransactionResponse | undefined, method: algosdk.ABIMethod | undefined, ): ABIReturn | undefined { if (!method || !confirmation || method.returns.type === 'void') { @@ -573,34 +554,6 @@ export class AppManager { } } -function getHoldingReference(holdingReference: HoldingReference): algosdk.TransactionHoldingReference { - return { - assetIndex: holdingReference.assetId, - address: typeof holdingReference.address === 'string' ? Address.fromString(holdingReference.address) : holdingReference.address!, - } satisfies algosdk.TransactionHoldingReference -} - -function getLocalsReference(localsReference: LocalsReference): algosdk.TransactionLocalsReference { - return { - appIndex: localsReference.appId, - address: typeof localsReference.address === 'string' ? Address.fromString(localsReference.address) : localsReference.address!, - } satisfies algosdk.TransactionLocalsReference -} - -/** - * Returns an `algosdk.TransactionResourceReference` given a `AccessReference`. - */ -export function getAccessReference(accessReference: AccessReference): algosdk.TransactionResourceReference { - return { - address: typeof accessReference.address === 'string' ? Address.fromString(accessReference.address) : accessReference.address, - appIndex: accessReference.appId, - assetIndex: accessReference.assetId, - holding: accessReference.holding ? getHoldingReference(accessReference.holding) : undefined, - locals: accessReference.locals ? getLocalsReference(accessReference.locals) : undefined, - box: accessReference.box ? AppManager.getBoxReference(accessReference.box) : undefined, - } as algosdk.TransactionResourceReference -} - /** * Find the first string within a line of TEAL. Only matches outside of quotes and base64 are returned. * Returns undefined if not found diff --git a/src/types/app-spec.ts b/src/types/app-spec.ts index d91c7fe0..fb3de0b7 100644 --- a/src/types/app-spec.ts +++ b/src/types/app-spec.ts @@ -1,8 +1,6 @@ -import algosdk from 'algosdk' +import * as algosdk from '@algorandfoundation/sdk' +import { ABIContractParams, ABIMethod, ABIMethodParams } from '@algorandfoundation/sdk' import { Arc56Contract, Method as Arc56Method, StorageKey, StructField } from './app-arc56' -import ABIContractParams = algosdk.ABIContractParams -import ABIMethodParams = algosdk.ABIMethodParams -import ABIMethod = algosdk.ABIMethod /** * Converts an ARC-32 Application Specification to an ARC-56 Contract diff --git a/src/types/app.ts b/src/types/app.ts index bb148776..3a31ecd7 100644 --- a/src/types/app.ts +++ b/src/types/app.ts @@ -1,4 +1,6 @@ -import algosdk from 'algosdk' +import { TransactionParams } from '@algorandfoundation/algokit-algod-client' +import { OnApplicationComplete, BoxReference as TransactBoxReference, Transaction } from '@algorandfoundation/algokit-transact' +import { ABIArgument, ABIMethod, ABIMethodParams, ABIType, ABIValue, Address, ProgramSourceMap } from '@algorandfoundation/sdk' import { Expand } from './expand' import { SendSingleTransactionResult, @@ -9,16 +11,7 @@ import { TransactionNote, TransactionToSign, } from './transaction' -import ABIArgument = algosdk.ABIArgument -import ABIMethod = algosdk.ABIMethod -import ABIMethodParams = algosdk.ABIMethodParams -import ABIType = algosdk.ABIType -import ABIValue = algosdk.ABIValue -import Address = algosdk.Address -import OnApplicationComplete = algosdk.OnApplicationComplete -import SourceMap = algosdk.ProgramSourceMap -import SuggestedParams = algosdk.SuggestedParams -import Transaction = algosdk.Transaction +type SourceMap = ProgramSourceMap /** The name of the TEAL template variable for deploy-time immutability control */ export const UPDATABLE_TEMPLATE_NAME = 'TMPL_UPDATABLE' @@ -74,7 +67,7 @@ export interface CoreAppCallArgs { /** The optional lease for the transaction */ lease?: string | Uint8Array /** Any box references to load */ - boxes?: (algosdk.BoxReference | BoxReference | BoxIdentifier)[] + boxes?: (TransactBoxReference | BoxReference | BoxIdentifier)[] /** The address of any accounts to load in */ accounts?: (string | Address)[] /** IDs of any apps to load into the foreignApps array */ @@ -136,7 +129,7 @@ interface CreateOrUpdateAppParams extends SendTransactionParams { /** The clear state program as raw teal (string) or compiled teal, base 64 encoded as a byte array (Uint8Array) */ clearStateProgram: Uint8Array | string /** Optional transaction parameters */ - transactionParams?: SuggestedParams + transactionParams?: TransactionParams /** The (optional) transaction note */ note?: TransactionNote /** The arguments passed in to the app call */ @@ -151,7 +144,7 @@ export interface CreateAppParams extends CreateOrUpdateAppParams { /** The storage schema to request for the created app */ schema: AppStorageSchema /** Override the on-completion action for the create call; defaults to NoOp */ - onCompleteAction?: Exclude | Exclude + onCompleteAction?: Exclude | Exclude } /** @@ -164,11 +157,11 @@ export interface UpdateAppParams extends CreateOrUpdateAppParams { } /** - * @deprecated Use `algosdk.OnApplicationComplete` directly instead. + * @deprecated Use `OnApplicationComplete` directly instead. * * The type of call / [on-completion action](https://dev.algorand.co/concepts/smart-contracts/overview#smart-contract-lifecycle) for a smart contract call. * - * Equivalent of `algosdk.OnApplicationComplete`, but as a more convenient string enum. + * Equivalent of `OnApplicationComplete`, but as a more convenient string enum. * * * `no_op`: Normal smart contract call, no special on-complete action * * `opt_in`: Opt-in to smart contract local storage @@ -184,11 +177,11 @@ export interface AppCallParams extends SendTransactionParams { /** The id of the app to call */ appId: number | bigint /** The type of call, everything except create (see `createApp`) and update (see `updateApp`) */ - callType: Exclude | Exclude + callType: Exclude | Exclude /** The account to make the call from */ from: SendTransactionFrom /** Optional transaction parameters */ - transactionParams?: SuggestedParams + transactionParams?: TransactionParams /** The (optional) transaction note */ note?: TransactionNote /** The arguments passed in to the app call */ @@ -322,7 +315,7 @@ export interface AppDeploymentParams /** Any args to pass to any create transaction that is issued as part of deployment */ createArgs?: AppCallArgs /** Override the on-completion action for the create call; defaults to NoOp */ - createOnCompleteAction?: Exclude | Exclude + createOnCompleteAction?: Exclude | Exclude /** Any args to pass to any update transaction that is issued as part of deployment */ updateArgs?: AppCallArgs /** Any args to pass to any delete transaction that is issued as part of deployment */ diff --git a/src/types/asset-manager.ts b/src/types/asset-manager.ts index 03b8cc35..5146fc32 100644 --- a/src/types/asset-manager.ts +++ b/src/types/asset-manager.ts @@ -1,4 +1,5 @@ -import algosdk, { Address } from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' +import { Address } from '@algorandfoundation/sdk' import { Config } from '../config' import { chunkArray } from '../util' import { AccountAssetInformation } from './account' @@ -136,7 +137,7 @@ export interface AssetInformation { /** Allows management of asset information. */ export class AssetManager { - private _algod: algosdk.Algodv2 + private _algod: AlgodClient private _newGroup: () => TransactionComposer /** @@ -148,7 +149,7 @@ export class AssetManager { * const assetManager = new AssetManager(algod, () => new TransactionComposer({algod, () => signer, () => suggestedParams})) * ``` */ - constructor(algod: algosdk.Algodv2, newGroup: () => TransactionComposer) { + constructor(algod: AlgodClient, newGroup: () => TransactionComposer) { this._algod = algod this._newGroup = newGroup } @@ -165,7 +166,7 @@ export class AssetManager { * @returns The asset information */ public async getById(assetId: bigint): Promise { - const asset = await this._algod.getAssetByID(Number(assetId)).do() + const asset = await this._algod.getAssetById(assetId) return { assetId: BigInt(asset.index), @@ -203,7 +204,7 @@ export class AssetManager { * @returns The account asset holding information */ public async getAccountInformation(sender: string | Address, assetId: bigint): Promise { - const info = await this._algod.accountAssetInformation(sender, Number(assetId)).do() + const info = await this._algod.accountAssetInformation(sender.toString(), assetId) return { assetId: BigInt(assetId), diff --git a/src/types/asset.ts b/src/types/asset.ts index b3d140a7..e28f11c4 100644 --- a/src/types/asset.ts +++ b/src/types/asset.ts @@ -1,7 +1,6 @@ -import algosdk from 'algosdk' +import { SdkTransactionParams } from '@algorandfoundation/sdk' import { AlgoAmount } from './amount' import { SendTransactionFrom, SendTransactionParams, TransactionNote } from './transaction' -import SuggestedParams = algosdk.SuggestedParams /** @deprecated Parameters for `createAsset` call. */ export interface CreateAssetParams extends SendTransactionParams { @@ -59,7 +58,7 @@ export interface CreateAssetParams extends SendTransactionParams { frozenByDefault?: boolean /** Optional transaction parameters */ - transactionParams?: SuggestedParams + transactionParams?: SdkTransactionParams /** The (optional) transaction note */ note?: TransactionNote /** An (optional) [transaction lease](https://dev.algorand.co/concepts/transactions/leases) to apply */ @@ -73,7 +72,7 @@ export interface AssetOptInParams extends SendTransactionParams { /** The ID of the assets to opt in for / out of */ assetId: number /** Optional transaction parameters */ - transactionParams?: SuggestedParams + transactionParams?: SdkTransactionParams /** The (optional) transaction note */ note?: TransactionNote /** An (optional) [transaction lease](https://dev.algorand.co/concepts/transactions/leases) to apply */ @@ -97,7 +96,7 @@ export interface AssetBulkOptInOutParams { /** Whether or not to validate the opt-in/out is valid before issuing transactions; default = true */ validateBalances?: boolean /** Optional transaction parameters */ - transactionParams?: SuggestedParams + transactionParams?: SdkTransactionParams /** The (optional) transaction note */ note?: TransactionNote /** The maximum fee that you are happy to pay per transaction (default: unbounded) - if this is set it's possible the transaction could get rejected during network congestion */ diff --git a/src/types/client-manager.spec.ts b/src/types/client-manager.spec.ts index 379bfe42..9620b0c1 100644 --- a/src/types/client-manager.spec.ts +++ b/src/types/client-manager.spec.ts @@ -44,7 +44,7 @@ describe('ClientManager', () => { const response = await Promise.all( new Array(150).fill(0).map(async (_) => { - return await algod.accountInformation('XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA').do() + return await algod.accountInformation('XBYLS2E6YI6XXL5BWCAMOA4GTWHXWENZMX5UHXMRNWWUQ7BXCY5WC5TEPA') }), ) expect(response.length).toBe(150) @@ -121,7 +121,7 @@ describe('ClientManager', () => { test('Get working LocalNet algod client', async () => { const algod = ClientManager.getAlgodClient(ClientManager.getDefaultLocalNetConfig('algod')) - await algod.status().do() + await algod.getStatus() }) test('Get working LocalNet indexer client', async () => { @@ -136,7 +136,7 @@ describe('ClientManager', () => { test('Get working MainNet algod client', async () => { const algod = ClientManager.getAlgodClient(ClientManager.getAlgoNodeConfig('mainnet', 'algod')) - await algod.status().do() + await algod.getStatus() }) test('Get working MainNet indexer client', async () => { diff --git a/src/types/client-manager.ts b/src/types/client-manager.ts index cd11ae84..5ba127e2 100644 --- a/src/types/client-manager.ts +++ b/src/types/client-manager.ts @@ -1,4 +1,6 @@ -import algosdk, { SuggestedParams } from 'algosdk' +import { AlgodClient, TransactionParams } from '@algorandfoundation/algokit-algod-client' +import * as algosdk from '@algorandfoundation/sdk' +import { Indexer, Kmd } from '@algorandfoundation/sdk' import { AlgoHttpClientWithRetry } from './algo-http-client-with-retry' import { type AlgorandClient } from './algorand-client' import { AppClient, AppClientParams, ResolveAppClientByCreatorAndName } from './app-client' @@ -6,14 +8,11 @@ import { AppFactory, AppFactoryParams } from './app-factory' import { TestNetDispenserApiClient, TestNetDispenserApiClientParams } from './dispenser-client' import { Expand } from './expand' import { AlgoClientConfig, AlgoConfig, NetworkDetails, genesisIdIsLocalNet } from './network-client' -import Kmd = algosdk.Kmd -import Indexer = algosdk.Indexer -import Algodv2 = algosdk.Algodv2 /** Clients from algosdk that interact with the official Algorand APIs */ export interface AlgoSdkClients { /** Algod client, see https://dev.algorand.co/reference/rest-apis/algod/ */ - algod: algosdk.Algodv2 + algod: AlgodClient /** Optional indexer client, see https://dev.algorand.co/reference/rest-apis/indexer */ indexer?: algosdk.Indexer /** Optional KMD client, see https://dev.algorand.co/reference/rest-apis/kmd/ */ @@ -46,7 +45,7 @@ export type ClientTypedAppFactoryParams = Expand | undefined + private _getNetworkPromise: Promise | undefined /** * Get details about the current network. * @example Getting genesis ID @@ -134,15 +133,15 @@ export class ClientManager { */ public async network(): Promise { if (!this._getNetworkPromise) { - this._getNetworkPromise = this._algod.getTransactionParams().do() + this._getNetworkPromise = this._algod.transactionParams() } const params = await this._getNetworkPromise return { - isTestNet: ['testnet-v1.0', 'testnet-v1', 'testnet'].includes(params.genesisID ?? 'unknown'), - isMainNet: ['mainnet-v1.0', 'mainnet-v1', 'mainnet'].includes(params.genesisID ?? 'unknown'), - isLocalNet: ClientManager.genesisIdIsLocalNet(params.genesisID ?? 'unknown'), - genesisId: params.genesisID ?? 'unknown', + isTestNet: ['testnet-v1.0', 'testnet-v1', 'testnet'].includes(params.genesisId ?? 'unknown'), + isMainNet: ['mainnet-v1.0', 'mainnet-v1', 'mainnet'].includes(params.genesisId ?? 'unknown'), + isLocalNet: ClientManager.genesisIdIsLocalNet(params.genesisId ?? 'unknown'), + genesisId: params.genesisId ?? 'unknown', genesisHash: params.genesisHash ? Buffer.from(params.genesisHash).toString('base64') : 'unknown', } } @@ -587,11 +586,16 @@ export class ClientManager { * await algod.healthCheck().do() * ``` */ - public static getAlgodClient(config: AlgoClientConfig): Algodv2 { + public static getAlgodClient(config: AlgoClientConfig): AlgodClient { const { token, server, port } = config - const tokenHeader = typeof token === 'string' ? { 'X-Algo-API-Token': token } : (token ?? {}) - const httpClientWithRetry = new AlgoHttpClientWithRetry(tokenHeader, server, port) - return new algosdk.Algodv2(httpClientWithRetry, server) + const baseUrl = port !== undefined ? `${server}:${port}` : server + + const tokenHeader: algosdk.TokenHeader = typeof token === 'string' ? { 'X-Algo-API-Token': token } : (token ?? {}) + + return new AlgodClient({ + baseUrl: baseUrl, + headers: { ...tokenHeader }, + }) } /** @@ -605,7 +609,7 @@ export class ClientManager { * await algod.healthCheck().do() * ``` */ - public static getAlgodClientFromEnvironment(): Algodv2 { + public static getAlgodClientFromEnvironment(): AlgodClient { return ClientManager.getAlgodClient(ClientManager.getAlgodConfigFromEnvironment()) } diff --git a/src/types/composer.ts b/src/types/composer.ts index 87769d3a..d4e89c99 100644 --- a/src/types/composer.ts +++ b/src/types/composer.ts @@ -1,25 +1,31 @@ -import algosdk, { ABIMethod, Address } from 'algosdk' +import { AlgodClient, SimulateRequest, SimulateTransaction, TransactionParams } from '@algorandfoundation/algokit-algod-client' +import { AccessReference, OnApplicationComplete, Transaction, assignFee, getTransactionId } from '@algorandfoundation/algokit-transact' +import * as algosdk from '@algorandfoundation/sdk' +import { + ABIMethod, + Address, + AtomicTransactionComposer, + SdkTransactionParams, + TransactionSigner, + TransactionWithSigner, + isTransactionWithSigner, +} from '@algorandfoundation/sdk' import { Config } from '../config' import { encodeLease, getABIReturnValue, sendAtomicTransactionComposer } from '../transaction/transaction' import { asJson, calculateExtraProgramPages } from '../util' import { TransactionSignerAccount } from './account' import { AlgoAmount } from './amount' -import { AccessReference, AppManager, BoxIdentifier, BoxReference, getAccessReference } from './app-manager' +import { AppManager, BoxIdentifier, BoxReference } from './app-manager' import { Expand } from './expand' import { EventType } from './lifecycle-events' import { genesisIdIsLocalNet } from './network-client' -import { Arc2TransactionNote, SendAtomicTransactionComposerResults, SendParams } from './transaction' -import AtomicTransactionComposer = algosdk.AtomicTransactionComposer -import Transaction = algosdk.Transaction -import TransactionSigner = algosdk.TransactionSigner -import TransactionWithSigner = algosdk.TransactionWithSigner -import isTransactionWithSigner = algosdk.isTransactionWithSigner -import SimulateResponse = algosdk.modelsv2.SimulateResponse -import modelsv2 = algosdk.modelsv2 - -const address = (address: string | Address): Address => { - return typeof address === 'string' ? Address.fromString(address) : address -} +import { + Arc2TransactionNote, + SendAtomicTransactionComposerResults, + SendParams, + TransactionWrapper, + wrapPendingTransactionResponse, +} from './transaction' export const MAX_TRANSACTION_GROUP_SIZE = 16 @@ -36,7 +42,7 @@ export type SkipSignaturesSimulateOptions = Expand< /** The raw API options to control a simulate request. * See algod API docs for more information: https://dev.algorand.co/reference/rest-apis/algod/#simulatetransaction */ -export type RawSimulateOptions = Expand[0], 'txnGroups'>> +export type RawSimulateOptions = Expand> /** All options to control a simulate request */ export type SimulateOptions = Expand & RawSimulateOptions> @@ -344,7 +350,7 @@ export type CommonAppCallParams = CommonTransactionParams & { /** ID of the application; 0 if the application is being created. */ appId: bigint /** The [on-complete](https://dev.algorand.co/concepts/smart-contracts/avm#oncomplete) action of the call; defaults to no-op. */ - onComplete?: algosdk.OnApplicationComplete + onComplete?: OnApplicationComplete /** Any [arguments to pass to the smart contract call](/concepts/smart-contracts/languages/teal/#argument-passing). */ args?: Uint8Array[] /** Any account addresses to add to the [accounts array](https://dev.algorand.co/concepts/smart-contracts/resource-usage#what-are-reference-arrays). */ @@ -366,7 +372,7 @@ export type CommonAppCallParams = CommonTransactionParams & { /** Parameters to define an app create transaction */ export type AppCreateParams = Expand< Omit & { - onComplete?: Exclude + onComplete?: Exclude /** The program to execute for all OnCompletes other than ClearState as raw teal that will be compiled (string) or compiled teal (encoded as a byte array (Uint8Array)). */ approvalProgram: string | Uint8Array /** The program to execute for ClearState OnComplete as raw teal that will be compiled (string) or compiled teal (encoded as a byte array (Uint8Array)). */ @@ -392,7 +398,7 @@ export type AppCreateParams = Expand< /** Parameters to define an app update transaction */ export type AppUpdateParams = Expand< CommonAppCallParams & { - onComplete?: algosdk.OnApplicationComplete.UpdateApplicationOC + onComplete?: OnApplicationComplete.UpdateApplication /** The program to execute for all OnCompletes other than ClearState as raw teal (string) or compiled teal (base 64 encoded as a byte array (Uint8Array)) */ approvalProgram: string | Uint8Array /** The program to execute for ClearState OnComplete as raw teal (string) or compiled teal (base 64 encoded as a byte array (Uint8Array)) */ @@ -402,20 +408,17 @@ export type AppUpdateParams = Expand< /** Parameters to define an application call transaction. */ export type AppCallParams = CommonAppCallParams & { - onComplete?: Exclude + onComplete?: Exclude } /** Common parameters to define an ABI method call transaction. */ export type AppMethodCallParams = CommonAppCallParams & { - onComplete?: Exclude< - algosdk.OnApplicationComplete, - algosdk.OnApplicationComplete.UpdateApplicationOC | algosdk.OnApplicationComplete.ClearStateOC - > + onComplete?: Exclude } /** Parameters to define an application delete call transaction. */ export type AppDeleteParams = CommonAppCallParams & { - onComplete?: algosdk.OnApplicationComplete.DeleteApplicationOC + onComplete?: OnApplicationComplete.DeleteApplication } /** Parameters to define an ABI method call create transaction. */ @@ -500,11 +503,11 @@ class ErrorTransformerError extends Error { /** Parameters to create an `TransactionComposer`. */ export type TransactionComposerParams = { /** The algod client to use to get suggestedParams and send the transaction group */ - algod: algosdk.Algodv2 + algod: AlgodClient /** The function used to get the TransactionSigner for a given address */ getSigner: (address: string | Address) => algosdk.TransactionSigner /** The method used to get SuggestedParams for transactions in the group */ - getSuggestedParams?: () => Promise + getSuggestedParams?: () => Promise /** How many rounds a transaction should be valid for by default; if not specified * then will be 10 rounds (or 1000 rounds if issuing transactions to LocalNet). */ @@ -538,7 +541,7 @@ type TransactionWithSignerAndContext = algosdk.TransactionWithSigner & Transacti /** Set of transactions built by `TransactionComposer`. */ export interface BuiltTransactions { /** The built transactions */ - transactions: algosdk.Transaction[] + transactions: Transaction[] /** Any `ABIMethod` objects associated with any of the transactions in a map keyed by transaction index. */ methodCalls: Map /** Any `TransactionSigner` objects associated with any of the transactions in a map keyed by transaction index. */ @@ -562,10 +565,10 @@ export class TransactionComposer { private txns: Txn[] = [] /** The algod client used by the composer. */ - private algod: algosdk.Algodv2 + private algod: AlgodClient /** An async function that will return suggested params for the transaction. */ - private getSuggestedParams: () => Promise + private getSuggestedParams: () => Promise /** A function that takes in an address and return a signer function for that address. */ private getSigner: (address: string | Address) => algosdk.TransactionSigner @@ -609,7 +612,7 @@ export class TransactionComposer { */ constructor(params: TransactionComposerParams) { this.algod = params.algod - const defaultGetSuggestedParams = () => params.algod.getTransactionParams().do() + const defaultGetSuggestedParams = () => params.algod.transactionParams() this.getSuggestedParams = params.getSuggestedParams ?? defaultGetSuggestedParams this.getSigner = params.getSigner this.defaultValidityWindow = params.defaultValidityWindow ?? this.defaultValidityWindow @@ -959,7 +962,7 @@ export class TransactionComposer { * localByteSlices: 4 * }, * extraProgramPages: 1, - * onComplete: algosdk.OnApplicationComplete.OptInOC, + * onComplete: OnApplicationComplete.OptIn, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -1007,7 +1010,7 @@ export class TransactionComposer { * sender: 'CREATORADDRESS', * approvalProgram: "TEALCODE", * clearStateProgram: "TEALCODE", - * onComplete: algosdk.OnApplicationComplete.UpdateApplicationOC, + * onComplete: OnApplicationComplete.UpdateApplication, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -1028,7 +1031,7 @@ export class TransactionComposer { * ``` */ addAppUpdate(params: AppUpdateParams): TransactionComposer { - this.txns.push({ ...params, type: 'appCall', onComplete: algosdk.OnApplicationComplete.UpdateApplicationOC }) + this.txns.push({ ...params, type: 'appCall', onComplete: OnApplicationComplete.UpdateApplication }) return this } @@ -1047,7 +1050,7 @@ export class TransactionComposer { * ```typescript * composer.addAppDelete({ * sender: 'CREATORADDRESS', - * onComplete: algosdk.OnApplicationComplete.DeleteApplicationOC, + * onComplete: OnApplicationComplete.DeleteApplication, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -1068,7 +1071,7 @@ export class TransactionComposer { * ``` */ addAppDelete(params: AppDeleteParams): TransactionComposer { - this.txns.push({ ...params, type: 'appCall', onComplete: algosdk.OnApplicationComplete.DeleteApplicationOC }) + this.txns.push({ ...params, type: 'appCall', onComplete: OnApplicationComplete.DeleteApplication }) return this } @@ -1089,7 +1092,7 @@ export class TransactionComposer { * ```typescript * composer.addAppCall({ * sender: 'CREATORADDRESS', - * onComplete: algosdk.OnApplicationComplete.OptInOC, + * onComplete: OnApplicationComplete.OptIn, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -1150,7 +1153,7 @@ export class TransactionComposer { * localByteSlices: 4 * }, * extraProgramPages: 1, - * onComplete: algosdk.OnApplicationComplete.OptInOC, + * onComplete: OnApplicationComplete.OptIn, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -1203,7 +1206,7 @@ export class TransactionComposer { * args: ["arg1_value"], * approvalProgram: "TEALCODE", * clearStateProgram: "TEALCODE", - * onComplete: algosdk.OnApplicationComplete.UpdateApplicationOC, + * onComplete: OnApplicationComplete.UpdateApplication, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -1224,7 +1227,7 @@ export class TransactionComposer { * ``` */ addAppUpdateMethodCall(params: AppUpdateMethodCall) { - this.txns.push({ ...params, type: 'methodCall', onComplete: algosdk.OnApplicationComplete.UpdateApplicationOC }) + this.txns.push({ ...params, type: 'methodCall', onComplete: OnApplicationComplete.UpdateApplication }) return this } @@ -1254,7 +1257,7 @@ export class TransactionComposer { * sender: 'CREATORADDRESS', * method: method, * args: ["arg1_value"], - * onComplete: algosdk.OnApplicationComplete.DeleteApplicationOC, + * onComplete: OnApplicationComplete.DeleteApplication, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -1275,7 +1278,7 @@ export class TransactionComposer { * ``` */ addAppDeleteMethodCall(params: AppDeleteMethodCall) { - this.txns.push({ ...params, type: 'methodCall', onComplete: algosdk.OnApplicationComplete.DeleteApplicationOC }) + this.txns.push({ ...params, type: 'methodCall', onComplete: OnApplicationComplete.DeleteApplication }) return this } @@ -1305,7 +1308,7 @@ export class TransactionComposer { * sender: 'CREATORADDRESS', * method: method, * args: ["arg1_value"], - * onComplete: algosdk.OnApplicationComplete.OptInOC, + * onComplete: OnApplicationComplete.OptIn, * args: [new Uint8Array(1, 2, 3, 4)] * accountReferences: ["ACCOUNT_1"] * appReferences: [123n, 1234n] @@ -1459,43 +1462,44 @@ export class TransactionComposer { // We are going to mutate suggested params, let's create a clone first txnParams.suggestedParams = { ...txnParams.suggestedParams } - if (params.lease) txnParams.lease = encodeLease(params.lease)! satisfies algosdk.Transaction['lease'] - if (params.rekeyTo) txnParams.rekeyTo = address(params.rekeyTo) satisfies algosdk.Transaction['rekeyTo'] + if (params.lease) txnParams.lease = encodeLease(params.lease)! satisfies Transaction['lease'] + if (params.rekeyTo) txnParams.rekeyTo = params.rekeyTo.toString() satisfies Transaction['rekeyTo'] const encoder = new TextEncoder() if (params.note) - txnParams.note = (typeof params.note === 'string' ? encoder.encode(params.note) : params.note) satisfies algosdk.Transaction['note'] + txnParams.note = (typeof params.note === 'string' ? encoder.encode(params.note) : params.note) satisfies Transaction['note'] if (params.firstValidRound) { - txnParams.suggestedParams.firstValid = params.firstValidRound + txnParams.suggestedParams.firstRound = params.firstValidRound } if (params.lastValidRound) { - txnParams.suggestedParams.lastValid = params.lastValidRound + txnParams.suggestedParams.lastRound = params.lastValidRound } else { // If the validity window isn't set in this transaction or by default and we are pointing at // LocalNet set a bigger window to avoid dead transactions const window = params.validityWindow ? BigInt(params.validityWindow) - : !this.defaultValidityWindowIsExplicit && genesisIdIsLocalNet(txnParams.suggestedParams.genesisID ?? 'unknown') + : !this.defaultValidityWindowIsExplicit && genesisIdIsLocalNet(txnParams.suggestedParams.genesisId ?? 'unknown') ? 1000n : this.defaultValidityWindow - txnParams.suggestedParams.lastValid = BigInt(txnParams.suggestedParams.firstValid) + window + txnParams.suggestedParams.lastRound = BigInt(txnParams.suggestedParams.firstRound) + window } if (params.staticFee !== undefined && params.extraFee !== undefined) { throw Error('Cannot set both staticFee and extraFee') } - if (params.staticFee !== undefined) { - txnParams.suggestedParams.fee = params.staticFee.microAlgo - txnParams.suggestedParams.flatFee = true - } - - const txn = buildTxn(txnParams) + let txn = buildTxn(txnParams) - if (params.extraFee) txn.fee += params.extraFee.microAlgo - if (params.maxFee !== undefined && txn.fee > params.maxFee.microAlgo) { - throw Error(`Transaction fee ${txn.fee} µALGO is greater than maxFee ${params.maxFee}`) + if (params.staticFee !== undefined) { + txn.fee = params.staticFee.microAlgos + } else { + txn = assignFee(txn, { + feePerByte: txnParams.suggestedParams.fee, + minFee: txnParams.suggestedParams.minFee, + extraFee: params.extraFee?.microAlgos, + maxFee: params.maxFee?.microAlgos, + }) } const logicalMaxFee = @@ -1511,7 +1515,7 @@ export class TransactionComposer { */ private async buildMethodCall( params: AppCallMethodCall | AppCreateMethodCall | AppUpdateMethodCall, - suggestedParams: algosdk.SuggestedParams, + suggestedParams: SdkTransactionParams, includeSigner: boolean, ): Promise { const methodArgs: (algosdk.ABIArgument | TransactionWithSignerAndContext)[] = [] @@ -1627,16 +1631,22 @@ export class TransactionComposer { : params.clearStateProgram : undefined + // If accessReferences is provided, we should not pass legacy foreign arrays + const hasAccessReferences = params.accessReferences && params.accessReferences.length > 0 + const txnParams = { appID: appId, sender: params.sender, suggestedParams, - onComplete: params.onComplete ?? algosdk.OnApplicationComplete.NoOpOC, - appAccounts: params.accountReferences, - appForeignApps: params.appReferences?.map((x) => Number(x)), - appForeignAssets: params.assetReferences?.map((x) => Number(x)), - boxes: params.boxReferences?.map(AppManager.getBoxReference), - access: params.accessReferences?.map(getAccessReference), + onComplete: params.onComplete ?? OnApplicationComplete.NoOp, + ...(hasAccessReferences + ? { access: params.accessReferences } + : { + appAccounts: params.accountReferences, + appForeignApps: params.appReferences?.map((x) => Number(x)), + appForeignAssets: params.assetReferences?.map((x) => Number(x)), + boxes: params.boxReferences?.map(AppManager.getBoxReference), + }), approvalProgram, clearProgram: clearStateProgram, extraPages: @@ -1687,19 +1697,25 @@ export class TransactionComposer { // Process the ATC to get a set of transactions ready for broader grouping return this.buildAtc(methodAtc).map(({ context: _context, ...txnWithSigner }, idx) => { const maxFee = idx === methodAtc.count() - 1 ? result.context.maxFee : maxFees.get(idx) + // TODO: PD - review this way of assigning fee + const fee = idx === methodAtc.count() - 1 ? result.txn.fee : txnWithSigner.txn.fee const context = { ..._context, // Adds method context info maxFee, } return { - ...txnWithSigner, + signer: txnWithSigner.signer, + txn: { + ...txnWithSigner.txn, + fee: fee, + }, context, } }) } - private buildPayment(params: PaymentParams, suggestedParams: algosdk.SuggestedParams) { + private buildPayment(params: PaymentParams, suggestedParams: SdkTransactionParams) { return this.commonTxnBuildStep(algosdk.makePaymentTxnWithSuggestedParamsFromObject, params, { sender: params.sender, receiver: params.receiver, @@ -1709,7 +1725,7 @@ export class TransactionComposer { }) } - private buildAssetCreate(params: AssetCreateParams, suggestedParams: algosdk.SuggestedParams) { + private buildAssetCreate(params: AssetCreateParams, suggestedParams: SdkTransactionParams) { return this.commonTxnBuildStep(algosdk.makeAssetCreateTxnWithSuggestedParamsFromObject, params, { sender: params.sender, total: params.total, @@ -1727,7 +1743,7 @@ export class TransactionComposer { }) } - private buildAssetConfig(params: AssetConfigParams, suggestedParams: algosdk.SuggestedParams) { + private buildAssetConfig(params: AssetConfigParams, suggestedParams: SdkTransactionParams) { return this.commonTxnBuildStep(algosdk.makeAssetConfigTxnWithSuggestedParamsFromObject, params, { sender: params.sender, assetIndex: params.assetId, @@ -1740,7 +1756,7 @@ export class TransactionComposer { }) } - private buildAssetDestroy(params: AssetDestroyParams, suggestedParams: algosdk.SuggestedParams) { + private buildAssetDestroy(params: AssetDestroyParams, suggestedParams: SdkTransactionParams) { return this.commonTxnBuildStep(algosdk.makeAssetDestroyTxnWithSuggestedParamsFromObject, params, { sender: params.sender, assetIndex: params.assetId, @@ -1748,7 +1764,7 @@ export class TransactionComposer { }) } - private buildAssetFreeze(params: AssetFreezeParams, suggestedParams: algosdk.SuggestedParams) { + private buildAssetFreeze(params: AssetFreezeParams, suggestedParams: SdkTransactionParams) { return this.commonTxnBuildStep(algosdk.makeAssetFreezeTxnWithSuggestedParamsFromObject, params, { sender: params.sender, assetIndex: params.assetId, @@ -1758,7 +1774,7 @@ export class TransactionComposer { }) } - private buildAssetTransfer(params: AssetTransferParams, suggestedParams: algosdk.SuggestedParams) { + private buildAssetTransfer(params: AssetTransferParams, suggestedParams: SdkTransactionParams) { return this.commonTxnBuildStep(algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject, params, { sender: params.sender, receiver: params.receiver, @@ -1770,7 +1786,7 @@ export class TransactionComposer { }) } - private async buildAppCall(params: AppCallParams | AppUpdateParams | AppCreateParams, suggestedParams: algosdk.SuggestedParams) { + private async buildAppCall(params: AppCallParams | AppUpdateParams | AppCreateParams, suggestedParams: SdkTransactionParams) { const appId = 'appId' in params ? params.appId : 0n const approvalProgram = 'approvalProgram' in params @@ -1785,16 +1801,22 @@ export class TransactionComposer { : params.clearStateProgram : undefined + // If accessReferences is provided, we should not pass legacy foreign arrays + const hasAccessReferences = params.accessReferences && params.accessReferences.length > 0 + const sdkParams = { sender: params.sender, suggestedParams, appArgs: params.args, - onComplete: params.onComplete ?? algosdk.OnApplicationComplete.NoOpOC, - accounts: params.accountReferences, - foreignApps: params.appReferences?.map((x) => Number(x)), - foreignAssets: params.assetReferences?.map((x) => Number(x)), - boxes: params.boxReferences?.map(AppManager.getBoxReference), - access: params.accessReferences?.map(getAccessReference), + onComplete: params.onComplete ?? OnApplicationComplete.NoOp, + ...(hasAccessReferences + ? { access: params.accessReferences } + : { + accounts: params.accountReferences, + foreignApps: params.appReferences?.map((x) => Number(x)), + foreignAssets: params.assetReferences?.map((x) => Number(x)), + boxes: params.boxReferences?.map(AppManager.getBoxReference), + }), approvalProgram, clearProgram: clearStateProgram, } @@ -1822,7 +1844,7 @@ export class TransactionComposer { } } - private buildKeyReg(params: OnlineKeyRegistrationParams | OfflineKeyRegistrationParams, suggestedParams: algosdk.SuggestedParams) { + private buildKeyReg(params: OnlineKeyRegistrationParams | OfflineKeyRegistrationParams, suggestedParams: SdkTransactionParams) { if ('voteKey' in params) { return this.commonTxnBuildStep(algosdk.makeKeyRegistrationTxnWithSuggestedParamsFromObject, params, { sender: params.sender, @@ -1845,7 +1867,7 @@ export class TransactionComposer { } /** Builds all transaction types apart from `txnWithSigner`, `atc` and `methodCall` since those ones can have custom signers that need to be retrieved. */ - private async buildTxn(txn: Txn, suggestedParams: algosdk.SuggestedParams): Promise { + private async buildTxn(txn: Txn, suggestedParams: SdkTransactionParams): Promise { switch (txn.type) { case 'pay': return [this.buildPayment(txn, suggestedParams)] @@ -1872,7 +1894,7 @@ export class TransactionComposer { } } - private async buildTxnWithSigner(txn: Txn, suggestedParams: algosdk.SuggestedParams): Promise { + private async buildTxnWithSigner(txn: Txn, suggestedParams: SdkTransactionParams): Promise { if (txn.type === 'txnWithSigner') { return [ { @@ -1906,14 +1928,19 @@ export class TransactionComposer { */ async buildTransactions(): Promise { const suggestedParams = await this.getSuggestedParams() + const sdkTransactionParams: SdkTransactionParams = { + ...suggestedParams, + firstRound: suggestedParams.lastRound, + lastRound: suggestedParams.lastRound + 1000n, + } - const transactions: algosdk.Transaction[] = [] + const transactions: Transaction[] = [] const methodCalls = new Map() const signers = new Map() for (const txn of this.txns) { if (!['txnWithSigner', 'atc', 'methodCall'].includes(txn.type)) { - transactions.push(...(await this.buildTxn(txn, suggestedParams)).map((txn) => txn.txn)) + transactions.push(...(await this.buildTxn(txn, sdkTransactionParams)).map((txn) => txn.txn)) } else { const transactionsWithSigner = txn.type === 'txnWithSigner' @@ -1921,7 +1948,7 @@ export class TransactionComposer { : txn.type === 'atc' ? this.buildAtc(txn.atc) : txn.type === 'methodCall' - ? await this.buildMethodCall(txn, suggestedParams, false) + ? await this.buildMethodCall(txn, sdkTransactionParams, false) : [] transactionsWithSigner.forEach((ts) => { @@ -1965,11 +1992,15 @@ export class TransactionComposer { async build() { if (this.atc.getStatus() === algosdk.AtomicTransactionComposerStatus.BUILDING) { const suggestedParams = await this.getSuggestedParams() - + const sdkTransactionParams: SdkTransactionParams = { + ...suggestedParams, + firstRound: suggestedParams.lastRound, + lastRound: suggestedParams.lastRound + 1000n, + } // Build all of the transactions const txnWithSigners: TransactionWithSignerAndContext[] = [] for (const txn of this.txns) { - txnWithSigners.push(...(await this.buildTxnWithSigner(txn, suggestedParams))) + txnWithSigners.push(...(await this.buildTxnWithSigner(txn, sdkTransactionParams))) } // Add all of the transactions to the underlying ATC @@ -2025,7 +2056,7 @@ export class TransactionComposer { if (waitRounds === undefined) { const lastRound = group.reduce((max, txn) => (txn.txn.lastValid > max ? txn.txn.lastValid : BigInt(max)), 0n) - const { firstValid: firstRound } = suggestedParams! + const { lastRound: firstRound } = suggestedParams! waitRounds = Number(BigInt(lastRound) - BigInt(firstRound)) + 1 } @@ -2072,7 +2103,7 @@ export class TransactionComposer { * const result = await composer.simulate() * ``` */ - async simulate(): Promise + async simulate(): Promise /** * Compose the atomic transaction group and simulate sending it to the network * @returns The simulation result @@ -2085,7 +2116,7 @@ export class TransactionComposer { */ async simulate( options: SkipSignaturesSimulateOptions, - ): Promise + ): Promise /** * Compose the atomic transaction group and simulate sending it to the network * @returns The simulation result @@ -2096,8 +2127,8 @@ export class TransactionComposer { * }) * ``` */ - async simulate(options: RawSimulateOptions): Promise - async simulate(options?: SimulateOptions): Promise { + async simulate(options: RawSimulateOptions): Promise + async simulate(options?: SimulateOptions): Promise { const { skipSignatures = false, ...rawOptions } = options ?? {} const atc = skipSignatures ? new AtomicTransactionComposer() : this.atc @@ -2116,26 +2147,23 @@ export class TransactionComposer { await this.build() } - const { methodResults, simulateResponse } = await atc.simulate( - this.algod, - new modelsv2.SimulateRequest({ - txnGroups: [], - ...rawOptions, - ...(Config.debug - ? { - allowEmptySignatures: true, - fixSigners: true, - allowMoreLogging: true, - execTraceConfig: new modelsv2.SimulateTraceConfig({ - enable: true, - scratchChange: true, - stackChange: true, - stateChange: true, - }), - } - : undefined), - }), - ) + const { methodResults, simulateResponse } = await atc.simulate(this.algod, { + txnGroups: [], + ...rawOptions, + ...(Config.debug + ? { + allowEmptySignatures: true, + fixSigners: true, + allowMoreLogging: true, + execTraceConfig: { + enable: true, + scratchChange: true, + stackChange: true, + stateChange: true, + }, + } + : undefined), + } satisfies SimulateRequest) const failedGroup = simulateResponse?.txnGroups[0] if (failedGroup?.failureMessage) { @@ -2156,9 +2184,9 @@ export class TransactionComposer { const transactions = atc.buildGroup().map((t) => t.txn) const methodCalls = [...(atc['methodCalls'] as Map).values()] return { - confirmations: simulateResponse.txnGroups[0].txnResults.map((t) => t.txnResult), - transactions: transactions, - txIds: transactions.map((t) => t.txID()), + confirmations: simulateResponse.txnGroups[0].txnResults.map((t) => wrapPendingTransactionResponse(t.txnResult)), + transactions: transactions.map((t) => new TransactionWrapper(t)), + txIds: transactions.map((t) => getTransactionId(t)), groupId: Buffer.from(transactions[0].group ?? new Uint8Array()).toString('base64'), simulateResponse, returns: methodResults.map((r, i) => getABIReturnValue(r, methodCalls[i]!.returns.type)), diff --git a/src/types/debugging.ts b/src/types/debugging.ts index 755cce95..26b3e816 100644 --- a/src/types/debugging.ts +++ b/src/types/debugging.ts @@ -2,7 +2,7 @@ * An asynchronous event listener */ -import algosdk from 'algosdk' +import { SimulateTransactionGroupResult } from '@algorandfoundation/algokit-algod-client' import { CompiledTeal } from './app' /** The directory name for AlgoKit project related files */ @@ -45,5 +45,5 @@ export interface TealSourcesDebugEventData { */ export interface AVMTracesEventData { /** The simulation response from Algod */ - simulateResponse: algosdk.modelsv2.SimulateResponse + simulateResponse: SimulateTransactionGroupResult } diff --git a/src/types/dispenser-client.ts b/src/types/dispenser-client.ts index cc9a9b44..01108371 100644 --- a/src/types/dispenser-client.ts +++ b/src/types/dispenser-client.ts @@ -1,4 +1,4 @@ -import { Address } from 'algosdk' +import { Address } from '@algorandfoundation/sdk' import { asJson } from '../util' const DISPENSER_BASE_URL = 'https://api.dispenser.algorandfoundation.tools' diff --git a/src/types/indexer.ts b/src/types/indexer.ts index 6de740cb..61055e4f 100644 --- a/src/types/indexer.ts +++ b/src/types/indexer.ts @@ -1,5 +1,4 @@ -import algosdk from 'algosdk' -import indexerModels = algosdk.indexerModels +import { indexerModels } from '@algorandfoundation/sdk' /** @deprecated Use `algosdk.indexerModels.TransactionsResponse`. Indexer result for a transaction search, https://dev.algorand.co/reference/rest-apis/indexer#get-v2transactions */ export type TransactionSearchResults = indexerModels.TransactionsResponse diff --git a/src/types/kmd-account-manager.ts b/src/types/kmd-account-manager.ts index b92231b2..7b3053e7 100644 --- a/src/types/kmd-account-manager.ts +++ b/src/types/kmd-account-manager.ts @@ -1,5 +1,6 @@ -import algosdk, { Address } from 'algosdk' import { Config } from '../config' +import * as algosdk from '@algorandfoundation/sdk' +import { Address } from '@algorandfoundation/sdk' import { SigningAccount, TransactionSignerAccount } from './account' import { AlgoAmount } from './amount' import { ClientManager } from './client-manager' @@ -84,7 +85,7 @@ export class KmdAccountManager { if (predicate) { for (i = 0; i < addresses.length; i++) { const address = addresses[i] - const account = await this._clientManager.algod.accountInformation(address).do() + const account = await this._clientManager.algod.accountInformation(address) if (predicate(account)) { break } @@ -163,7 +164,7 @@ export class KmdAccountManager { await new TransactionComposer({ algod: this._clientManager.algod, getSigner: () => dispenser.signer, - getSuggestedParams: () => this._clientManager.algod.getTransactionParams().do(), + getSuggestedParams: () => this._clientManager.algod.transactionParams(), }) .addPayment({ amount: fundWith ?? AlgoAmount.Algo(1000), diff --git a/src/types/network-client.ts b/src/types/network-client.ts index 88386990..0a138d7d 100644 --- a/src/types/network-client.ts +++ b/src/types/network-client.ts @@ -1,4 +1,4 @@ -import { TokenHeader } from 'algosdk' +import { TokenHeader } from '@algorandfoundation/sdk' /** Config for an Algorand SDK client. */ export interface AlgoClientConfig { diff --git a/src/types/testing.ts b/src/types/testing.ts index f7292dd3..97f13844 100644 --- a/src/types/testing.ts +++ b/src/types/testing.ts @@ -1,16 +1,14 @@ -import algosdk, { Address } from 'algosdk' +import { AlgodClient } from '@algorandfoundation/algokit-algod-client' +import { Transaction } from '@algorandfoundation/algokit-transact' +import type { Account } from '@algorandfoundation/sdk' +import * as algosdk from '@algorandfoundation/sdk' +import { Address, Indexer, Kmd, LogicSigAccount } from '@algorandfoundation/sdk' import { TransactionLogger } from '../testing' import { TestLogger } from '../testing/test-logger' import { AlgoAmount } from '../types/amount' import { MultisigAccount, SigningAccount, TransactionSignerAccount } from './account' import { AlgorandClient } from './algorand-client' import { AlgoConfig } from './network-client' -import Account = algosdk.Account -import Algodv2 = algosdk.Algodv2 -import Indexer = algosdk.Indexer -import Kmd = algosdk.Kmd -import LogicSigAccount = algosdk.LogicSigAccount -import Transaction = algosdk.Transaction /** * Test automation context. @@ -19,7 +17,7 @@ export interface AlgorandTestAutomationContext { /** An AlgorandClient instance loaded with the current context, including testAccount and any generated accounts loaded as signers */ algorand: AlgorandClient /** Algod client instance that will log transactions in `transactionLogger` */ - algod: Algodv2 + algod: AlgodClient /** Indexer client instance */ indexer: Indexer /** KMD client instance */ @@ -51,7 +49,7 @@ export interface GetTestAccountParams { /** Configuration for creating an Algorand testing fixture. */ export interface AlgorandFixtureConfig extends Partial { /** An optional algod client, if not specified then it will create one against `algodConfig` (if present) then environment variables defined network (if present) or default LocalNet. */ - algod?: Algodv2 + algod?: AlgodClient /** An optional indexer client, if not specified then it will create one against `indexerConfig` (if present) then environment variables defined network (if present) or default LocalNet. */ indexer?: Indexer /** An optional kmd client, if not specified then it will create one against `kmdConfig` (if present) then environment variables defined network (if present) or default LocalNet. */ @@ -59,7 +57,7 @@ export interface AlgorandFixtureConfig extends Partial { /** The amount of funds to allocate to the default testing account, if not specified then it will get 10 ALGO. */ testAccountFunding?: AlgoAmount /** Optional override for how to get an account; this allows you to retrieve accounts from a known or cached list of accounts. */ - accountGetter?: (algod: Algodv2, kmd?: Kmd) => Promise + accountGetter?: (algod: AlgodClient, kmd?: Kmd) => Promise } /** An Algorand automated testing fixture */ diff --git a/src/types/transaction.ts b/src/types/transaction.ts index 012ffa7f..5f4a8e8d 100644 --- a/src/types/transaction.ts +++ b/src/types/transaction.ts @@ -1,13 +1,24 @@ -import algosdk from 'algosdk' +import { PendingTransactionResponse } from '@algorandfoundation/algokit-algod-client' +import { + AppCallTransactionFields, + AssetConfigTransactionFields, + AssetFreezeTransactionFields, + AssetTransferTransactionFields, + KeyRegistrationTransactionFields, + PaymentTransactionFields, + SignedTransaction, + Transaction, + TransactionType, + getTransactionId, +} from '@algorandfoundation/algokit-transact' +import { HeartbeatTransactionFields } from '@algorandfoundation/algokit-transact/transactions/heartbeat' +import { StateProofTransactionFields } from '@algorandfoundation/algokit-transact/transactions/state-proof' +import * as algosdk from '@algorandfoundation/sdk' +import { AtomicTransactionComposer, LogicSigAccount, type Account } from '@algorandfoundation/sdk' import { MultisigAccount, SigningAccount, TransactionSignerAccount } from './account' import { AlgoAmount } from './amount' import { ABIReturn } from './app' import { Expand } from './expand' -import Account = algosdk.Account -import AtomicTransactionComposer = algosdk.AtomicTransactionComposer -import LogicSigAccount = algosdk.LogicSigAccount -import Transaction = algosdk.Transaction -import modelsv2 = algosdk.modelsv2 export type TransactionNote = Uint8Array | TransactionNoteData | Arc2TransactionNote // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -52,23 +63,23 @@ export type SendSingleTransactionResult = Expand { /** base64 encoded representation of the group ID of the atomic group */ groupId: string /** The transaction IDs that have been prepared and/or sent */ @@ -78,21 +89,21 @@ export interface SendAtomicTransactionComposerResults extends SendTransactionRes /** The responses if the transactions were sent and waited for, * the index of the confirmation will match the index of the underlying transaction */ - confirmations: modelsv2.PendingTransactionResponse[] + confirmations: PendingTransactionResponseWrapper[] } /** The result of sending and confirming a transaction */ export interface ConfirmedTransactionResult extends SendTransactionResult { /** The response from sending and waiting for the transaction */ - confirmation: modelsv2.PendingTransactionResponse + confirmation: PendingTransactionResponseWrapper } /** The result of sending and confirming one or more transactions, but where there is a primary transaction of interest */ export interface ConfirmedTransactionResults extends SendTransactionResult, SendTransactionResults { /** The response from sending and waiting for the primary transaction */ - confirmation: modelsv2.PendingTransactionResponse + confirmation: PendingTransactionResponseWrapper /** The response from sending and waiting for the transactions */ - confirmations: modelsv2.PendingTransactionResponse[] + confirmations: PendingTransactionResponseWrapper[] } /** Core account abstraction when signing/sending transactions @@ -148,7 +159,7 @@ export interface AdditionalAtomicTransactionComposerContext { maxFees: Map /* The suggested params info relevant to transactions in the `AtomicTransactionComposer` */ - suggestedParams: Pick + suggestedParams: Pick } /** An `AtomicTransactionComposer` with transactions to send. */ @@ -166,3 +177,88 @@ export interface AtomicTransactionComposerToSend extends SendParams { **/ additionalAtcContext?: AdditionalAtomicTransactionComposerContext } + +export class TransactionWrapper implements Transaction { + type: TransactionType + sender: string + fee?: bigint + firstValid: bigint + lastValid: bigint + genesisHash?: Uint8Array + genesisId?: string + note?: Uint8Array + rekeyTo?: string + lease?: Uint8Array + group?: Uint8Array + payment?: PaymentTransactionFields + assetTransfer?: AssetTransferTransactionFields + assetConfig?: AssetConfigTransactionFields + appCall?: AppCallTransactionFields + keyRegistration?: KeyRegistrationTransactionFields + assetFreeze?: AssetFreezeTransactionFields + heartbeat?: HeartbeatTransactionFields + stateProof?: StateProofTransactionFields + + constructor(transaction: Transaction) { + this.type = transaction.type + this.sender = transaction.sender + this.fee = transaction.fee + this.firstValid = transaction.firstValid + this.lastValid = transaction.lastValid + this.genesisHash = transaction.genesisHash + this.genesisId = transaction.genesisId + this.note = transaction.note + this.rekeyTo = transaction.rekeyTo + this.lease = transaction.lease + this.group = transaction.group + this.payment = transaction.payment + this.assetTransfer = transaction.assetTransfer + this.assetConfig = transaction.assetConfig + this.appCall = transaction.appCall + this.keyRegistration = transaction.keyRegistration + this.assetFreeze = transaction.assetFreeze + this.heartbeat = transaction.heartbeat + this.stateProof = transaction.stateProof + } + + /** + * Get the transaction ID + * @returns The transaction ID as a base64-encoded string + */ + txID(): string { + return getTransactionId(this) + } +} + +// TODO: PD - review the names of these wrapper +export type SignedTransactionWrapper = Omit & { + txn: TransactionWrapper +} + +export type PendingTransactionResponseWrapper = Omit & { + txn: SignedTransactionWrapper + innerTxns?: PendingTransactionResponseWrapper[] +} + +function wrapSignedTransaction(signedTransaction: SignedTransaction): SignedTransactionWrapper { + return { + ...signedTransaction, + txn: new TransactionWrapper(signedTransaction.txn), + } +} + +export function wrapPendingTransactionResponse(response: PendingTransactionResponse): PendingTransactionResponseWrapper { + return { + ...response, + txn: wrapSignedTransaction(response.txn), + innerTxns: response.innerTxns?.map(wrapPendingTransactionResponse), + } +} + +export function wrapPendingTransactionResponseOptional( + response?: PendingTransactionResponse, +): PendingTransactionResponseWrapper | undefined { + if (!response) return undefined + + return wrapPendingTransactionResponse(response) +} diff --git a/src/types/transfer.ts b/src/types/transfer.ts index fe4ec52c..8bd1d34f 100644 --- a/src/types/transfer.ts +++ b/src/types/transfer.ts @@ -1,8 +1,7 @@ -import algosdk from 'algosdk' +import { SdkTransactionParams } from '@algorandfoundation/sdk' import { AlgoAmount } from './amount' import { TestNetDispenserApiClient } from './dispenser-client' import { SendTransactionFrom, SendTransactionParams, TransactionNote } from './transaction' -import SuggestedParams = algosdk.SuggestedParams /** @deprecated Parameters for `transferAlgos` call. */ export interface AlgoTransferParams extends SendTransactionParams { @@ -13,7 +12,7 @@ export interface AlgoTransferParams extends SendTransactionParams { /** The amount to send */ amount: AlgoAmount /** Optional transaction parameters */ - transactionParams?: SuggestedParams + transactionParams?: SdkTransactionParams /** The (optional) transaction note */ note?: TransactionNote /** An (optional) [transaction lease](https://dev.algorand.co/concepts/transactions/leases) to apply */ @@ -27,7 +26,7 @@ export interface AlgoRekeyParams extends SendTransactionParams { /** The account / account address that will have the private key that is authorised to transact on behalf of the from account from now on */ rekeyTo: SendTransactionFrom | string /** Optional transaction parameters */ - transactionParams?: SuggestedParams + transactionParams?: SdkTransactionParams /** The (optional) transaction note */ note?: TransactionNote /** An (optional) [transaction lease](https://dev.algorand.co/concepts/transactions/leases) to apply */ @@ -45,7 +44,7 @@ export interface EnsureFundedParams extends SendTransactionParams { /** When issuing a funding amount, the minimum amount to transfer (avoids many small transfers if this gets called often on an active account) */ minFundingIncrement?: AlgoAmount /** Optional transaction parameters */ - transactionParams?: SuggestedParams + transactionParams?: SdkTransactionParams /** The (optional) transaction note, default: "Funding account to meet minimum requirement" */ note?: TransactionNote /** An (optional) [transaction lease](https://dev.algorand.co/concepts/transactions/leases) to apply */ @@ -63,7 +62,7 @@ export interface TransferAssetParams extends SendTransactionParams { /** The amount to send as the smallest divisible unit value */ amount: number | bigint /** Optional transaction parameters */ - transactionParams?: SuggestedParams + transactionParams?: SdkTransactionParams /** An address of a target account from which to perform a clawback operation. Please note, in such cases senderAccount must be equal to clawback field on ASA metadata. */ clawbackFrom?: SendTransactionFrom | string /** The (optional) transaction note */ diff --git a/src/util.spec.ts b/src/util.spec.ts index 2d03ba7b..80d97fbe 100644 --- a/src/util.spec.ts +++ b/src/util.spec.ts @@ -1,7 +1,7 @@ import { convertAbiByteArrays as convertAbiByteArrays } from './util' import { describe, it, expect } from 'vitest' -import { ABIValue, ABIByteType, ABIArrayStaticType, ABIArrayDynamicType, ABITupleType, ABIBoolType, ABIUintType } from 'algosdk' // Adjust this import path +import { ABIValue, ABIByteType, ABIArrayStaticType, ABIArrayDynamicType, ABITupleType, ABIBoolType, ABIUintType } from '@algorandfoundation/sdk' // Adjust this import path describe('convertAbiByteArrays', () => { describe('Basic byte arrays', () => { diff --git a/src/util.ts b/src/util.ts index c415b806..99d8a7c3 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,4 +1,4 @@ -import { ABIArrayDynamicType, ABIArrayStaticType, ABIByteType, ABIReturnType, ABITupleType, ABIType, ABIUintType, ABIValue } from 'algosdk' +import { ABIArrayDynamicType, ABIArrayStaticType, ABIByteType, ABIReturnType, ABITupleType, ABIType, ABIUintType, ABIValue } from '@algorandfoundation/sdk' import { APP_PAGE_MAX_SIZE } from './types/app' /** diff --git a/tests/example-contracts/client/TestContractClient.ts b/tests/example-contracts/client/TestContractClient.ts index a5fc5234..7235784a 100644 --- a/tests/example-contracts/client/TestContractClient.ts +++ b/tests/example-contracts/client/TestContractClient.ts @@ -4,8 +4,10 @@ * DO NOT MODIFY IT BY HAND. * requires: @algorandfoundation/algokit-utils: ^2 */ -import type { ABIResult, TransactionWithSigner } from 'algosdk' -import { Algodv2, AtomicTransactionComposer, OnApplicationComplete, Transaction, modelsv2 } from 'algosdk' +import { AlgodClient, SimulateTransactionGroupResult, SimulateTransactionResult } from '@algorandfoundation/algokit-algod-client' +import { OnApplicationComplete, Transaction } from '@algorandfoundation/algokit-transact' +import type { ABIResult, TransactionWithSigner } from '@algorandfoundation/sdk' +import { AtomicTransactionComposer } from '@algorandfoundation/sdk' import * as algokit from '../../../src/index' import type { ABIAppCallArg, @@ -210,23 +212,23 @@ export const APP_SPEC: AppSpec = { /** * Defines an onCompletionAction of 'no_op' */ -export type OnCompleteNoOp = { onCompleteAction?: 'no_op' | OnApplicationComplete.NoOpOC } +export type OnCompleteNoOp = { onCompleteAction?: 'no_op' | OnApplicationComplete.NoOp } /** * Defines an onCompletionAction of 'opt_in' */ -export type OnCompleteOptIn = { onCompleteAction: 'opt_in' | OnApplicationComplete.OptInOC } +export type OnCompleteOptIn = { onCompleteAction: 'opt_in' | OnApplicationComplete.OptIn } /** * Defines an onCompletionAction of 'close_out' */ -export type OnCompleteCloseOut = { onCompleteAction: 'close_out' | OnApplicationComplete.CloseOutOC } +export type OnCompleteCloseOut = { onCompleteAction: 'close_out' | OnApplicationComplete.CloseOut } /** * Defines an onCompletionAction of 'delete_application' */ -export type OnCompleteDelApp = { onCompleteAction: 'delete_application' | OnApplicationComplete.DeleteApplicationOC } +export type OnCompleteDelApp = { onCompleteAction: 'delete_application' | OnApplicationComplete.DeleteApplication } /** * Defines an onCompletionAction of 'update_application' */ -export type OnCompleteUpdApp = { onCompleteAction: 'update_application' | OnApplicationComplete.UpdateApplicationOC } +export type OnCompleteUpdApp = { onCompleteAction: 'update_application' | OnApplicationComplete.UpdateApplication } /** * A state record containing a single unsigned integer */ @@ -551,7 +553,7 @@ export class TestContractClient { */ constructor( appDetails: AppDetails, - private algod: Algodv2, + private algod: AlgodClient, ) { this.sender = appDetails.sender this.appClient = algokit.getAppClient( @@ -789,7 +791,7 @@ export class TestContractClient { }, async simulate(options?: SimulateOptions) { await promiseChain - const result = await atc.simulate(client.algod, new modelsv2.SimulateRequest({ txnGroups: [], ...options })) + const result = await atc.simulate(client.algod, { txnGroups: [], ...options }) return { ...result, returns: result.methodResults?.map((val, i) => @@ -914,11 +916,11 @@ export type TestContractComposer = { */ execute(sendParams?: AppClientComposeExecuteParams): Promise> } -export type SimulateOptions = Omit[0], 'txnGroups'> +export type SimulateOptions = Omit export type TestContractComposerSimulateResult = { returns: TReturns methodResults: ABIResult[] - simulateResponse: modelsv2.SimulateResponse + simulateResponse: SimulateTransactionGroupResult } export type TestContractComposerResults = { returns: TReturns diff --git a/tests/example-contracts/testing-app/contract.ts b/tests/example-contracts/testing-app/contract.ts index a6d0612b..51fa1670 100644 --- a/tests/example-contracts/testing-app/contract.ts +++ b/tests/example-contracts/testing-app/contract.ts @@ -1,4 +1,5 @@ -import algosdk, { Address } from 'algosdk' +import * as algosdk from '@algorandfoundation/sdk' +import { Address } from '@algorandfoundation/sdk' import { readFile } from 'fs/promises' import path from 'path' import { encodeTransactionNote, replaceDeployTimeControlParams } from '../../../src' diff --git a/tsconfig.base.json b/tsconfig.base.json index 58894254..08685cb1 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -12,6 +12,22 @@ "resolveJsonModule": true, "skipLibCheck": true, "declarationMap": true, - "composite": true + "baseUrl": ".", + "paths": { + "@algorandfoundation/algokit-abi": ["packages/abi/src"], + "@algorandfoundation/algokit-abi/*": ["packages/abi/src/*"], + "@algorandfoundation/algokit-common": ["packages/common/src"], + "@algorandfoundation/algokit-common/*": ["packages/common/src/*"], + "@algorandfoundation/algokit-transact": ["packages/transact/src"], + "@algorandfoundation/algokit-transact/*": ["packages/transact/src/*"], + "@algorandfoundation/algokit-algod-client": ["packages/algod_client/src"], + "@algorandfoundation/algokit-algod-client/*": ["packages/algod_client/src/*"], + "@algorandfoundation/algokit-indexer-client": ["packages/indexer_client/src"], + "@algorandfoundation/algokit-indexer-client/*": ["packages/indexer_client/src/*"], + "@algorandfoundation/algokit-kmd-client": ["packages/kmd_client/src"], + "@algorandfoundation/algokit-kmd-client/*": ["packages/kmd_client/src/*"], + "@algorandfoundation/sdk": ["packages/sdk/src"], + "@algorandfoundation/sdk/*": ["packages/sdk/src/*"] + } } } diff --git a/tsconfig.json b/tsconfig.json index 451549ab..17de7436 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,16 +10,25 @@ "declaration": true, "importHelpers": true, "isolatedModules": true, - "resolveJsonModule": true + "resolveJsonModule": true, + "baseUrl": ".", + "paths": { + "@algorandfoundation/algokit-common": ["./packages/common/src"], + "@algorandfoundation/algokit-common/*": ["./packages/common/src/*"], + "@algorandfoundation/algokit-transact": ["./packages/transact/src"], + "@algorandfoundation/algokit-transact/*": ["./packages/transact/src/*"], + "@algorandfoundation/algokit-abi": ["./packages/abi/src"], + "@algorandfoundation/algokit-abi/*": ["./packages/abi/src/*"], + "@algorandfoundation/algokit-algod-client": ["./packages/algod_client/src"], + "@algorandfoundation/algokit-algod-client/*": ["./packages/algod_client/src/*"], + "@algorandfoundation/algokit-indexer-client": ["./packages/indexer_client/src"], + "@algorandfoundation/algokit-indexer-client/*": ["./packages/indexer_client/src/*"], + "@algorandfoundation/algokit-kmd-client": ["./packages/kmd_client/src"], + "@algorandfoundation/algokit-kmd-client/*": ["./packages/kmd_client/src/*"], + "@algorandfoundation/sdk": ["./packages/sdk/src"], + "@algorandfoundation/sdk/*": ["./packages/sdk/src/*"] + } }, "include": ["src/**/*.ts", "tests/**/*.ts"], - "exclude": ["**/*.algo.ts"], - "references": [ - { "path": "./packages/common" }, - { "path": "./packages/abi" }, - { "path": "./packages/transact" }, - { "path": "./packages/algod_client" }, - { "path": "./packages/kmd_client" }, - { "path": "./packages/indexer_client" } - ] + "exclude": ["**/*.algo.ts"] } diff --git a/tsconfig.typecheck.json b/tsconfig.typecheck.json new file mode 100644 index 00000000..7d3ea590 --- /dev/null +++ b/tsconfig.typecheck.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts", "tests/**/*.ts", "packages/*/src/**/*.ts", "packages/*/tests/**/*.ts"] +}