From d89e32eef4db37748c0eaf976b61791e534e7178 Mon Sep 17 00:00:00 2001 From: friedger Date: Mon, 15 Sep 2025 12:02:32 +0200 Subject: [PATCH 1/6] fix: use correct annotation object --- example/tests/my-contract_test.clar | 1 + src/clarunit-generator.ts | 8 ++++---- tests/clarity-parser-flow.test.ts | 8 ++++++++ tests/clarity-parser.test.ts | 4 ++++ tests/contracts/parser-tests/all-annotations.clar | 4 ++++ tests/contracts/parser-tests/simple-flow.clar | 2 ++ 6 files changed, 23 insertions(+), 4 deletions(-) diff --git a/example/tests/my-contract_test.clar b/example/tests/my-contract_test.clar index 576f5ec..3a1623d 100644 --- a/example/tests/my-contract_test.clar +++ b/example/tests/my-contract_test.clar @@ -7,6 +7,7 @@ ) ) +;; @caller 'ST000000000000000000002AMW42H (define-public (test-a-times-b2) (begin (asserts! (is-eq (ok u108) (contract-call? .my-contract a-times-b u9 u12)) diff --git a/src/clarunit-generator.ts b/src/clarunit-generator.ts index 9e225c2..681b6f2 100644 --- a/src/clarunit-generator.ts +++ b/src/clarunit-generator.ts @@ -56,10 +56,10 @@ export function generateUnitTests(simnet: Simnet) { delete functionAnnotations.prepare; // handle caller address for this test - const callerAddress = functionAnnotations.caller - ? annotations.caller[0] === "'" - ? `${(annotations.caller as string).substring(1)}` - : accounts.get(annotations.caller)! + const callerAddress = functionAnnotations.caller && typeof functionAnnotations.caller === "string" + ? functionAnnotations.caller[0] === "'" + ? `${(functionAnnotations.caller as string).substring(1)}` + : accounts.get(functionAnnotations.caller)! : accounts.get("deployer")!; if (functionAnnotations.prepare) { diff --git a/tests/clarity-parser-flow.test.ts b/tests/clarity-parser-flow.test.ts index b920136..dac478a 100644 --- a/tests/clarity-parser-flow.test.ts +++ b/tests/clarity-parser-flow.test.ts @@ -53,6 +53,14 @@ describe("verify clarity parser for flow tests", () => { functionName: "allow-contract-caller", }, }); + expect(callInfos["test-simple-flow"][3]).toEqual({ + callAnnotations: { caller: "'ST000000000000000000002AMW42H" }, + callInfo: { + args: [], + contractName: "", + functionName: "my-test-function", + }, + }); }); it("should parse flow test with bad annotations", () => { diff --git a/tests/clarity-parser.test.ts b/tests/clarity-parser.test.ts index 3673afe..1721dff 100644 --- a/tests/clarity-parser.test.ts +++ b/tests/clarity-parser.test.ts @@ -46,6 +46,10 @@ describe("verify clarity parser", () => { "mine-before": "20", name: "all annotation test 2", }); + + expect(result["test-all-annotations-3"]).toEqual({ + caller: "'ST000000000000000000002AMW42H", + }); }); it("should parse with bad annotations", () => { diff --git a/tests/contracts/parser-tests/all-annotations.clar b/tests/contracts/parser-tests/all-annotations.clar index 3fec2c0..67a6c8b 100644 --- a/tests/contracts/parser-tests/all-annotations.clar +++ b/tests/contracts/parser-tests/all-annotations.clar @@ -10,4 +10,8 @@ ;; @mine-before 20 ;; @caller wallet_2 (define-public (test-all-annotations-2) + (ok true)) + +;; @caller 'ST000000000000000000002AMW42H +(define-public (test-all-annotations-3) (ok true)) \ No newline at end of file diff --git a/tests/contracts/parser-tests/simple-flow.clar b/tests/contracts/parser-tests/simple-flow.clar index 558dbbb..542310a 100644 --- a/tests/contracts/parser-tests/simple-flow.clar +++ b/tests/contracts/parser-tests/simple-flow.clar @@ -7,6 +7,8 @@ (try! (my-test-function2)) ;; @caller wallet_1 (unwrap! (contract-call? 'ST000000000000000000002AMW42H.pox-4 allow-contract-caller .pox4-self-service-multi none) (err "allow-contract-caller failed")) + ;; @caller 'ST000000000000000000002AMW42H + (try! (my-test-function)) (ok true))) (define-public (my-test-function) From 95a5d027a4224a33fc064c6b9e9e96a58ca7a416 Mon Sep 17 00:00:00 2001 From: friedger Date: Mon, 15 Sep 2025 12:25:23 +0200 Subject: [PATCH 2/6] fix: add generator tests --- Clarinet.toml | 5 ++++ deployments/default.simnet-plan.yaml | 10 ++++++- src/clarunit-generator.ts | 25 ++++++++--------- tests/clarunit.test.ts | 3 +++ .../generator-tests/annotations_test.clar | 27 +++++++++++++++++++ 5 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 tests/clarunit.test.ts create mode 100644 tests/contracts/generator-tests/annotations_test.clar diff --git a/Clarinet.toml b/Clarinet.toml index 7731877..eb7c7c0 100644 --- a/Clarinet.toml +++ b/Clarinet.toml @@ -6,6 +6,11 @@ telemetry = false cache_dir = './.cache' requirements = [] +[contracts.annotations_test] +path = "tests/contracts/generator-tests/annotations_test.clar" +clarity_version = 3 +epoch = "3.1" + [repl.analysis] passes = ['check_checker'] diff --git a/deployments/default.simnet-plan.yaml b/deployments/default.simnet-plan.yaml index 81393c0..923ffd6 100644 --- a/deployments/default.simnet-plan.yaml +++ b/deployments/default.simnet-plan.yaml @@ -56,4 +56,12 @@ genesis: - cost-voting - bns plan: - batches: [] + batches: + - id: 0 + transactions: + - emulated-contract-publish: + contract-name: annotations_test + emulated-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + path: tests/contracts/generator-tests/annotations_test.clar + clarity-version: 3 + epoch: "3.1" diff --git a/src/clarunit-generator.ts b/src/clarunit-generator.ts index 681b6f2..36903ce 100644 --- a/src/clarunit-generator.ts +++ b/src/clarunit-generator.ts @@ -1,15 +1,13 @@ import { Simnet, tx } from "@hirosystems/clarinet-sdk"; import { describe, it } from "vitest"; -import { - extractTestAnnotations, -} from "./parser/clarity-parser"; +import { extractTestAnnotations } from "./parser/clarity-parser"; import { expectOkTrue, isValidTestFunction } from "./parser/test-helpers"; import { FunctionAnnotations } from "./parser/clarity-parser-flow-tests"; /** * Returns true if the contract is a test contract * @param contractName name of the contract - * @returns + * @returns */ function isTestContract(contractName: string) { return ( @@ -44,10 +42,11 @@ export function generateUnitTests(simnet: Simnet) { annotations[functionName] || {}; const mineBlocksBefore = - parseInt(annotations["mine-blocks-before"] as string) || 0; + parseInt(functionAnnotations["mine-blocks-before"] as string) || 0; - const testDescription = `${functionCall.name}${functionAnnotations.name ? `: ${functionAnnotations.name}` : "" - }`; + const testDescription = `${functionCall.name}${ + functionAnnotations.name ? `: ${functionAnnotations.name}` : "" + }`; it(testDescription, () => { // handle prepare function for this test if (hasDefaultPrepareFunction && !functionAnnotations.prepare) @@ -56,11 +55,13 @@ export function generateUnitTests(simnet: Simnet) { delete functionAnnotations.prepare; // handle caller address for this test - const callerAddress = functionAnnotations.caller && typeof functionAnnotations.caller === "string" - ? functionAnnotations.caller[0] === "'" - ? `${(functionAnnotations.caller as string).substring(1)}` - : accounts.get(functionAnnotations.caller)! - : accounts.get("deployer")!; + const callerAddress = + functionAnnotations.caller && + typeof functionAnnotations.caller === "string" + ? functionAnnotations.caller[0] === "'" + ? `${(functionAnnotations.caller as string).substring(1)}` + : accounts.get(functionAnnotations.caller)! + : accounts.get("deployer")!; if (functionAnnotations.prepare) { // mine block with prepare function call diff --git a/tests/clarunit.test.ts b/tests/clarunit.test.ts new file mode 100644 index 0000000..150b348 --- /dev/null +++ b/tests/clarunit.test.ts @@ -0,0 +1,3 @@ +import { clarunit } from "../src/index"; + +clarunit(simnet); diff --git a/tests/contracts/generator-tests/annotations_test.clar b/tests/contracts/generator-tests/annotations_test.clar new file mode 100644 index 0000000..38d4a63 --- /dev/null +++ b/tests/contracts/generator-tests/annotations_test.clar @@ -0,0 +1,27 @@ + +;; test block-height at launch +;; One block is need to advance to epoch 2.5 +(define-public (test-block-height-at-launch) + (begin + (asserts! (is-eq u3 stacks-block-height) (err (concat "expected block height 3, found " (int-to-ascii stacks-block-height)))) + (ok true))) + +;; @mine-blocks-before 10 +(define-public (test-mine-blocks-before) + (begin + (asserts! (is-eq u13 stacks-block-height) (err (concat "expected block height 13, found " (int-to-ascii stacks-block-height)))) + (ok true))) + +;; @caller wallet_1 +(define-public (test-caller) + (begin + (asserts! (is-eq tx-sender 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5) (err tx-sender)) + (asserts! (is-eq contract-caller 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5) (err contract-caller)) + (ok true))) + +;; @caller 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE +(define-public (test-caller-2) + (begin + (asserts! (is-eq tx-sender 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE) (err tx-sender)) + (asserts! (is-eq contract-caller 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE) (err contract-caller)) + (ok true))) \ No newline at end of file From 25b1b54106125bc2e70c57204ebe281b00d0dd4a Mon Sep 17 00:00:00 2001 From: friedger Date: Mon, 15 Sep 2025 18:40:19 +0200 Subject: [PATCH 3/6] fix: add flow test --- Clarinet.toml | 5 ++ deployments/default.simnet-plan.yaml | 5 ++ .../annotations_flow_test.clar | 33 ++++++++++++ .../generator-tests/annotations_test.clar | 53 +++++++++++++------ 4 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 tests/contracts/generator-tests/annotations_flow_test.clar diff --git a/Clarinet.toml b/Clarinet.toml index eb7c7c0..61fe30d 100644 --- a/Clarinet.toml +++ b/Clarinet.toml @@ -11,6 +11,11 @@ path = "tests/contracts/generator-tests/annotations_test.clar" clarity_version = 3 epoch = "3.1" +[contracts.annotations_flow_test] +path = "tests/contracts/generator-tests/annotations_flow_test.clar" +clarity_version = 3 +epoch = "3.1" + [repl.analysis] passes = ['check_checker'] diff --git a/deployments/default.simnet-plan.yaml b/deployments/default.simnet-plan.yaml index 923ffd6..e01a6a4 100644 --- a/deployments/default.simnet-plan.yaml +++ b/deployments/default.simnet-plan.yaml @@ -59,6 +59,11 @@ plan: batches: - id: 0 transactions: + - emulated-contract-publish: + contract-name: annotations_flow_test + emulated-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + path: tests/contracts/generator-tests/annotations_flow_test.clar + clarity-version: 3 - emulated-contract-publish: contract-name: annotations_test emulated-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM diff --git a/tests/contracts/generator-tests/annotations_flow_test.clar b/tests/contracts/generator-tests/annotations_flow_test.clar new file mode 100644 index 0000000..e7ba840 --- /dev/null +++ b/tests/contracts/generator-tests/annotations_flow_test.clar @@ -0,0 +1,33 @@ +;; @name test block height at launch +(define-public (test-block-height-at-launch) + (begin + ;; @caller wallet_1 + (try! (assert-block-height-3)) + ;; @mine-blocks-before 10 + ;; @caller wallet_1 + (try! (assert-block-height-13)) + (ok true) + ) +) + +(define-public (assert-block-height-3) + (begin + (asserts! (is-eq u3 stacks-block-height) + (err (concat "expected block height 3, found " + (int-to-ascii stacks-block-height) + )) + ) + (ok true) + ) +) + +(define-public (assert-block-height-13) + (begin + (asserts! (is-eq u13 stacks-block-height) + (err (concat "expected block height 13, found " + (int-to-ascii stacks-block-height) + )) + ) + (ok true) + ) +) diff --git a/tests/contracts/generator-tests/annotations_test.clar b/tests/contracts/generator-tests/annotations_test.clar index 38d4a63..e8a9e64 100644 --- a/tests/contracts/generator-tests/annotations_test.clar +++ b/tests/contracts/generator-tests/annotations_test.clar @@ -1,27 +1,50 @@ - ;; test block-height at launch ;; One block is need to advance to epoch 2.5 (define-public (test-block-height-at-launch) - (begin - (asserts! (is-eq u3 stacks-block-height) (err (concat "expected block height 3, found " (int-to-ascii stacks-block-height)))) - (ok true))) + (begin + (asserts! (is-eq u3 stacks-block-height) + (err (concat "expected block height 3, found " + (int-to-ascii stacks-block-height) + )) + ) + (ok true) + ) +) ;; @mine-blocks-before 10 (define-public (test-mine-blocks-before) - (begin - (asserts! (is-eq u13 stacks-block-height) (err (concat "expected block height 13, found " (int-to-ascii stacks-block-height)))) - (ok true))) + (begin + (asserts! (is-eq u13 stacks-block-height) + (err (concat "expected block height 13, found " + (int-to-ascii stacks-block-height) + )) + ) + (ok true) + ) +) ;; @caller wallet_1 (define-public (test-caller) - (begin - (asserts! (is-eq tx-sender 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5) (err tx-sender)) - (asserts! (is-eq contract-caller 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5) (err contract-caller)) - (ok true))) + (begin + (asserts! (is-eq tx-sender 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5) + (err tx-sender) + ) + (asserts! (is-eq contract-caller 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5) + (err contract-caller) + ) + (ok true) + ) +) ;; @caller 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE (define-public (test-caller-2) - (begin - (asserts! (is-eq tx-sender 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE) (err tx-sender)) - (asserts! (is-eq contract-caller 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE) (err contract-caller)) - (ok true))) \ No newline at end of file + (begin + (asserts! (is-eq tx-sender 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE) + (err tx-sender) + ) + (asserts! (is-eq contract-caller 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE) + (err contract-caller) + ) + (ok true) + ) +) From 1aead92a0767ecc831195be480dfcfd6f21c35bd Mon Sep 17 00:00:00 2001 From: friedger Date: Mon, 15 Sep 2025 18:55:13 +0200 Subject: [PATCH 4/6] feat: add support for address in flow tests --- src/clarunit-flow-generator.ts | 5 ++--- src/clarunit-generator.ts | 9 ++------- src/clarunit-utils.ts | 7 +++++++ .../contracts/generator-tests/annotations_flow_test.clar | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 src/clarunit-utils.ts diff --git a/src/clarunit-flow-generator.ts b/src/clarunit-flow-generator.ts index 19b3303..db83778 100644 --- a/src/clarunit-flow-generator.ts +++ b/src/clarunit-flow-generator.ts @@ -8,6 +8,7 @@ import { extractTestAnnotationsAndCalls, } from "./parser/clarity-parser-flow-tests"; import { expectOk, isValidTestFunction } from "./parser/test-helpers"; +import { getCaller } from "./clarunit-utils"; import path from "path"; /** @@ -100,9 +101,7 @@ function mineBlocksFromFunctionBody( const mineBlocksBefore = parseInt(callAnnotations["mine-blocks-before"] as string) || 0; // get caller address - const caller = accounts.get( - (callAnnotations["caller"] as string) || "deployer" - )!; + const caller = getCaller(callAnnotations, accounts); if (mineBlocksBefore >= 1) { if (blockStarted) { diff --git a/src/clarunit-generator.ts b/src/clarunit-generator.ts index 36903ce..a211cb5 100644 --- a/src/clarunit-generator.ts +++ b/src/clarunit-generator.ts @@ -3,6 +3,7 @@ import { describe, it } from "vitest"; import { extractTestAnnotations } from "./parser/clarity-parser"; import { expectOkTrue, isValidTestFunction } from "./parser/test-helpers"; import { FunctionAnnotations } from "./parser/clarity-parser-flow-tests"; +import { getCaller } from "./clarunit-utils"; /** * Returns true if the contract is a test contract @@ -55,13 +56,7 @@ export function generateUnitTests(simnet: Simnet) { delete functionAnnotations.prepare; // handle caller address for this test - const callerAddress = - functionAnnotations.caller && - typeof functionAnnotations.caller === "string" - ? functionAnnotations.caller[0] === "'" - ? `${(functionAnnotations.caller as string).substring(1)}` - : accounts.get(functionAnnotations.caller)! - : accounts.get("deployer")!; + const callerAddress = getCaller(functionAnnotations, accounts); if (functionAnnotations.prepare) { // mine block with prepare function call diff --git a/src/clarunit-utils.ts b/src/clarunit-utils.ts new file mode 100644 index 0000000..bf7e47a --- /dev/null +++ b/src/clarunit-utils.ts @@ -0,0 +1,7 @@ +export const getCaller = (annotations: any, accounts: Map) => { + return annotations.caller && typeof annotations.caller === "string" + ? annotations.caller[0] === "'" + ? `${(annotations.caller as string).substring(1)}` + : accounts.get(annotations.caller)! + : accounts.get("deployer")!; +}; diff --git a/tests/contracts/generator-tests/annotations_flow_test.clar b/tests/contracts/generator-tests/annotations_flow_test.clar index e7ba840..ffed113 100644 --- a/tests/contracts/generator-tests/annotations_flow_test.clar +++ b/tests/contracts/generator-tests/annotations_flow_test.clar @@ -1,7 +1,7 @@ ;; @name test block height at launch (define-public (test-block-height-at-launch) (begin - ;; @caller wallet_1 + ;; @caller 'SP1T91N2Y2TE5M937FE3R6DE0HGWD85SGCV50T95A (try! (assert-block-height-3)) ;; @mine-blocks-before 10 ;; @caller wallet_1 From 0d0f143454852edcc0000f44dc1608ee72534dd7 Mon Sep 17 00:00:00 2001 From: friedger Date: Tue, 16 Sep 2025 15:03:39 +0200 Subject: [PATCH 5/6] feat: add support for multi line contract calls in flow tests --- .gitignore | 3 +- Clarinet.toml | 10 +- deployments/default.simnet-plan.yaml | 167 ++++++++++++++++++ src/parser/clarity-parser-flow-tests.ts | 20 ++- src/parser/string-to-cv.ts | 7 + .../contract-call_flow_test.clar | 16 ++ 6 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 tests/contracts/generator-tests/contract-call_flow_test.clar diff --git a/.gitignore b/.gitignore index 4ddd794..e5d2b67 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ coverage costs-reports.json node_modules *.log.txt -history.txt \ No newline at end of file +history.txt +.cache \ No newline at end of file diff --git a/Clarinet.toml b/Clarinet.toml index 61fe30d..471788e 100644 --- a/Clarinet.toml +++ b/Clarinet.toml @@ -4,7 +4,10 @@ description = '' authors = [] telemetry = false cache_dir = './.cache' -requirements = [] +requirements = [ + { contract_id = "SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd001-direct-execute"}, + { contract_id = "SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccip024-miamicoin-signal-vote"} +] [contracts.annotations_test] path = "tests/contracts/generator-tests/annotations_test.clar" @@ -16,6 +19,11 @@ path = "tests/contracts/generator-tests/annotations_flow_test.clar" clarity_version = 3 epoch = "3.1" +[contracts.contract_call_flow_test] +path = "tests/contracts/generator-tests/contract-call_flow_test.clar" +clarity_version = 3 +epoch = "3.1" + [repl.analysis] passes = ['check_checker'] diff --git a/deployments/default.simnet-plan.yaml b/deployments/default.simnet-plan.yaml index e01a6a4..99c15e1 100644 --- a/deployments/default.simnet-plan.yaml +++ b/deployments/default.simnet-plan.yaml @@ -58,6 +58,168 @@ genesis: plan: batches: - id: 0 + transactions: + - emulated-contract-publish: + contract-name: citycoin-core-trait + emulated-sender: SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27 + path: "./.cache/requirements/SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27.citycoin-core-trait.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: citycoin-token-trait + emulated-sender: SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27 + path: "./.cache/requirements/SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27.citycoin-token-trait.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: miamicoin-auth + emulated-sender: SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27 + path: "./.cache/requirements/SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27.miamicoin-auth.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: sip-010-trait-ft-standard + emulated-sender: SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE + path: "./.cache/requirements/SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: miamicoin-token + emulated-sender: SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27 + path: "./.cache/requirements/SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27.miamicoin-token.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: extension-trait + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.extension-trait.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: proposal-trait + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.proposal-trait.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: base-dao + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.base-dao.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: ccd001-direct-execute + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd001-direct-execute.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: nft-trait + emulated-sender: SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9 + path: "./.cache/requirements/SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: ccd002-trait + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd002-trait.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: stacking-trait + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.stacking-trait.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: ccd002-treasury-mia-stacking + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd002-treasury-mia-stacking.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: ccd002-treasury-nyc-stacking + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd002-treasury-nyc-stacking.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: ccd003-user-registry + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd003-user-registry.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: ccd004-city-registry + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd004-city-registry.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: ccd005-city-data + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd005-city-data.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: ccd007-trait + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd007-trait.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: citycoin-token-v2-trait + emulated-sender: SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11 + path: "./.cache/requirements/SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11.citycoin-token-v2-trait.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: citycoin-core-v2-trait + emulated-sender: SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11 + path: "./.cache/requirements/SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11.citycoin-core-v2-trait.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: newyorkcitycoin-auth-v2 + emulated-sender: SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11 + path: "./.cache/requirements/SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11.newyorkcitycoin-auth-v2.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: newyorkcitycoin-auth + emulated-sender: SP2H8PY27SEZ03MWRKS5XABZYQN17ETGQS3527SA5 + path: "./.cache/requirements/SP2H8PY27SEZ03MWRKS5XABZYQN17ETGQS3527SA5.newyorkcitycoin-auth.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: newyorkcitycoin-token + emulated-sender: SP2H8PY27SEZ03MWRKS5XABZYQN17ETGQS3527SA5 + path: "./.cache/requirements/SP2H8PY27SEZ03MWRKS5XABZYQN17ETGQS3527SA5.newyorkcitycoin-token.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: newyorkcitycoin-token-v2 + emulated-sender: SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11 + path: "./.cache/requirements/SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11.newyorkcitycoin-token-v2.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: miamicoin-auth-v2 + emulated-sender: SP1H1733V5MZ3SZ9XRW9FKYGEZT0JDGEB8Y634C7R + path: "./.cache/requirements/SP1H1733V5MZ3SZ9XRW9FKYGEZT0JDGEB8Y634C7R.miamicoin-auth-v2.clar" + clarity-version: 1 + epoch: "2.1" + - id: 1 + transactions: + - emulated-contract-publish: + contract-name: miamicoin-core-v1-patch + emulated-sender: SP1H1733V5MZ3SZ9XRW9FKYGEZT0JDGEB8Y634C7R + path: "./.cache/requirements/SP1H1733V5MZ3SZ9XRW9FKYGEZT0JDGEB8Y634C7R.miamicoin-core-v1-patch.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: miamicoin-token-v2 + emulated-sender: SP1H1733V5MZ3SZ9XRW9FKYGEZT0JDGEB8Y634C7R + path: "./.cache/requirements/SP1H1733V5MZ3SZ9XRW9FKYGEZT0JDGEB8Y634C7R.miamicoin-token-v2.clar" + clarity-version: 1 + - emulated-contract-publish: + contract-name: ccd007-citycoin-stacking + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd007-citycoin-stacking.clar" + clarity-version: 1 + epoch: "2.1" + - id: 2 + transactions: + - emulated-contract-publish: + contract-name: ccip015-trait + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccip015-trait.clar" + clarity-version: 2 + epoch: "2.4" + - id: 3 + transactions: + - emulated-contract-publish: + contract-name: ccip024-miamicoin-signal-vote + emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH + path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccip024-miamicoin-signal-vote.clar" + clarity-version: 2 + epoch: "2.5" + - id: 4 transactions: - emulated-contract-publish: contract-name: annotations_flow_test @@ -69,4 +231,9 @@ plan: emulated-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM path: tests/contracts/generator-tests/annotations_test.clar clarity-version: 3 + - emulated-contract-publish: + contract-name: contract_call_flow_test + emulated-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + path: tests/contracts/generator-tests/contract-call_flow_test.clar + clarity-version: 3 epoch: "3.1" diff --git a/src/parser/clarity-parser-flow-tests.ts b/src/parser/clarity-parser-flow-tests.ts index 3ac09f3..63a8bee 100644 --- a/src/parser/clarity-parser-flow-tests.ts +++ b/src/parser/clarity-parser-flow-tests.ts @@ -114,7 +114,7 @@ export function extractContractCalls(lastFunctionBody: string, simnet: Simnet) { if (prop) callAnnotations[prop] = value ?? true; } // try to extract call info from (unwrap! (contract-call? ...)) - let callInfo = extractUnwrapInfo(call, simnet); + let callInfo = extractUnwrapInfo(call, simnet, callAnnotations); if (!callInfo) { // try to extract call info from (try! (my-function)) callInfo = extractTryInfo(call); @@ -133,9 +133,13 @@ export function extractContractCalls(lastFunctionBody: string, simnet: Simnet) { * @param statement * @returns */ -function extractUnwrapInfo(statement: string, simnet: Simnet): CallInfo | null { +function extractUnwrapInfo( + statement: string, + simnet: Simnet, + callAnnotations: FunctionAnnotations +): CallInfo | null { const match = statement.match( - /\(unwrap! \(contract-call\? (?:\.(.+?)|'(.+?)) (.+?)(( .+?)*)\)/ + /\(unwrap!\s+\(contract-call\?\s+(?:\.(.+?)|'(.+?))\s+(.+?)((\s+.+?)*)\)/m ); if (!match) return null; // match[1] is the contract address, @@ -155,7 +159,15 @@ function extractUnwrapInfo(statement: string, simnet: Simnet): CallInfo | null { } }); if (!fn) { - throw `function ${functionName} not found in contract ${contractName}`; + if (callAnnotations["type-hints"]) { + fn = { + args: (callAnnotations["type-hints"] as string) + .split(",") + .map((s) => ({ type: s.trim() })), + }; + } else { + throw `function ${functionName} not found in contract ${contractName} and not type-hints provided`; + } } const args = fn.args.map((arg: any, index: number) => stringToCV(argStrings[index], arg.type) diff --git a/src/parser/string-to-cv.ts b/src/parser/string-to-cv.ts index 912c3ac..1023398 100644 --- a/src/parser/string-to-cv.ts +++ b/src/parser/string-to-cv.ts @@ -75,6 +75,13 @@ export function stringToCV( : { type: "principal", value: Cl.standardPrincipal(address) }; case "bool": return { type: "bool", value: Cl.bool(arg === "true") }; + case "trait_reference": + console.log("trait_reference", arg); + const [addressTrait, nameTrait] = arg.split("."); + return { + type: "trait_reference", + value: Cl.contractPrincipal(addressTrait.substring(1), nameTrait), + }; } const typeDescriptor = Object.keys(type)[0]; switch (typeDescriptor) { diff --git a/tests/contracts/generator-tests/contract-call_flow_test.clar b/tests/contracts/generator-tests/contract-call_flow_test.clar new file mode 100644 index 0000000..e383560 --- /dev/null +++ b/tests/contracts/generator-tests/contract-call_flow_test.clar @@ -0,0 +1,16 @@ +;; @name test external contract call +(define-public (test-contract-call) + (begin + ;; @caller 'SP7DGES13508FHRWS1FB0J3SZA326FP6QRMB6JDE + ;; @type-hints trait_reference + (unwrap! + (contract-call? + 'SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd001-direct-execute + direct-execute + 'SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccip024-miamicoin-signal-vote + ) + (err "direct execute failed") + ) + (ok true) + ) +) From 8115ef575e40c0925c016682d0050c94b0d7b996 Mon Sep 17 00:00:00 2001 From: friedger Date: Fri, 26 Sep 2025 13:09:16 +0200 Subject: [PATCH 6/6] feat: add better type-hints support, use remote_data --- Clarinet.toml | 8 + deployments/default.simnet-plan.yaml | 201 ++---------------- package.json | 6 +- src/parser/clarity-parser-flow-tests.ts | 47 +++- src/parser/string-to-cv.ts | 13 +- tests/clarity-parser-flow.test.ts | 7 +- tests/clarunit.test.ts | 1 - .../annotations_flow_test.clar | 8 +- .../generator-tests/annotations_test.clar | 12 +- tests/contracts/parser-tests/simple-flow.clar | 1 + 10 files changed, 101 insertions(+), 203 deletions(-) diff --git a/Clarinet.toml b/Clarinet.toml index 471788e..b9edec7 100644 --- a/Clarinet.toml +++ b/Clarinet.toml @@ -32,3 +32,11 @@ strict = false trusted_sender = false trusted_caller = false callee_filter = false + + +[repl.remote_data] +# Enable mainnet execution simulation +enabled = true +# Specify the Stacks block height to fork from +initial_height = 3491155 +use_mainnet_wallets = true \ No newline at end of file diff --git a/deployments/default.simnet-plan.yaml b/deployments/default.simnet-plan.yaml index 99c15e1..0cd8dcf 100644 --- a/deployments/default.simnet-plan.yaml +++ b/deployments/default.simnet-plan.yaml @@ -5,235 +5,76 @@ network: simnet genesis: wallets: - name: deployer - address: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + address: SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R balance: "100000000000000" sbtc-balance: "1000000000" - name: faucet - address: STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 + address: SPNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2C69MJD9 balance: "100000000000000" sbtc-balance: "1000000000" - name: wallet_1 - address: ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 + address: SP1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2XG1V316 balance: "100000000000000" sbtc-balance: "1000000000" - name: wallet_2 - address: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG + address: SP2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CH94GRJ balance: "100000000000000" sbtc-balance: "1000000000" - name: wallet_3 - address: ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC + address: SP2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1J5QKA2F balance: "100000000000000" sbtc-balance: "1000000000" - name: wallet_4 - address: ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND + address: SP2NEB84ASENDXKYGJPQW86YXQCEFEX2ZPB1S2EP balance: "100000000000000" sbtc-balance: "1000000000" - name: wallet_5 - address: ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB + address: SP2REHHS5J3CERCRBEPMGH7921Q6PYKAADR2V8W5C balance: "100000000000000" sbtc-balance: "1000000000" - name: wallet_6 - address: ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0 + address: SP3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ3JJEPMJ balance: "100000000000000" sbtc-balance: "1000000000" - name: wallet_7 - address: ST3PF13W7Z0RRM42A8VZRVFQ75SV1K26RXEP8YGKJ + address: SP3PF13W7Z0RRM42A8VZRVFQ75SV1K26RXFA1W3C2 balance: "100000000000000" sbtc-balance: "1000000000" - name: wallet_8 - address: ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP + address: SP3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N6R5192Q balance: "100000000000000" sbtc-balance: "1000000000" contracts: + - genesis + - lockup + - bns + - cost-voting - costs - pox + - costs-2 - pox-2 + - costs-3 - pox-3 - pox-4 - - lockup - - costs-2 - - costs-3 - - cost-voting - - bns + - signers + - signers-voting plan: batches: - id: 0 - transactions: - - emulated-contract-publish: - contract-name: citycoin-core-trait - emulated-sender: SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27 - path: "./.cache/requirements/SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27.citycoin-core-trait.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: citycoin-token-trait - emulated-sender: SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27 - path: "./.cache/requirements/SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27.citycoin-token-trait.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: miamicoin-auth - emulated-sender: SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27 - path: "./.cache/requirements/SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27.miamicoin-auth.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: sip-010-trait-ft-standard - emulated-sender: SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE - path: "./.cache/requirements/SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: miamicoin-token - emulated-sender: SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27 - path: "./.cache/requirements/SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27.miamicoin-token.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: extension-trait - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.extension-trait.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: proposal-trait - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.proposal-trait.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: base-dao - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.base-dao.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: ccd001-direct-execute - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd001-direct-execute.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: nft-trait - emulated-sender: SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9 - path: "./.cache/requirements/SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: ccd002-trait - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd002-trait.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: stacking-trait - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.stacking-trait.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: ccd002-treasury-mia-stacking - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd002-treasury-mia-stacking.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: ccd002-treasury-nyc-stacking - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd002-treasury-nyc-stacking.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: ccd003-user-registry - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd003-user-registry.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: ccd004-city-registry - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd004-city-registry.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: ccd005-city-data - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd005-city-data.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: ccd007-trait - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd007-trait.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: citycoin-token-v2-trait - emulated-sender: SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11 - path: "./.cache/requirements/SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11.citycoin-token-v2-trait.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: citycoin-core-v2-trait - emulated-sender: SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11 - path: "./.cache/requirements/SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11.citycoin-core-v2-trait.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: newyorkcitycoin-auth-v2 - emulated-sender: SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11 - path: "./.cache/requirements/SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11.newyorkcitycoin-auth-v2.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: newyorkcitycoin-auth - emulated-sender: SP2H8PY27SEZ03MWRKS5XABZYQN17ETGQS3527SA5 - path: "./.cache/requirements/SP2H8PY27SEZ03MWRKS5XABZYQN17ETGQS3527SA5.newyorkcitycoin-auth.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: newyorkcitycoin-token - emulated-sender: SP2H8PY27SEZ03MWRKS5XABZYQN17ETGQS3527SA5 - path: "./.cache/requirements/SP2H8PY27SEZ03MWRKS5XABZYQN17ETGQS3527SA5.newyorkcitycoin-token.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: newyorkcitycoin-token-v2 - emulated-sender: SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11 - path: "./.cache/requirements/SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11.newyorkcitycoin-token-v2.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: miamicoin-auth-v2 - emulated-sender: SP1H1733V5MZ3SZ9XRW9FKYGEZT0JDGEB8Y634C7R - path: "./.cache/requirements/SP1H1733V5MZ3SZ9XRW9FKYGEZT0JDGEB8Y634C7R.miamicoin-auth-v2.clar" - clarity-version: 1 - epoch: "2.1" - - id: 1 - transactions: - - emulated-contract-publish: - contract-name: miamicoin-core-v1-patch - emulated-sender: SP1H1733V5MZ3SZ9XRW9FKYGEZT0JDGEB8Y634C7R - path: "./.cache/requirements/SP1H1733V5MZ3SZ9XRW9FKYGEZT0JDGEB8Y634C7R.miamicoin-core-v1-patch.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: miamicoin-token-v2 - emulated-sender: SP1H1733V5MZ3SZ9XRW9FKYGEZT0JDGEB8Y634C7R - path: "./.cache/requirements/SP1H1733V5MZ3SZ9XRW9FKYGEZT0JDGEB8Y634C7R.miamicoin-token-v2.clar" - clarity-version: 1 - - emulated-contract-publish: - contract-name: ccd007-citycoin-stacking - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccd007-citycoin-stacking.clar" - clarity-version: 1 - epoch: "2.1" - - id: 2 - transactions: - - emulated-contract-publish: - contract-name: ccip015-trait - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccip015-trait.clar" - clarity-version: 2 - epoch: "2.4" - - id: 3 - transactions: - - emulated-contract-publish: - contract-name: ccip024-miamicoin-signal-vote - emulated-sender: SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH - path: "./.cache/requirements/SP8A9HZ3PKST0S42VM9523Z9NV42SZ026V4K39WH.ccip024-miamicoin-signal-vote.clar" - clarity-version: 2 - epoch: "2.5" - - id: 4 transactions: - emulated-contract-publish: contract-name: annotations_flow_test - emulated-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + emulated-sender: SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R path: tests/contracts/generator-tests/annotations_flow_test.clar clarity-version: 3 - emulated-contract-publish: contract-name: annotations_test - emulated-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + emulated-sender: SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R path: tests/contracts/generator-tests/annotations_test.clar clarity-version: 3 - emulated-contract-publish: contract-name: contract_call_flow_test - emulated-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + emulated-sender: SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R path: tests/contracts/generator-tests/contract-call_flow_test.clar clarity-version: 3 epoch: "3.1" diff --git a/package.json b/package.json index 01405a1..820b423 100644 --- a/package.json +++ b/package.json @@ -32,11 +32,11 @@ }, "homepage": "https://github.com/stacks-network/clarunit#readme", "dependencies": { - "@hirosystems/clarinet-sdk": "3.1.0", + "@hirosystems/clarinet-sdk": "3.7.0", "@stacks/common": "^7.0.2", - "@stacks/transactions": "7.1.0", + "@stacks/transactions": "7.2.0", "chokidar-cli": "^3.0.0", - "typescript": "5.8.3", + "typescript": "5.9.2", "vite": "6.3.5", "vitest": "3.2.3", "vitest-environment-clarinet": "2.3.0" diff --git a/src/parser/clarity-parser-flow-tests.ts b/src/parser/clarity-parser-flow-tests.ts index 63a8bee..6c5898f 100644 --- a/src/parser/clarity-parser-flow-tests.ts +++ b/src/parser/clarity-parser-flow-tests.ts @@ -163,10 +163,10 @@ function extractUnwrapInfo( fn = { args: (callAnnotations["type-hints"] as string) .split(",") - .map((s) => ({ type: s.trim() })), + .map((s) => ({ type: parseTypeHint(s.trim()) })), }; } else { - throw `function ${functionName} not found in contract ${contractName} and not type-hints provided`; + throw `function ${functionName} of ${contractName} not found in Clarinet toml and no type-hints provided`; } } const args = fn.args.map((arg: any, index: number) => @@ -220,3 +220,46 @@ function splitArgs(argString: string): string[] { return splitArgs; } + +/** + * Parse type hint string into ContractInterfaceAtomType + * @param typeHint string like "uint128", "(optional uint128)", etc. + * @returns ContractInterfaceAtomType + */ +function parseTypeHint(typeHint: string): any { + typeHint = typeHint.trim(); + + // Handle parentheses wrapped types like (optional uint128) + if (typeHint.startsWith("(") && typeHint.endsWith(")")) { + const inner = typeHint.slice(1, -1).trim(); + const parts = inner.split(" "); + + if (parts[0] === "optional") { + return { + optional: parseTypeHint(parts.slice(1).join(" ")), + }; + } + + // Add other complex type handling as needed + } + + // Handle simple types + switch (typeHint) { + case "uint": + case "uint128": + return "uint128"; + case "int": + case "int128": + return "int128"; + case "bool": + return "bool"; + case "principal": + return "principal"; + case "trait_reference": + return "trait_reference"; + case "none": + return "none"; + default: + throw new Error(`Unsupported type hint: ${typeHint}`); + } +} diff --git a/src/parser/string-to-cv.ts b/src/parser/string-to-cv.ts index 1023398..f04cb16 100644 --- a/src/parser/string-to-cv.ts +++ b/src/parser/string-to-cv.ts @@ -63,6 +63,8 @@ export function stringToCV( return { type: "uint", value: Cl.uint(arg.slice(1)) }; case "int128": return { type: "int", value: Cl.int(arg) }; + case "bool": + return { type: "bool", value: Cl.bool(arg === "true") }; case "principal": const [address, name] = arg.split("."); return name @@ -73,14 +75,15 @@ export function stringToCV( value: Cl.contractPrincipal(simnet.deployer, name), } : { type: "principal", value: Cl.standardPrincipal(address) }; - case "bool": - return { type: "bool", value: Cl.bool(arg === "true") }; case "trait_reference": - console.log("trait_reference", arg); const [addressTrait, nameTrait] = arg.split("."); return { type: "trait_reference", - value: Cl.contractPrincipal(addressTrait.substring(1), nameTrait), + value: Cl.contractPrincipal( + // handle both fully qualified contract ids and .contract-name + addressTrait.length > 1 ? addressTrait.substring(1) : simnet.deployer, + nameTrait + ), }; } const typeDescriptor = Object.keys(type)[0]; @@ -118,7 +121,7 @@ export function stringToCV( }; } default: - throw new Error(`Unsupported type ${type}`); + throw new Error(`Unsupported type ${arg}, ${typeDescriptor}`); } } diff --git a/tests/clarity-parser-flow.test.ts b/tests/clarity-parser-flow.test.ts index dac478a..5e88b8a 100644 --- a/tests/clarity-parser-flow.test.ts +++ b/tests/clarity-parser-flow.test.ts @@ -31,7 +31,10 @@ describe("verify clarity parser for flow tests", () => { }, }); expect(callInfos["test-simple-flow"][2]).toEqual({ - callAnnotations: { caller: "wallet_1" }, + callAnnotations: { + caller: "wallet_1", + "type-hints": "principal, (optional uint)", + }, callInfo: { args: [ { @@ -39,7 +42,7 @@ describe("verify clarity parser for flow tests", () => { value: { type: "contract", value: - "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.pox4-self-service-multi", + "SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R.pox4-self-service-multi", }, }, { diff --git a/tests/clarunit.test.ts b/tests/clarunit.test.ts index 150b348..3f19e33 100644 --- a/tests/clarunit.test.ts +++ b/tests/clarunit.test.ts @@ -1,3 +1,2 @@ import { clarunit } from "../src/index"; - clarunit(simnet); diff --git a/tests/contracts/generator-tests/annotations_flow_test.clar b/tests/contracts/generator-tests/annotations_flow_test.clar index ffed113..f5cfae6 100644 --- a/tests/contracts/generator-tests/annotations_flow_test.clar +++ b/tests/contracts/generator-tests/annotations_flow_test.clar @@ -12,8 +12,8 @@ (define-public (assert-block-height-3) (begin - (asserts! (is-eq u3 stacks-block-height) - (err (concat "expected block height 3, found " + (asserts! (is-eq u3491158 stacks-block-height) + (err (concat "expected block height 3491158, found " (int-to-ascii stacks-block-height) )) ) @@ -23,8 +23,8 @@ (define-public (assert-block-height-13) (begin - (asserts! (is-eq u13 stacks-block-height) - (err (concat "expected block height 13, found " + (asserts! (is-eq u3491168 stacks-block-height) + (err (concat "expected block height 3491168, found " (int-to-ascii stacks-block-height) )) ) diff --git a/tests/contracts/generator-tests/annotations_test.clar b/tests/contracts/generator-tests/annotations_test.clar index e8a9e64..16d5d4b 100644 --- a/tests/contracts/generator-tests/annotations_test.clar +++ b/tests/contracts/generator-tests/annotations_test.clar @@ -2,8 +2,8 @@ ;; One block is need to advance to epoch 2.5 (define-public (test-block-height-at-launch) (begin - (asserts! (is-eq u3 stacks-block-height) - (err (concat "expected block height 3, found " + (asserts! (is-eq u3491158 stacks-block-height) + (err (concat "expected block height 3491158, found " (int-to-ascii stacks-block-height) )) ) @@ -14,8 +14,8 @@ ;; @mine-blocks-before 10 (define-public (test-mine-blocks-before) (begin - (asserts! (is-eq u13 stacks-block-height) - (err (concat "expected block height 13, found " + (asserts! (is-eq u3491168 stacks-block-height) + (err (concat "expected block height 3491168, found " (int-to-ascii stacks-block-height) )) ) @@ -26,10 +26,10 @@ ;; @caller wallet_1 (define-public (test-caller) (begin - (asserts! (is-eq tx-sender 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5) + (asserts! (is-eq tx-sender 'SP1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2XG1V316) (err tx-sender) ) - (asserts! (is-eq contract-caller 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5) + (asserts! (is-eq contract-caller 'SP1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2XG1V316) (err contract-caller) ) (ok true) diff --git a/tests/contracts/parser-tests/simple-flow.clar b/tests/contracts/parser-tests/simple-flow.clar index 542310a..c470637 100644 --- a/tests/contracts/parser-tests/simple-flow.clar +++ b/tests/contracts/parser-tests/simple-flow.clar @@ -6,6 +6,7 @@ ;; @caller wallet_2 (try! (my-test-function2)) ;; @caller wallet_1 + ;; @type-hints principal, (optional uint) (unwrap! (contract-call? 'ST000000000000000000002AMW42H.pox-4 allow-contract-caller .pox4-self-service-multi none) (err "allow-contract-caller failed")) ;; @caller 'ST000000000000000000002AMW42H (try! (my-test-function))