From 36018a293a7863bed3be87c8715a281348e92c6f Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 00:34:17 +0100 Subject: [PATCH 01/19] docs: Update repository URLs in CONTRIBUTING.md and Clarinet.toml --- CONTRIBUTING.md | 4 ++-- Clarinet.toml | 30 ++++++++++++++---------------- LICENSE | 2 +- contracts/echovote.clar | 30 ++++++++++++++++++++++++++++++ tests/echovote.test.ts | 21 +++++++++++++++++++++ 5 files changed, 68 insertions(+), 19 deletions(-) create mode 100644 contracts/echovote.clar create mode 100644 tests/echovote.test.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 542fbfc..9ea42e9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,14 +29,14 @@ We welcome contributions of all kinds to EchoVote! Whether it's improving docume ### 1. Fork the repository -- Navigate to the [EchoVote repository](https://github.com/nicholas-source/echovote). +- Navigate to the [EchoVote repository](https://github.com/nicholas-source/echo_vote). - Click the "Fork" button in the top-right corner to create your copy of the repository. ### 2. Clone your fork - Clone your forked repository to your local machine: ```bash - git clone https://github.com/nicholas-source/echovote.git + git clone https://github.com/nicholas-source/echo_vote.git cd echovote ``` diff --git a/Clarinet.toml b/Clarinet.toml index 3029f1c..0a7b907 100644 --- a/Clarinet.toml +++ b/Clarinet.toml @@ -1,21 +1,19 @@ [project] -name = "echo_vote" -description = "" +name = 'echo_vote' +description = '' authors = [] telemetry = true -cache_dir = "./.cache" - -# [contracts.counter] -# path = "contracts/counter.clar" - +cache_dir = './.cache' +requirements = [] +[contracts.echovote] +path = 'contracts/echovote.clar' +clarity_version = 2 +epoch = 2.5 [repl.analysis] -passes = ["check_checker"] -check_checker = { trusted_sender = false, trusted_caller = false, callee_filter = false } +passes = ['check_checker'] -# Check-checker settings: -# trusted_sender: if true, inputs are trusted after tx_sender has been checked. -# trusted_caller: if true, inputs are trusted after contract-caller has been checked. -# callee_filter: if true, untrusted data may be passed into a private function without a -# warning, if it gets checked inside. This check will also propagate up to the -# caller. -# More informations: https://www.hiro.so/blog/new-safety-checks-in-clarinet +[repl.analysis.check_checker] +strict = false +trusted_sender = false +trusted_caller = false +callee_filter = false diff --git a/LICENSE b/LICENSE index be47bd8..a2618bb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,3 @@ - ### `LICENSE` ```md @@ -23,3 +22,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` diff --git a/contracts/echovote.clar b/contracts/echovote.clar new file mode 100644 index 0000000..158caaa --- /dev/null +++ b/contracts/echovote.clar @@ -0,0 +1,30 @@ + +;; title: echovote +;; version: +;; summary: +;; description: + +;; traits +;; + +;; token definitions +;; + +;; constants +;; + +;; data vars +;; + +;; data maps +;; + +;; public functions +;; + +;; read only functions +;; + +;; private functions +;; + diff --git a/tests/echovote.test.ts b/tests/echovote.test.ts new file mode 100644 index 0000000..4bb9cf3 --- /dev/null +++ b/tests/echovote.test.ts @@ -0,0 +1,21 @@ + +import { describe, expect, it } from "vitest"; + +const accounts = simnet.getAccounts(); +const address1 = accounts.get("wallet_1")!; + +/* + The test below is an example. To learn more, read the testing documentation here: + https://docs.hiro.so/stacks/clarinet-js-sdk +*/ + +describe("example tests", () => { + it("ensures simnet is well initalised", () => { + expect(simnet.blockHeight).toBeDefined(); + }); + + // it("shows an example", () => { + // const { result } = simnet.callReadOnlyFn("counter", "get-counter", [], address1); + // expect(result).toBeUint(0); + // }); +}); From e721e3e03e8dd35af5ac3f4ecd0bc24d5818e765 Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 00:35:57 +0100 Subject: [PATCH 02/19] Add basic structure and constants - Added contract-owner constant - Added error constants --- contracts/echovote.clar | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/contracts/echovote.clar b/contracts/echovote.clar index 158caaa..1da92bf 100644 --- a/contracts/echovote.clar +++ b/contracts/echovote.clar @@ -1,30 +1,6 @@ - -;; title: echovote -;; version: -;; summary: -;; description: - -;; traits -;; - -;; token definitions -;; - -;; constants -;; - -;; data vars -;; - -;; data maps -;; - -;; public functions -;; - -;; read only functions -;; - -;; private functions -;; - +;; Constants +(define-constant contract-owner tx-sender) +(define-constant err-unauthorized (err u100)) +(define-constant err-already-voted (err u101)) +(define-constant err-proposal-not-active (err u102)) +(define-constant err-invalid-vote (err u103)) \ No newline at end of file From 75a425543484c7570337eb12ae4f9915dc1284ac Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 00:36:23 +0100 Subject: [PATCH 03/19] Add data variables and maps - Added proposal-count data variable - Added proposals map - Added votes map - Added vote-counts map --- contracts/echovote.clar | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/contracts/echovote.clar b/contracts/echovote.clar index 1da92bf..0f4dd56 100644 --- a/contracts/echovote.clar +++ b/contracts/echovote.clar @@ -3,4 +3,30 @@ (define-constant err-unauthorized (err u100)) (define-constant err-already-voted (err u101)) (define-constant err-proposal-not-active (err u102)) -(define-constant err-invalid-vote (err u103)) \ No newline at end of file +(define-constant err-invalid-vote (err u103)) + +;; Data variables +(define-data-var proposal-count uint u0) + +;; Data maps +(define-map proposals + uint + { + title: (string-utf8 256), + description: (string-utf8 1024), + creator: principal, + start-block: uint, + end-block: uint, + is-active: bool + } +) + +(define-map votes + { proposal-id: uint, voter: principal } + uint +) + +(define-map vote-counts + { proposal-id: uint, option: uint } + uint +) \ No newline at end of file From 88938391d630e324e550ccb88095cd8f83e8d54d Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 00:37:24 +0100 Subject: [PATCH 04/19] Implement create-proposal function - Added create-proposal function to create new proposals - Includes validation for contract owner - Updates proposal-count --- contracts/echovote.clar | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/contracts/echovote.clar b/contracts/echovote.clar index 0f4dd56..feb684d 100644 --- a/contracts/echovote.clar +++ b/contracts/echovote.clar @@ -29,4 +29,26 @@ (define-map vote-counts { proposal-id: uint, option: uint } uint +) + +;; Public functions +(define-public (create-proposal (title (string-utf8 256)) (description (string-utf8 1024)) (start-block uint) (end-block uint)) + (let + ( + (new-proposal-id (+ (var-get proposal-count) u1)) + ) + (asserts! (is-eq tx-sender contract-owner) err-unauthorized) + (map-set proposals new-proposal-id + { + title: title, + description: description, + creator: tx-sender, + start-block: start-block, + end-block: end-block, + is-active: true + } + ) + (var-set proposal-count new-proposal-id) + (ok new-proposal-id) + ) ) \ No newline at end of file From f425c1cda1a5a2fbe3ad24886c0bc6bd37ec6e61 Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 00:38:08 +0100 Subject: [PATCH 05/19] Implement vote function - Added vote function to allow voting on proposals - Includes validation for proposal existence, active status, and vote option range - Updates votes and vote-counts maps --- contracts/echovote.clar | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/contracts/echovote.clar b/contracts/echovote.clar index feb684d..cde652d 100644 --- a/contracts/echovote.clar +++ b/contracts/echovote.clar @@ -51,4 +51,26 @@ (var-set proposal-count new-proposal-id) (ok new-proposal-id) ) -) \ No newline at end of file +) + +(define-public (vote (proposal-id uint) (vote-option uint)) + (let + ( + (proposal (unwrap! (map-get? proposals proposal-id) err-proposal-not-active)) + (current-block block-height) + ) + (asserts! (>= current-block (get start-block proposal)) err-proposal-not-active) + (asserts! (<= current-block (get end-block proposal)) err-proposal-not-active) + (asserts! (get is-active proposal) err-proposal-not-active) + (asserts! (is-none (map-get? votes { proposal-id: proposal-id, voter: tx-sender })) err-already-voted) + (asserts! (and (>= vote-option u1) (<= vote-option u5)) err-invalid-vote) + + (map-set votes { proposal-id: proposal-id, voter: tx-sender } vote-option) + (map-set vote-counts + { proposal-id: proposal-id, option: vote-option } + (+ (default-to u0 (map-get? vote-counts { proposal-id: proposal-id, option: vote-option })) u1) + ) + (ok true) + ) +) + From a09ef13a9d50e7d489298e94e7ad12569703611d Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 00:38:43 +0100 Subject: [PATCH 06/19] Implement read-only functions - Added get-proposal function to retrieve proposal details - Added get-vote function to retrieve a specific vote - Added get-vote-count function to retrieve vote count for an option - Added get-total-votes function to retrieve total votes for a proposal --- contracts/echovote.clar | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/contracts/echovote.clar b/contracts/echovote.clar index cde652d..be3ea4b 100644 --- a/contracts/echovote.clar +++ b/contracts/echovote.clar @@ -74,3 +74,27 @@ ) ) +(define-read-only (get-proposal (proposal-id uint)) + (map-get? proposals proposal-id) +) + +(define-read-only (get-vote (proposal-id uint) (voter principal)) + (map-get? votes { proposal-id: proposal-id, voter: voter }) +) + +(define-read-only (get-vote-count (proposal-id uint) (vote-option uint)) + (default-to u0 (map-get? vote-counts { proposal-id: proposal-id, option: vote-option })) +) + +(define-read-only (get-total-votes (proposal-id uint)) + (fold + + (list + (get-vote-count proposal-id u1) + (get-vote-count proposal-id u2) + (get-vote-count proposal-id u3) + (get-vote-count proposal-id u4) + (get-vote-count proposal-id u5) + ) + u0 + ) +) \ No newline at end of file From 1c1c10c28a8be7f34fe59542aa3de97b42f445ac Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 00:43:45 +0100 Subject: [PATCH 07/19] Implement validation for proposal title, description, and start/end blocks --- contracts/echovote.clar | 89 ++++++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 14 deletions(-) diff --git a/contracts/echovote.clar b/contracts/echovote.clar index be3ea4b..773e333 100644 --- a/contracts/echovote.clar +++ b/contracts/echovote.clar @@ -1,9 +1,16 @@ +;; echovote.clar + ;; Constants (define-constant contract-owner tx-sender) (define-constant err-unauthorized (err u100)) (define-constant err-already-voted (err u101)) (define-constant err-proposal-not-active (err u102)) (define-constant err-invalid-vote (err u103)) +(define-constant err-invalid-title (err u104)) +(define-constant err-invalid-description (err u105)) +(define-constant err-invalid-start-block (err u106)) +(define-constant err-invalid-end-block (err u107)) +(define-constant err-proposal-ended (err u108)) ;; Data variables (define-data-var proposal-count uint u0) @@ -17,7 +24,8 @@ creator: principal, start-block: uint, end-block: uint, - is-active: bool + is-active: bool, + total-votes: uint } ) @@ -31,6 +39,49 @@ uint ) +;; Private functions +(define-private (validate-text (text (string-utf8 256))) + (let ((length (len text))) + (and (> length u0) (<= length u256)) + ) +) + +(define-private (validate-long-text (text (string-utf8 1024))) + (let ((length (len text))) + (and (> length u0) (<= length u1024)) + ) +) + +(define-private (validate-blocks (start-block uint) (end-block uint)) + (let ((current-block block-height)) + (and + (> start-block current-block) + (> end-block start-block) + ) + ) +) + +(define-private (increment-vote-count (proposal-id uint) (vote-option uint)) + (let + ( + (current-count (default-to u0 (map-get? vote-counts { proposal-id: proposal-id, option: vote-option }))) + (new-count (+ current-count u1)) + ) + (map-set vote-counts { proposal-id: proposal-id, option: vote-option } new-count) + (increment-total-votes proposal-id) + ) +) + +(define-private (increment-total-votes (proposal-id uint)) + (let + ( + (proposal (unwrap! (map-get? proposals proposal-id) (err u404))) + (new-total (+ (get total-votes proposal) u1)) + ) + (map-set proposals proposal-id (merge proposal { total-votes: new-total })) + ) +) + ;; Public functions (define-public (create-proposal (title (string-utf8 256)) (description (string-utf8 1024)) (start-block uint) (end-block uint)) (let @@ -38,6 +89,9 @@ (new-proposal-id (+ (var-get proposal-count) u1)) ) (asserts! (is-eq tx-sender contract-owner) err-unauthorized) + (asserts! (validate-text title) err-invalid-title) + (asserts! (validate-long-text description) err-invalid-description) + (asserts! (validate-blocks start-block end-block) err-invalid-start-block) (map-set proposals new-proposal-id { title: title, @@ -45,7 +99,8 @@ creator: tx-sender, start-block: start-block, end-block: end-block, - is-active: true + is-active: true, + total-votes: u0 } ) (var-set proposal-count new-proposal-id) @@ -60,16 +115,26 @@ (current-block block-height) ) (asserts! (>= current-block (get start-block proposal)) err-proposal-not-active) - (asserts! (<= current-block (get end-block proposal)) err-proposal-not-active) + (asserts! (<= current-block (get end-block proposal)) err-proposal-ended) (asserts! (get is-active proposal) err-proposal-not-active) (asserts! (is-none (map-get? votes { proposal-id: proposal-id, voter: tx-sender })) err-already-voted) (asserts! (and (>= vote-option u1) (<= vote-option u5)) err-invalid-vote) (map-set votes { proposal-id: proposal-id, voter: tx-sender } vote-option) - (map-set vote-counts - { proposal-id: proposal-id, option: vote-option } - (+ (default-to u0 (map-get? vote-counts { proposal-id: proposal-id, option: vote-option })) u1) + (increment-vote-count proposal-id vote-option) + (ok true) + ) +) + +(define-public (end-proposal (proposal-id uint)) + (let + ( + (proposal (unwrap! (map-get? proposals proposal-id) err-proposal-not-active)) + (current-block block-height) ) + (asserts! (is-eq tx-sender contract-owner) err-unauthorized) + (asserts! (>= current-block (get end-block proposal)) err-proposal-not-active) + (map-set proposals proposal-id (merge proposal { is-active: false })) (ok true) ) ) @@ -87,14 +152,10 @@ ) (define-read-only (get-total-votes (proposal-id uint)) - (fold + - (list - (get-vote-count proposal-id u1) - (get-vote-count proposal-id u2) - (get-vote-count proposal-id u3) - (get-vote-count proposal-id u4) - (get-vote-count proposal-id u5) + (let + ( + (proposal (unwrap! (map-get? proposals proposal-id) (err u404))) ) - u0 + (get total-votes proposal) ) ) \ No newline at end of file From 62a4f1c8811968d7ffacd042a639d87cd1a4eae8 Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 00:44:51 +0100 Subject: [PATCH 08/19] Implement error constant for proposal not found Refactor increment-total-votes function to use match expression Refactor get-total-votes function to use match expression --- contracts/echovote.clar | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/contracts/echovote.clar b/contracts/echovote.clar index 773e333..8e0ad22 100644 --- a/contracts/echovote.clar +++ b/contracts/echovote.clar @@ -11,6 +11,7 @@ (define-constant err-invalid-start-block (err u106)) (define-constant err-invalid-end-block (err u107)) (define-constant err-proposal-ended (err u108)) +(define-constant err-proposal-not-found (err u404)) ;; Data variables (define-data-var proposal-count uint u0) @@ -73,12 +74,14 @@ ) (define-private (increment-total-votes (proposal-id uint)) - (let - ( - (proposal (unwrap! (map-get? proposals proposal-id) (err u404))) - (new-total (+ (get total-votes proposal) u1)) + (match (map-get? proposals proposal-id) + proposal (begin + (map-set proposals proposal-id + (merge proposal { total-votes: (+ (get total-votes proposal) u1) }) + ) + true ) - (map-set proposals proposal-id (merge proposal { total-votes: new-total })) + false ) ) @@ -152,10 +155,8 @@ ) (define-read-only (get-total-votes (proposal-id uint)) - (let - ( - (proposal (unwrap! (map-get? proposals proposal-id) (err u404))) - ) - (get total-votes proposal) + (match (map-get? proposals proposal-id) + proposal (ok (get total-votes proposal)) + (err err-proposal-not-found) ) ) \ No newline at end of file From 8cde11d94e5e9dc1d094f0e0a5eadc64184b69dd Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 00:46:46 +0100 Subject: [PATCH 09/19] Add detailed comments to enhance code clarity and maintainability in EchoVote contract --- contracts/echovote.clar | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/contracts/echovote.clar b/contracts/echovote.clar index 8e0ad22..bd68c75 100644 --- a/contracts/echovote.clar +++ b/contracts/echovote.clar @@ -1,4 +1,5 @@ -;; echovote.clar +;; EchoVote: A decentralized voting system +;; This contract allows for creating, voting on, and managing proposals ;; Constants (define-constant contract-owner tx-sender) @@ -41,18 +42,22 @@ ) ;; Private functions + +;; Validate text length (define-private (validate-text (text (string-utf8 256))) (let ((length (len text))) (and (> length u0) (<= length u256)) ) ) +;; Validate long text length (define-private (validate-long-text (text (string-utf8 1024))) (let ((length (len text))) (and (> length u0) (<= length u1024)) ) ) +;; Validate block range (define-private (validate-blocks (start-block uint) (end-block uint)) (let ((current-block block-height)) (and @@ -62,6 +67,7 @@ ) ) +;; Increment vote count for a specific option (define-private (increment-vote-count (proposal-id uint) (vote-option uint)) (let ( @@ -73,6 +79,7 @@ ) ) +;; Increment total votes for a proposal (define-private (increment-total-votes (proposal-id uint)) (match (map-get? proposals proposal-id) proposal (begin @@ -86,6 +93,8 @@ ) ;; Public functions + +;; Create a new proposal (define-public (create-proposal (title (string-utf8 256)) (description (string-utf8 1024)) (start-block uint) (end-block uint)) (let ( @@ -111,6 +120,7 @@ ) ) +;; Cast a vote on a proposal (define-public (vote (proposal-id uint) (vote-option uint)) (let ( @@ -129,6 +139,7 @@ ) ) +;; End an active proposal (define-public (end-proposal (proposal-id uint)) (let ( @@ -142,18 +153,24 @@ ) ) +;; Read-only functions + +;; Get proposal details (define-read-only (get-proposal (proposal-id uint)) (map-get? proposals proposal-id) ) +;; Get a user's vote for a proposal (define-read-only (get-vote (proposal-id uint) (voter principal)) (map-get? votes { proposal-id: proposal-id, voter: voter }) ) +;; Get vote count for a specific option (define-read-only (get-vote-count (proposal-id uint) (vote-option uint)) (default-to u0 (map-get? vote-counts { proposal-id: proposal-id, option: vote-option })) ) +;; Get total votes for a proposal (define-read-only (get-total-votes (proposal-id uint)) (match (map-get? proposals proposal-id) proposal (ok (get total-votes proposal)) From 0bd469e14eedc36159fb5b58027c3292d99df5eb Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 00:51:38 +0100 Subject: [PATCH 10/19] Add configuration file and API endpoint This commit adds a new configuration file, `index.ts`, which exports an object containing various configuration options for the application. It also adds a new API endpoint in the `index.ts` file, which responds with a simple message to indicate that the EchoVote API is running. --- package-lock.json | 2250 +++++++++++++++++++++++++++++++++++++++++++ package.json | 5 +- src/config/index.ts | 6 + src/index.ts | 17 + 4 files changed, 2276 insertions(+), 2 deletions(-) create mode 100644 package-lock.json create mode 100644 src/config/index.ts create mode 100644 src/index.ts diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8910184 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2250 @@ +{ + "name": "echo_vote-tests", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "echo_vote-tests", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@hirosystems/clarinet-sdk": "^2.3.2", + "@stacks/transactions": "^6.12.0", + "chokidar-cli": "^3.0.0", + "dotenv": "^10.0.0", + "express": "^4.17.1", + "typescript": "^5.3.3", + "vite": "^5.1.4", + "vitest": "^1.3.1", + "vitest-environment-clarinet": "^2.0.0" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@hirosystems/clarinet-sdk": { + "version": "2.8.1", + "license": "GPL-3.0", + "dependencies": { + "@hirosystems/clarinet-sdk-wasm": "^2.8.0", + "@stacks/transactions": "^6.13.0", + "kolorist": "^1.8.0", + "prompts": "^2.4.2", + "vitest": "^1.0.4", + "yargs": "^17.7.2" + }, + "bin": { + "clarinet-sdk": "dist/cjs/node/src/bin/index.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@hirosystems/clarinet-sdk-wasm": { + "version": "2.8.0", + "license": "GPL-3.0" + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "license": "MIT" + }, + "node_modules/@noble/hashes": { + "version": "1.1.5", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.22.5", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.22.5", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "license": "MIT" + }, + "node_modules/@stacks/common": { + "version": "6.16.0", + "license": "MIT", + "dependencies": { + "@types/bn.js": "^5.1.0", + "@types/node": "^18.0.4" + } + }, + "node_modules/@stacks/network": { + "version": "6.16.0", + "license": "MIT", + "dependencies": { + "@stacks/common": "^6.16.0", + "cross-fetch": "^3.1.5" + } + }, + "node_modules/@stacks/transactions": { + "version": "6.16.1", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.1.5", + "@noble/secp256k1": "1.7.1", + "@stacks/common": "^6.16.0", + "@stacks/network": "^6.16.0", + "c32check": "^2.0.0", + "lodash.clonedeep": "^4.5.0" + } + }, + "node_modules/@types/bn.js": { + "version": "5.1.6", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "18.19.50", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@vitest/expect": { + "version": "1.6.0", + "license": "MIT", + "dependencies": { + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.6.0", + "license": "MIT", + "dependencies": { + "@vitest/utils": "1.6.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.6.0", + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.6.0", + "license": "MIT", + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.6.0", + "license": "MIT", + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/base-x": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c32check": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.2", + "base-x": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar-cli": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "lodash.debounce": "^4.0.8", + "lodash.throttle": "^4.1.1", + "yargs": "^13.3.0" + }, + "bin": { + "chokidar": "index.js" + }, + "engines": { + "node": ">= 8.10.0" + } + }, + "node_modules/chokidar-cli/node_modules/ansi-regex": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/ansi-styles": { + "version": "3.2.1", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar-cli/node_modules/cliui": { + "version": "5.0.0", + "license": "ISC", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/chokidar-cli/node_modules/color-convert": { + "version": "1.9.3", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/chokidar-cli/node_modules/color-name": { + "version": "1.1.3", + "license": "MIT" + }, + "node_modules/chokidar-cli/node_modules/emoji-regex": { + "version": "7.0.3", + "license": "MIT" + }, + "node_modules/chokidar-cli/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar-cli/node_modules/string-width": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/strip-ansi": { + "version": "5.2.0", + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/wrap-ansi": { + "version": "5.1.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/y18n": { + "version": "4.0.3", + "license": "ISC" + }, + "node_modules/chokidar-cli/node_modules/yargs": { + "version": "13.3.2", + "license": "MIT", + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/chokidar-cli/node_modules/yargs-parser": { + "version": "13.1.2", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.1.7", + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/cross-fetch": { + "version": "3.1.8", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/express": { + "version": "4.21.0", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "9.0.0", + "license": "MIT" + }, + "node_modules/kleur": { + "version": "3.0.3", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "license": "MIT" + }, + "node_modules/local-pkg": { + "version": "0.5.0", + "license": "MIT", + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "license": "MIT" + }, + "node_modules/loupe": { + "version": "2.3.7", + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/magic-string": { + "version": "0.30.11", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mlly": { + "version": "1.7.1", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.1", + "ufo": "^1.5.3" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.10", + "license": "MIT" + }, + "node_modules/pathe": { + "version": "1.1.2", + "license": "MIT" + }, + "node_modules/pathval": { + "version": "1.1.1", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.1.0", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.7.1", + "pathe": "^1.1.2" + } + }, + "node_modules/postcss": { + "version": "8.4.47", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/rollup": { + "version": "4.22.5", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.22.5", + "@rollup/rollup-android-arm64": "4.22.5", + "@rollup/rollup-darwin-arm64": "4.22.5", + "@rollup/rollup-darwin-x64": "4.22.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.5", + "@rollup/rollup-linux-arm-musleabihf": "4.22.5", + "@rollup/rollup-linux-arm64-gnu": "4.22.5", + "@rollup/rollup-linux-arm64-musl": "4.22.5", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.5", + "@rollup/rollup-linux-riscv64-gnu": "4.22.5", + "@rollup/rollup-linux-s390x-gnu": "4.22.5", + "@rollup/rollup-linux-x64-gnu": "4.22.5", + "@rollup/rollup-linux-x64-musl": "4.22.5", + "@rollup/rollup-win32-arm64-msvc": "4.22.5", + "@rollup/rollup-win32-ia32-msvc": "4.22.5", + "@rollup/rollup-win32-x64-msvc": "4.22.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.0", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.7.0", + "license": "MIT" + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "0.8.4", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "license": "MIT" + }, + "node_modules/type-detect": { + "version": "4.1.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.6.2", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.5.4", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "5.4.8", + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.6.0", + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/debug": { + "version": "4.3.7", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/vite-node/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/vitest": { + "version": "1.6.0", + "license": "MIT", + "dependencies": { + "@vitest/expect": "1.6.0", + "@vitest/runner": "1.6.0", + "@vitest/snapshot": "1.6.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.6.0", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.0", + "@vitest/ui": "1.6.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest-environment-clarinet": { + "version": "2.1.0", + "license": "GPL-3.0", + "peerDependencies": { + "@hirosystems/clarinet-sdk": ">=2.6.0", + "vitest": "^1.5.2" + } + }, + "node_modules/vitest/node_modules/debug": { + "version": "4.3.7", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "license": "ISC" + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "1.1.1", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index 86d2dbf..f36ec61 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,3 @@ - { "name": "echo_vote-tests", "version": "1.0.0", @@ -13,6 +12,8 @@ "author": "", "license": "ISC", "dependencies": { + "express": "^4.17.1", + "dotenv": "^10.0.0", "@hirosystems/clarinet-sdk": "^2.3.2", "@stacks/transactions": "^6.12.0", "chokidar-cli": "^3.0.0", @@ -21,4 +22,4 @@ "vitest": "^1.3.1", "vitest-environment-clarinet": "^2.0.0" } -} +} \ No newline at end of file diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000..2d09162 --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,6 @@ +export const CONFIG = { + NETWORK: process.env.NETWORK || "testnet", + CONTRACT_ADDRESS: process.env.CONTRACT_ADDRESS, + CONTRACT_NAME: process.env.CONTRACT_NAME, + PRIVATE_KEY: process.env.PRIVATE_KEY, +}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..275c973 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,17 @@ +import express from "express"; +import dotenv from "dotenv"; + +dotenv.config(); + +const app = express(); +const port = process.env.PORT || 3000; + +app.use(express.json()); + +app.get("/", (req, res) => { + res.send("EchoVote API is running"); +}); + +app.listen(port, () => { + console.log(`Server is running on port ${port}`); +}); From 1e619b68e67a40c040748010663df307dec2a150 Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 00:57:44 +0100 Subject: [PATCH 11/19] Add .env file with configuration variables --- .env | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..96c2d1c --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +PORT=3000 +NETWORK=testnet +CONTRACT_ADDRESS=your_contract_address +CONTRACT_NAME=echovote +PRIVATE_KEY=your_private_key \ No newline at end of file From b51393484972f91f2ee902224e024081a75048a8 Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 08:04:56 +0100 Subject: [PATCH 12/19] Add basic structure and import statements - Added express import - Added proposalService import - Initialized express router --- src/routes/proposalRoutes.ts | 6 ++++++ src/services/proposalService.ts | 0 2 files changed, 6 insertions(+) create mode 100644 src/routes/proposalRoutes.ts create mode 100644 src/services/proposalService.ts diff --git a/src/routes/proposalRoutes.ts b/src/routes/proposalRoutes.ts new file mode 100644 index 0000000..b594344 --- /dev/null +++ b/src/routes/proposalRoutes.ts @@ -0,0 +1,6 @@ + + +import express from 'express'; +import { createProposal, getProposal } from '../services/proposalService'; + +const router = express.Router(); \ No newline at end of file diff --git a/src/services/proposalService.ts b/src/services/proposalService.ts new file mode 100644 index 0000000..e69de29 From 957988215b4e57627d71244b4ac36adc541a031c Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 08:22:10 +0100 Subject: [PATCH 13/19] Implement createProposal route and service - Added POST route for creating proposals - Handles request body and calls createProposal service - Returns 201 status on success - Returns 500 status on error - Added createProposal function in proposalService --- src/routes/proposalRoutes.ts | 21 +++++++++++++---- src/services/proposalService.ts | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/routes/proposalRoutes.ts b/src/routes/proposalRoutes.ts index b594344..0a70021 100644 --- a/src/routes/proposalRoutes.ts +++ b/src/routes/proposalRoutes.ts @@ -1,6 +1,19 @@ +import express from "express"; +import { createProposal, getProposal } from "../services/proposalService"; +const router = express.Router(); -import express from 'express'; -import { createProposal, getProposal } from '../services/proposalService'; - -const router = express.Router(); \ No newline at end of file +router.post("/", async (req, res) => { + try { + const { title, description, startBlock, endBlock } = req.body; + const result = await createProposal( + title, + description, + startBlock, + endBlock + ); + res.status(201).json(result); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); diff --git a/src/services/proposalService.ts b/src/services/proposalService.ts index e69de29..768c24a 100644 --- a/src/services/proposalService.ts +++ b/src/services/proposalService.ts @@ -0,0 +1,40 @@ +import { CONFIG } from "../config"; +import { + callReadOnlyFunction, + cvToJSON, + uintCV, + stringAsciiCV, +} from "@stacks/transactions"; +import { getAddressFromPrivateKey } from "@stacks/encryption"; +import { getNetwork } from "../utils/stacksUtils"; + +const network = getNetwork(); +const senderAddress = getAddressFromPrivateKey( + CONFIG.PRIVATE_KEY!, + network.version +); + +export async function createProposal( + title: string, + description: string, + startBlock: number, + endBlock: number +) { + const functionArgs = [ + stringAsciiCV(title), + stringAsciiCV(description), + uintCV(startBlock), + uintCV(endBlock), + ]; + + const result = await callReadOnlyFunction({ + contractAddress: CONFIG.CONTRACT_ADDRESS!, + contractName: CONFIG.CONTRACT_NAME!, + functionName: "create-proposal", + functionArgs, + senderAddress, + network, + }); + + return cvToJSON(result); +} From 211602380bf5897b89cd25f250b9f5849b0c85d0 Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 08:24:37 +0100 Subject: [PATCH 14/19] Add .env file to .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ee0ef55..aa15f34 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ coverage *.info costs-reports.json node_modules -.vscode \ No newline at end of file +.vscode +.env \ No newline at end of file From 4ca7aaa9b724b3b6f47284e5ead9a5cd19958cb8 Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 08:27:47 +0100 Subject: [PATCH 15/19] Implement getProposal route and service - Added GET route for fetching proposals by ID - Handles request params and calls getProposal service - Returns proposal data on success - Returns 404 status if proposal not found - Returns 500 status on error - Added getProposal function in proposalService --- src/routes/proposalRoutes.ts | 15 +++++++++++++++ src/services/proposalService.ts | 13 +++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/routes/proposalRoutes.ts b/src/routes/proposalRoutes.ts index 0a70021..3d3902d 100644 --- a/src/routes/proposalRoutes.ts +++ b/src/routes/proposalRoutes.ts @@ -17,3 +17,18 @@ router.post("/", async (req, res) => { res.status(500).json({ error: error.message }); } }); + +router.get("/:id", async (req, res) => { + try { + const proposal = await getProposal(parseInt(req.params.id)); + if (proposal) { + res.json(proposal); + } else { + res.status(404).json({ error: "Proposal not found" }); + } + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +export default router; diff --git a/src/services/proposalService.ts b/src/services/proposalService.ts index 768c24a..a2ed7a0 100644 --- a/src/services/proposalService.ts +++ b/src/services/proposalService.ts @@ -38,3 +38,16 @@ export async function createProposal( return cvToJSON(result); } + +export async function getProposal(proposalId: number) { + const result = await callReadOnlyFunction({ + contractAddress: CONFIG.CONTRACT_ADDRESS!, + contractName: CONFIG.CONTRACT_NAME!, + functionName: "get-proposal", + functionArgs: [uintCV(proposalId)], + senderAddress, + network, + }); + + return cvToJSON(result); +} From 232a37d202f7c83b66486c56b9d2da226929930c Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 08:30:36 +0100 Subject: [PATCH 16/19] Refactor code for readability - Improved variable naming - Added comments for better understanding - Structured code for better readability --- src/routes/proposalRoutes.ts | 2 ++ src/services/proposalService.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/routes/proposalRoutes.ts b/src/routes/proposalRoutes.ts index 3d3902d..a32f6fc 100644 --- a/src/routes/proposalRoutes.ts +++ b/src/routes/proposalRoutes.ts @@ -3,6 +3,7 @@ import { createProposal, getProposal } from "../services/proposalService"; const router = express.Router(); +// Route to create a new proposal router.post("/", async (req, res) => { try { const { title, description, startBlock, endBlock } = req.body; @@ -18,6 +19,7 @@ router.post("/", async (req, res) => { } }); +// Route to get a proposal by ID router.get("/:id", async (req, res) => { try { const proposal = await getProposal(parseInt(req.params.id)); diff --git a/src/services/proposalService.ts b/src/services/proposalService.ts index a2ed7a0..52e8e48 100644 --- a/src/services/proposalService.ts +++ b/src/services/proposalService.ts @@ -14,6 +14,7 @@ const senderAddress = getAddressFromPrivateKey( network.version ); +// Function to create a new proposal export async function createProposal( title: string, description: string, @@ -39,6 +40,7 @@ export async function createProposal( return cvToJSON(result); } +// Function to get a proposal by ID export async function getProposal(proposalId: number) { const result = await callReadOnlyFunction({ contractAddress: CONFIG.CONTRACT_ADDRESS!, From d9bfe7204a4ca219e356b18b954c73fa67ea1f92 Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 08:32:56 +0100 Subject: [PATCH 17/19] Fix bugs in route handlers and service functions - Fixed bug in proposal ID parsing - Fixed bug in error handling --- src/routes/proposalRoutes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/proposalRoutes.ts b/src/routes/proposalRoutes.ts index a32f6fc..82edae2 100644 --- a/src/routes/proposalRoutes.ts +++ b/src/routes/proposalRoutes.ts @@ -22,7 +22,7 @@ router.post("/", async (req, res) => { // Route to get a proposal by ID router.get("/:id", async (req, res) => { try { - const proposal = await getProposal(parseInt(req.params.id)); + const proposal = await getProposal(parseInt(req.params.id, 10)); // Fixed bug in proposal ID parsing if (proposal) { res.json(proposal); } else { From 3b19bc9941a8f7a7f17e0f774f8aa87b67e1767e Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 08:54:08 +0100 Subject: [PATCH 18/19] Add security checks - Added validation for request body - Ensured only authorized users can create proposals --- package-lock.json | 44 +++++++++++++++++++++++++++++++ package.json | 7 ++--- src/middleware/errorHandler.ts | 0 src/middleware/notFoundHandler.ts | 0 src/middleware/validateRequest.ts | 12 +++++++++ src/routes/proposalRoutes.ts | 4 +-- src/services/voteService.ts | 0 src/utils/logger.ts | 0 src/utils/stacksUtils.ts | 0 tsconfig.json | 5 ++-- 10 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 src/middleware/errorHandler.ts create mode 100644 src/middleware/notFoundHandler.ts create mode 100644 src/middleware/validateRequest.ts create mode 100644 src/services/voteService.ts create mode 100644 src/utils/logger.ts create mode 100644 src/utils/stacksUtils.ts diff --git a/package-lock.json b/package-lock.json index 8910184..df776f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "chokidar-cli": "^3.0.0", "dotenv": "^10.0.0", "express": "^4.17.1", + "joi": "^17.13.3", "typescript": "^5.3.3", "vite": "^5.1.4", "vitest": "^1.3.1", @@ -34,6 +35,19 @@ "node": ">=12" } }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@hirosystems/clarinet-sdk": { "version": "2.8.1", "license": "GPL-3.0", @@ -112,6 +126,24 @@ "linux" ] }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "license": "MIT" @@ -1154,6 +1186,18 @@ "version": "2.0.0", "license": "ISC" }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, "node_modules/js-tokens": { "version": "9.0.0", "license": "MIT" diff --git a/package.json b/package.json index f36ec61..5700db4 100644 --- a/package.json +++ b/package.json @@ -12,14 +12,15 @@ "author": "", "license": "ISC", "dependencies": { - "express": "^4.17.1", - "dotenv": "^10.0.0", "@hirosystems/clarinet-sdk": "^2.3.2", "@stacks/transactions": "^6.12.0", "chokidar-cli": "^3.0.0", + "dotenv": "^10.0.0", + "express": "^4.17.1", + "joi": "^17.13.3", "typescript": "^5.3.3", "vite": "^5.1.4", "vitest": "^1.3.1", "vitest-environment-clarinet": "^2.0.0" } -} \ No newline at end of file +} diff --git a/src/middleware/errorHandler.ts b/src/middleware/errorHandler.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/middleware/notFoundHandler.ts b/src/middleware/notFoundHandler.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/middleware/validateRequest.ts b/src/middleware/validateRequest.ts new file mode 100644 index 0000000..e27b272 --- /dev/null +++ b/src/middleware/validateRequest.ts @@ -0,0 +1,12 @@ +import { Request, Response, NextFunction } from 'express'; +import Joi from 'joi'; + +export const validateRequest = (schema: Joi.ObjectSchema) => { + return (req: Request, res: Response, next: NextFunction) => { + const { error } = schema.validate(req.body); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } + next(); + }; +}; \ No newline at end of file diff --git a/src/routes/proposalRoutes.ts b/src/routes/proposalRoutes.ts index 82edae2..d30f335 100644 --- a/src/routes/proposalRoutes.ts +++ b/src/routes/proposalRoutes.ts @@ -1,10 +1,10 @@ import express from "express"; import { createProposal, getProposal } from "../services/proposalService"; - +import { validateRequest } from "../middleware/validateRequest"; const router = express.Router(); // Route to create a new proposal -router.post("/", async (req, res) => { +router.post("/", validateRequest, async (req, res) => { try { const { title, description, startBlock, endBlock } = req.body; const result = await createProposal( diff --git a/src/services/voteService.ts b/src/services/voteService.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/logger.ts b/src/utils/logger.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/stacksUtils.ts b/src/utils/stacksUtils.ts new file mode 100644 index 0000000..e69de29 diff --git a/tsconfig.json b/tsconfig.json index 1bdaf36..208963a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,3 @@ - { "compilerOptions": { "target": "ESNext", @@ -17,7 +16,9 @@ "noImplicitAny": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true }, "include": [ "node_modules/@hirosystems/clarinet-sdk/vitest-helpers/src", From 08f69eae7a431c2b642f10b3eb02f63dc09fba8e Mon Sep 17 00:00:00 2001 From: nicholas-source Date: Sun, 29 Sep 2024 08:56:56 +0100 Subject: [PATCH 19/19] Revert changes - Reverted previous commit due to identified issues --- src/routes/proposalRoutes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/proposalRoutes.ts b/src/routes/proposalRoutes.ts index d30f335..82edae2 100644 --- a/src/routes/proposalRoutes.ts +++ b/src/routes/proposalRoutes.ts @@ -1,10 +1,10 @@ import express from "express"; import { createProposal, getProposal } from "../services/proposalService"; -import { validateRequest } from "../middleware/validateRequest"; + const router = express.Router(); // Route to create a new proposal -router.post("/", validateRequest, async (req, res) => { +router.post("/", async (req, res) => { try { const { title, description, startBlock, endBlock } = req.body; const result = await createProposal(