diff --git a/Clarinet.toml b/Clarinet.toml index ab1f4c0..aaffa13 100644 --- a/Clarinet.toml +++ b/Clarinet.toml @@ -4,23 +4,18 @@ telemetry = true cache_dir = './.cache' requirements = [] -[contracts.stacks-m2m-aibtc] -path = 'src/stacks-m2m-aibtc.clar' +[contracts.aibtcdev-aibtc] +path = 'src/aibtcdev-aibtc.clar' clarity_version = 2 epoch = 2.4 -[contracts.stacks-m2m-trait-v1] -path = 'src/stacks-m2m-trait-v1.clar' +[contracts.aibtcdev-trait-v1] +path = 'src/aibtcdev-trait-v1.clar' clarity_version = 2 epoch = 2.4 -[contracts.stacks-m2m-v1] -path = 'src/stacks-m2m-v1.clar' -clarity_version = 2 -epoch = 2.4 - -[contracts.stacks-m2m-v2] -path = 'src/stacks-m2m-v2.clar' +[contracts.aibtcdev-resources-v1] +path = 'src/aibtcdev-resources-v1.clar' clarity_version = 2 epoch = 2.4 diff --git a/deployments/default.simnet-plan.yaml b/deployments/default.simnet-plan.yaml index 2a0e396..fcbace8 100644 --- a/deployments/default.simnet-plan.yaml +++ b/deployments/default.simnet-plan.yaml @@ -50,23 +50,18 @@ plan: - id: 0 transactions: - emulated-contract-publish: - contract-name: stacks-m2m-aibtc + contract-name: aibtcdev-aibtc emulated-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM - path: src/stacks-m2m-aibtc.clar + path: src/aibtcdev-aibtc.clar clarity-version: 2 - emulated-contract-publish: - contract-name: stacks-m2m-trait-v1 + contract-name: aibtcdev-trait-v1 emulated-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM - path: src/stacks-m2m-trait-v1.clar + path: src/aibtcdev-trait-v1.clar clarity-version: 2 - emulated-contract-publish: - contract-name: stacks-m2m-v1 + contract-name: aibtcdev-resources-v1 emulated-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM - path: src/stacks-m2m-v1.clar - clarity-version: 2 - - emulated-contract-publish: - contract-name: stacks-m2m-v2 - emulated-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM - path: src/stacks-m2m-v2.clar + path: src/aibtcdev-resources-v1.clar clarity-version: 2 epoch: "2.4" diff --git a/src/stacks-m2m-aibtc.clar b/src/aibtcdev-aibtc.clar similarity index 99% rename from src/stacks-m2m-aibtc.clar rename to src/aibtcdev-aibtc.clar index cc35a23..2cbcce8 100644 --- a/src/stacks-m2m-aibtc.clar +++ b/src/aibtcdev-aibtc.clar @@ -1,4 +1,4 @@ -;; title: stacks-m2m-aibtc +;; title: aibtcdev-aibtc ;; version: 0.0.1 ;; summary: Copy of ALEX aBTC contract for use on testnet only. diff --git a/src/stacks-m2m-v2.clar b/src/aibtcdev-resources-v1.clar similarity index 96% rename from src/stacks-m2m-v2.clar rename to src/aibtcdev-resources-v1.clar index 94a8bc8..75614ac 100644 --- a/src/stacks-m2m-v2.clar +++ b/src/aibtcdev-resources-v1.clar @@ -1,11 +1,11 @@ -;; title: stacks-m2m-v2 +;; title: aibtcdev-resources-v1 ;; version: 0.0.2 ;; summary: HTTP 402 payments powered by Stacks ;; traits ;; -(impl-trait .stacks-m2m-trait-v1.stacks-m2m-trait-v1) +(impl-trait .aibtcdev-trait-v1.aibtcdev-trait-v1) ;; constants ;; @@ -332,8 +332,8 @@ ;; MAINNET ;; xBTC SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.token-wbtc ;; aBTC SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.token-abtc - (try! (contract-call? .stacks-m2m-aibtc transfer (get price resourceData) contract-caller (var-get paymentAddress) memo)) - (try! (contract-call? .stacks-m2m-aibtc transfer (get price resourceData) contract-caller (var-get paymentAddress) none)) + (try! (contract-call? .aibtcdev-aibtc transfer (get price resourceData) contract-caller (var-get paymentAddress) memo)) + (try! (contract-call? .aibtcdev-aibtc transfer (get price resourceData) contract-caller (var-get paymentAddress) none)) ) ;; return new count (ok newCount) diff --git a/src/stacks-m2m-trait-v1.clar b/src/aibtcdev-trait-v1.clar similarity index 92% rename from src/stacks-m2m-trait-v1.clar rename to src/aibtcdev-trait-v1.clar index 7c9b3e2..8d3bac6 100644 --- a/src/stacks-m2m-trait-v1.clar +++ b/src/aibtcdev-trait-v1.clar @@ -1,4 +1,4 @@ -(define-trait stacks-m2m-trait-v1 +(define-trait aibtcdev-trait-v1 ( (set-payment-address (principal principal) (response bool uint)) (add-resource ((string-utf8 50) (string-utf8 255) uint) (response uint uint)) diff --git a/src/stacks-m2m-v1.clar b/src/stacks-m2m-v1.clar deleted file mode 100644 index 8341cbd..0000000 --- a/src/stacks-m2m-v1.clar +++ /dev/null @@ -1,409 +0,0 @@ - -;; title: stacks-m2m-v1 -;; version: 0.0.1 -;; summary: HTTP 402 payments powered by Stacks - -;; traits -;; - -;; getting errors for add-resource, pay-invoice, and pay-invoice-by-name -;; maybe next time! -;; (impl-trait .stacks-m2m-trait-v1.stacks-m2m-trait-v1) - -;; constants -;; - -;; initially scoped to service provider deploying a contract -(define-constant DEPLOYER contract-caller) -(define-constant SELF (as-contract tx-sender)) - -;; errors -(define-constant ERR_UNAUTHORIZED (err u1000)) -(define-constant ERR_INVALID_PARAMS (err u1001)) -(define-constant ERR_NAME_ALREADY_USED (err u1002)) -(define-constant ERR_SAVING_RESOURCE_DATA (err u1003)) -(define-constant ERR_DELETING_RESOURCE_DATA (err u1004)) -(define-constant ERR_RESOURCE_NOT_FOUND (err u1005)) -(define-constant ERR_USER_ALREADY_EXISTS (err u1006)) -(define-constant ERR_SAVING_USER_DATA (err u1007)) -(define-constant ERR_USER_NOT_FOUND (err u1008)) -(define-constant ERR_INVOICE_ALREADY_PAID (err u1009)) -(define-constant ERR_SAVING_INVOICE_DATA (err u1010)) -(define-constant ERR_INVOICE_HASH_NOT_FOUND (err u1011)) -(define-constant ERR_SETTING_MEMO_ON_TRANSFER (err u1012)) - -;; data vars -;; - -;; tracking counts for each map -(define-data-var userCount uint u0) -(define-data-var resourceCount uint u0) -(define-data-var invoiceCount uint u0) - -;; payout address, deployer can set -(define-data-var paymentAddress principal DEPLOYER) - -;; data maps -;; - -;; tracks user indexes by address -(define-map UserIndexes - principal ;; user address - uint ;; user index -) - -;; tracks full user data keyed by user index -;; can iterate over full map with userCount data-var -(define-map UserData - uint ;; user index - { - address: principal, - totalSpent: uint, - totalUsed: uint, - } -) - -;; tracks resource indexes by resource name -(define-map ResourceIndexes - (string-utf8 50) ;; resource name - uint ;; resource index -) - -;; tracks resources added by deployer keyed by resource index -;; can iterate over full map with resourceCount data-var -(define-map ResourceData - uint ;; resource index - { - createdAt: uint, - name: (string-utf8 50), - description: (string-utf8 255), - price: uint, - totalSpent: uint, - totalUsed: uint, - } -) - -;; tracks invoice indexes by invoice ID -(define-map InvoiceIndexes - (buff 32) ;; invoice SHA256 hash - uint ;; invoice index -) - -;; tracks last payment from user for a resource -(define-map RecentPayments - { - userIndex: uint, - resourceIndex: uint, - } - uint ;; invoice index -) - -;; tracks invoices paid by users requesting access to a resource -(define-map InvoiceData - uint ;; invoice index - { - amount: uint, - createdAt: uint, - hash: (buff 32), - userIndex: uint, - resourceName: (string-utf8 50), - resourceIndex: uint, - } -) - -;; read only functions -;; - -;; returns total registered users -(define-read-only (get-total-users) - (var-get userCount) -) - -;; returns user index for address if known -(define-read-only (get-user-index (user principal)) - (map-get? UserIndexes user) -) - -;; returns user data by user index if known -(define-read-only (get-user-data (index uint)) - (map-get? UserData index) -) - -;; returns user data by address if known -(define-read-only (get-user-data-by-address (user principal)) - (get-user-data (unwrap! (get-user-index user) none)) -) - -;; returns total registered resources -(define-read-only (get-total-resources) - (var-get resourceCount) -) - -;; returns resource index for name if known -(define-read-only (get-resource-index (name (string-utf8 50))) - (map-get? ResourceIndexes name) -) - -;; returns resource data by resource index if known -(define-read-only (get-resource (index uint)) - (map-get? ResourceData index) -) - -;; returns resource data by resource name if known -(define-read-only (get-resource-by-name (name (string-utf8 50))) - (get-resource (unwrap! (get-resource-index name) none)) -) - -;; returns total registered invoices -(define-read-only (get-total-invoices) - (var-get invoiceCount) -) - -;; returns invoice index for hash if known -(define-read-only (get-invoice-index (hash (buff 32))) - (map-get? InvoiceIndexes hash) -) - -;; returns invoice data by invoice index if known -(define-read-only (get-invoice (index uint)) - (map-get? InvoiceData index) -) - -;; returns invoice data by invoice hash if known -(define-read-only (get-invoice-by-hash (hash (buff 32))) - (get-invoice (unwrap! (get-invoice-index hash) none)) -) - -;; returns invoice index by user index and resource index if known -(define-read-only (get-recent-payment (resourceIndex uint) (userIndex uint)) - (map-get? RecentPayments { - userIndex: userIndex, - resourceIndex: resourceIndex, - }) -) - -;; returns invoice data by user index and resource index if known -(define-read-only (get-recent-payment-data (resourceIndex uint) (userIndex uint)) - (get-invoice (unwrap! (get-recent-payment resourceIndex userIndex) none)) -) - -;; returns invoice data by user address and resource name if known -(define-read-only (get-recent-payment-data-by-address (name (string-utf8 50)) (user principal)) - (get-recent-payment-data (unwrap! (get-resource-index name) none) (unwrap! (get-user-index user) none)) -) - -;; returns payment address -(define-read-only (get-payment-address) - (some (var-get paymentAddress)) -) - -;; returns a unique but deterministic invoice hash based on: -;; - the bitcoin block and stacks block values (time) -;; - the user address requesting the invoice (who) -;; - the resource the user is requesting (what) -;; - the contract name (where) -(define-read-only (get-invoice-hash (user principal) (resourceIndex uint) (blockHeight uint)) - (let - ( - ;; 32 byte bitcoin hash / stacks hash from block height - (btcBlockHash (unwrap! (get-block-info? burnchain-header-hash blockHeight) none)) - (stxBlockHash (unwrap! (get-block-info? id-header-hash blockHeight) none)) - ;; concatenate bitcoin + stacks hash into single buff - (combinedBlockHash (concat btcBlockHash stxBlockHash)) - ;; 20 byte pubkey from address - (userDestruct (unwrap! (principal-destruct? user) none)) - (userPubkey (get hash-bytes userDestruct)) - ;; 32 byte resource hash, combo of resource name + contract name - (resourceData (unwrap! (get-resource resourceIndex) none)) - (resourceName (unwrap! (to-consensus-buff? (get name resourceData)) none)) - (contractName (unwrap! (to-consensus-buff? SELF) none)) - (resourceInfo (concat resourceName contractName)) - (resourceHash (sha256 resourceInfo)) - ;; concatenate user pubkey + resource hash - (combinedUserHash (concat userPubkey resourceHash)) - ;; concatenate both combined hashes for a single buff - (allCombinedHashes (concat combinedBlockHash combinedUserHash)) - ) - ;; return combined hash - (some (sha256 allCombinedHashes)) - ) -) - -;; public functions -;; - -;; sets payment address used for invoices -;; only accessible by deployer or current payment address -(define-public (set-payment-address (oldAddress principal) (newAddress principal)) - (begin - ;; check that old address matches current address - (asserts! (is-eq oldAddress (var-get paymentAddress)) ERR_UNAUTHORIZED) - ;; address cannot be the same - (asserts! (not (is-eq oldAddress newAddress)) ERR_UNAUTHORIZED) - ;; check if caller matches deployer or oldAddress - (asserts! (or - (is-eq contract-caller oldAddress) - (try! (is-deployer)) - ) ERR_UNAUTHORIZED) - ;; set new payment address - (ok (var-set paymentAddress newAddress)) - ) -) - -;; adds active resource that invoices can be generated for -;; only accessible by deployer -(define-public (add-resource (name (string-utf8 50)) (description (string-utf8 255)) (price uint)) - (let - ( - (newCount (+ (get-total-resources) u1)) - ) - ;; check if caller matches deployer - (try! (is-deployer)) - ;; check all values are provided - (asserts! (> (len name) u0) ERR_INVALID_PARAMS) - (asserts! (> (len description) u0) ERR_INVALID_PARAMS) - (asserts! (> price u0) ERR_INVALID_PARAMS) - ;; update ResourceIndexes map, check name is unique - (asserts! (map-insert ResourceIndexes name newCount) ERR_NAME_ALREADY_USED) - ;; update ResourceData map - (asserts! (map-insert ResourceData - newCount - { - createdAt: block-height, - name: name, - description: description, - price: price, - totalSpent: u0, - totalUsed: u0, - } - ) ERR_SAVING_RESOURCE_DATA) - ;; increment resourceCount - (var-set resourceCount newCount) - ;; return new count - (ok newCount) - ) -) - -;; deletes active resource that invoices can be generated against -;; does not delete unique name, rule stays enforced to prevent -;; any bait/switch and other weirdness while we're exploring -(define-public (delete-resource (index uint)) - (begin - ;; check if caller matches deployer - (try! (is-deployer)) - ;; check provided index is within range - (asserts! (and (> index u0) (<= index (var-get resourceCount))) ERR_INVALID_PARAMS) - ;; return and delete resource data from map - (ok (asserts! (map-delete ResourceData index) ERR_DELETING_RESOURCE_DATA)) - ) -) - -;; adapter to allow deleting by name instead of index -(define-public (delete-resource-by-name (name (string-utf8 50))) - (delete-resource (unwrap! (get-resource-index name) ERR_INVALID_PARAMS)) -) - -;; allows a user to pay an invoice for a resource -(define-public (pay-invoice (resourceIndex uint) (memo (optional (buff 34)))) - (let - ( - (newCount (+ (get-total-invoices) u1)) - (lastAnchoredBlock (- block-height u1)) - (resourceData (unwrap! (get-resource resourceIndex) ERR_RESOURCE_NOT_FOUND)) - (userIndex (unwrap! (get-or-create-user contract-caller) ERR_USER_NOT_FOUND)) - (userData (unwrap! (get-user-data userIndex) ERR_USER_NOT_FOUND)) - (invoiceHash (unwrap! (get-invoice-hash contract-caller resourceIndex lastAnchoredBlock) ERR_INVOICE_HASH_NOT_FOUND)) - ) - ;; update InvoiceIndexes map, check invoice hash is unique - (asserts! (map-insert InvoiceIndexes invoiceHash newCount) ERR_INVOICE_ALREADY_PAID) - ;; update InvoiceData map - (asserts! (map-insert InvoiceData - newCount - { - amount: (get price resourceData), - createdAt: block-height, - hash: invoiceHash, - userIndex: userIndex, - resourceName: (get name resourceData), - resourceIndex: resourceIndex, - } - ) ERR_SAVING_INVOICE_DATA) - ;; update RecentPayments map - (map-set RecentPayments - { - userIndex: userIndex, - resourceIndex: resourceIndex, - } - newCount - ) - ;; update UserData map - (map-set UserData - userIndex - (merge userData { - totalSpent: (+ (get totalSpent userData) (get price resourceData)), - totalUsed: (+ (get totalUsed userData) u1) - }) - ) - ;; update ResourceData map - (map-set ResourceData - resourceIndex - (merge resourceData { - totalSpent: (+ (get totalSpent resourceData) (get price resourceData)), - totalUsed: (+ (get totalUsed resourceData) u1) - }) - ) - ;; increment counter - (var-set invoiceCount newCount) - ;; print updated details - (print { - invoiceHash: invoiceHash, - resourceData: (get-resource resourceIndex), - userData: (get-user-data userIndex) - }) - ;; make transfer - (if (is-some memo) - (try! (stx-transfer-memo? (get price resourceData) contract-caller (var-get paymentAddress) (unwrap! memo ERR_SETTING_MEMO_ON_TRANSFER))) - (try! (stx-transfer? (get price resourceData) contract-caller (var-get paymentAddress))) - ) - ;; return new count - (ok newCount) - ) -) - -(define-public (pay-invoice-by-resource-name (name (string-utf8 50)) (memo (optional (buff 34)))) - (pay-invoice (unwrap! (get-resource-index name) ERR_RESOURCE_NOT_FOUND) memo) -) - -;; private functions -;; - -(define-private (is-deployer) - (ok (asserts! (is-eq contract-caller DEPLOYER) ERR_UNAUTHORIZED)) -) - -(define-private (get-or-create-user (address principal)) - (match (map-get? UserIndexes address) - value (ok value) ;; return index if found - (let - ( - ;; increment current index - (newCount (+ (get-total-users) u1)) - ) - ;; update UserIndexes map, check address is unique - (asserts! (map-insert UserIndexes address newCount) ERR_SAVING_USER_DATA) - ;; update UserData map - (asserts! (map-insert UserData - newCount - { - address: address, - totalSpent: u0, - totalUsed: u0, - } - ) ERR_SAVING_USER_DATA) - ;; save new index - (var-set userCount newCount) - ;; return new index - (ok newCount) - ) - ) -) diff --git a/tests/stacks-m2m-aibtc.test.ts b/tests/aibtcdev-aibtc.test.ts similarity index 90% rename from tests/stacks-m2m-aibtc.test.ts rename to tests/aibtcdev-aibtc.test.ts index 96b7d5e..3c7b051 100644 --- a/tests/stacks-m2m-aibtc.test.ts +++ b/tests/aibtcdev-aibtc.test.ts @@ -9,7 +9,7 @@ const FAUCET_DRIP = 10_000; // 0.0001 BTC const FAUCET_DROP = 1_000_000; // 0.01 BTC const FAUCET_FLOOD = 100_000_000; // 1 BTC -describe("stacks-m2m-aibtc", () => { +describe("aibtcdev-aibtc", () => { // faucet drip it(`faucet-drip(): succeeds and mints ${FAUCET_DRIP} aiBTC`, () => { // ARRANGE @@ -17,13 +17,13 @@ describe("stacks-m2m-aibtc", () => { const address1 = accounts.get("wallet_1")!; // ACT const response = simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "faucet-drip", [Cl.principal(address1)], address1 ); const balance = simnet.callReadOnlyFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "get-balance", [Cl.principal(address1)], address1 @@ -39,13 +39,13 @@ describe("stacks-m2m-aibtc", () => { const address1 = accounts.get("wallet_1")!; // ACT const response = simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "faucet-drop", [Cl.principal(address1)], address1 ); const balance = simnet.callReadOnlyFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "get-balance", [Cl.principal(address1)], address1 @@ -61,13 +61,13 @@ describe("stacks-m2m-aibtc", () => { const address1 = accounts.get("wallet_1")!; // ACT const response = simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "faucet-flood", [Cl.principal(address1)], address1 ); const balance = simnet.callReadOnlyFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "get-balance", [Cl.principal(address1)], address1 @@ -86,19 +86,19 @@ describe("stacks-m2m-aibtc", () => { const funding = [ simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "faucet-flood", [Cl.principal(address1)], address1 ), simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "faucet-flood", [Cl.principal(address2)], address2 ), simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "faucet-flood", [Cl.principal(address3)], address3 @@ -108,7 +108,7 @@ describe("stacks-m2m-aibtc", () => { // xfer from 1 to 2 const transfer1 = simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "transfer", [ Cl.uint(FAUCET_DRIP), @@ -120,7 +120,7 @@ describe("stacks-m2m-aibtc", () => { ); // xfer from 2 to 3 const transfer2 = simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "transfer", [ Cl.uint(FAUCET_DROP), @@ -132,7 +132,7 @@ describe("stacks-m2m-aibtc", () => { ); // xfer from 3 to 1 const transfer3 = simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "transfer", [ Cl.uint(FAUCET_FLOOD), @@ -145,19 +145,19 @@ describe("stacks-m2m-aibtc", () => { // get balances const balance1 = simnet.callReadOnlyFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "get-balance", [Cl.principal(address1)], address1 ); const balance2 = simnet.callReadOnlyFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "get-balance", [Cl.principal(address2)], address1 ); const balance3 = simnet.callReadOnlyFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "get-balance", [Cl.principal(address3)], address1 diff --git a/tests/stacks-m2m-v2.test.ts b/tests/aibtcdev-resources-v1.test.ts similarity index 91% rename from tests/stacks-m2m-v2.test.ts rename to tests/aibtcdev-resources-v1.test.ts index 6e80584..1108d5f 100644 --- a/tests/stacks-m2m-v2.test.ts +++ b/tests/aibtcdev-resources-v1.test.ts @@ -35,7 +35,7 @@ describe("Adding a resource", () => { const address1 = accounts.get("wallet_1")!; // ACT const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", testResource, address1 @@ -50,7 +50,7 @@ describe("Adding a resource", () => { const deployer = accounts.get("deployer")!; // ACT const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", createResource("", "description", defaultPrice), deployer @@ -65,7 +65,7 @@ describe("Adding a resource", () => { const deployer = accounts.get("deployer")!; // ACT const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", createResource("name", "", defaultPrice), deployer @@ -80,7 +80,7 @@ describe("Adding a resource", () => { const deployer = accounts.get("deployer")!; // ACT const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", createResource("name", "description", 0), deployer @@ -95,13 +95,13 @@ describe("Adding a resource", () => { const expectedCount = 1; // ACT const firstResponse = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", testResource, deployer ); const secondResponse = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", testResource, deployer @@ -120,19 +120,19 @@ describe("Adding a resource", () => { const expectedCount = 1; // ACT const oldCount = simnet.callReadOnlyFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "get-total-resources", [], deployer ); const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", testResource, deployer ); const newCount = simnet.callReadOnlyFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "get-total-resources", [], deployer @@ -153,7 +153,7 @@ describe("Toggling a Resource Status", () => { // ACT // create resource simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", testResource, deployer @@ -162,7 +162,7 @@ describe("Toggling a Resource Status", () => { simnet.mineEmptyBlocks(5000); // toggle resource const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "toggle-resource", [Cl.uint(1)], address1 @@ -177,7 +177,7 @@ describe("Toggling a Resource Status", () => { const deployer = accounts.get("deployer")!; // ACT const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "toggle-resource", [Cl.uint(10)], deployer @@ -193,7 +193,7 @@ describe("Toggling a Resource Status", () => { // ACT // create resource simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", testResource, deployer @@ -202,14 +202,14 @@ describe("Toggling a Resource Status", () => { simnet.mineEmptyBlocks(5000); // toggle resource const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "toggle-resource", [Cl.uint(1)], deployer ); // get resource const resourceResponse = simnet.callReadOnlyFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "get-resource", [Cl.uint(1)], deployer @@ -218,14 +218,14 @@ describe("Toggling a Resource Status", () => { simnet.mineEmptyBlocks(5000); // toggle resource const response2 = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "toggle-resource", [Cl.uint(1)], deployer ); // get resource const resourceResponse2 = simnet.callReadOnlyFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "get-resource", [Cl.uint(1)], deployer @@ -266,7 +266,7 @@ describe("Toggling a Resource Status", () => { // ACT // create resource simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", testResource, deployer @@ -275,7 +275,7 @@ describe("Toggling a Resource Status", () => { simnet.mineEmptyBlocks(5000); // toggle resource const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "toggle-resource-by-name", [Cl.stringUtf8("Bitcoin Face")], address1 @@ -290,7 +290,7 @@ describe("Toggling a Resource Status", () => { const deployer = accounts.get("deployer")!; // ACT const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "toggle-resource-by-name", [Cl.stringUtf8("Nothingburger")], deployer @@ -306,7 +306,7 @@ describe("Toggling a Resource Status", () => { // ACT // create resource simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", testResource, deployer @@ -315,14 +315,14 @@ describe("Toggling a Resource Status", () => { simnet.mineEmptyBlocks(5000); // toggle resource const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "toggle-resource-by-name", [Cl.stringUtf8("Bitcoin Face")], deployer ); // get resource const resourceResponse = simnet.callReadOnlyFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "get-resource", [Cl.uint(1)], deployer @@ -331,14 +331,14 @@ describe("Toggling a Resource Status", () => { simnet.mineEmptyBlocks(5000); // toggle resource const response2 = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "toggle-resource-by-name", [Cl.stringUtf8("Bitcoin Face")], deployer ); // get resource const resourceResponse2 = simnet.callReadOnlyFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "get-resource", [Cl.uint(1)], deployer @@ -380,7 +380,7 @@ describe("Setting a Payment Address", () => { // ACT // get current payment address const currentPaymentAddressResponse = simnet.callReadOnlyFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "get-payment-address", [], address1 @@ -391,7 +391,7 @@ describe("Setting a Payment Address", () => { ); // set payment address const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "set-payment-address", [ Cl.standardPrincipal(currentPaymentAddress.value), @@ -411,7 +411,7 @@ describe("Setting a Payment Address", () => { // ACT // set payment address const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "set-payment-address", [Cl.standardPrincipal(address1), Cl.standardPrincipal(address1)], deployer @@ -429,7 +429,7 @@ describe("Setting a Payment Address", () => { // ACT // get current payment address const currentPaymentAddressResponse = simnet.callReadOnlyFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "get-payment-address", [], address1 @@ -440,7 +440,7 @@ describe("Setting a Payment Address", () => { ); // set payment address const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "set-payment-address", [ Cl.standardPrincipal(currentPaymentAddress.value), @@ -462,7 +462,7 @@ describe("Setting a Payment Address", () => { // ACT // get current payment address const currentPaymentAddressResponse = simnet.callReadOnlyFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "get-payment-address", [], address1 @@ -473,7 +473,7 @@ describe("Setting a Payment Address", () => { ); // set payment address const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "set-payment-address", [ Cl.standardPrincipal(currentPaymentAddress.value), @@ -485,7 +485,7 @@ describe("Setting a Payment Address", () => { simnet.mineEmptyBlocks(5000); // get current payment address again const updatedPaymentAddressResponse = simnet.callReadOnlyFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "get-payment-address", [], address1 @@ -496,7 +496,7 @@ describe("Setting a Payment Address", () => { ); // set payment address again const secondResponse = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "set-payment-address", [Cl.standardPrincipal(address1), Cl.standardPrincipal(address2)], address1 @@ -515,7 +515,7 @@ describe("Paying an Invoice", () => { const address1 = accounts.get("wallet_1")!; // ACT const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "pay-invoice", [ Cl.uint(0), // resource index @@ -535,21 +535,21 @@ describe("Paying an Invoice", () => { // ACT // add a resource simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", testResource, deployer ); // toggle resource simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "toggle-resource", [Cl.uint(1)], deployer ); // pay invoice for resource const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "pay-invoice", [ Cl.uint(1), // resource index @@ -575,21 +575,21 @@ describe("Paying an Invoice", () => { // ACT // mint aiBTC to pay for resources simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "faucet-flood", [Cl.principal(address1)], address1 ); // add a resource simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", testResource, deployer ); // pay invoice for resource const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "pay-invoice", [ Cl.uint(1), // resource index @@ -611,21 +611,21 @@ describe("Paying an Invoice", () => { // ACT // mint aiBTC to pay for resources simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "faucet-flood", [Cl.principal(address1)], address1 ); // add a resource simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", testResource, deployer ); // pay invoice for resource const response = simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "pay-invoice", [ Cl.uint(1), // resource index @@ -649,28 +649,28 @@ describe("Paying an Invoice", () => { // ACT // mint aiBTC to pay for resources simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "faucet-flood", [Cl.principal(address1)], address1 ); // mint aiBTC to pay for resources simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "faucet-flood", [Cl.principal(address2)], address2 ); // mint aiBTC to pay for resources simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "faucet-flood", [Cl.principal(address3)], address3 ); // add a resource simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", testResource, deployer @@ -678,7 +678,7 @@ describe("Paying an Invoice", () => { // pay invoice once for 3 users const blockResponses = [ simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "pay-invoice", [ Cl.uint(1), // resource index @@ -687,7 +687,7 @@ describe("Paying an Invoice", () => { address1 ), simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "pay-invoice", [ Cl.uint(1), // resource index @@ -696,7 +696,7 @@ describe("Paying an Invoice", () => { address2 ), simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "pay-invoice", [ Cl.uint(1), // resource index @@ -710,7 +710,7 @@ describe("Paying an Invoice", () => { // pay invoice again for 3 users blockResponses.push( simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "pay-invoice", [ Cl.uint(1), // resource index @@ -719,7 +719,7 @@ describe("Paying an Invoice", () => { address1 ), simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "pay-invoice", [ Cl.uint(1), // resource index @@ -728,7 +728,7 @@ describe("Paying an Invoice", () => { address2 ), simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "pay-invoice", [ Cl.uint(1), // resource index @@ -753,27 +753,27 @@ describe("Paying an Invoice", () => { // ACT // mint aiBTC to pay for resources simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "faucet-flood", [Cl.principal(address1)], address1 ); simnet.callPublicFn( - "stacks-m2m-aibtc", + "aibtcdev-aibtc", "faucet-flood", [Cl.principal(address2)], address2 ); // add a resource simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "add-resource", testResource, deployer ); // pay invoice for resource simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "pay-invoice", [ Cl.uint(1), // resource index @@ -785,7 +785,7 @@ describe("Paying an Invoice", () => { simnet.mineEmptyBlocks(5000); // pay invoice again for resource simnet.callPublicFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "pay-invoice", [ Cl.uint(1), // resource index @@ -797,20 +797,20 @@ describe("Paying an Invoice", () => { simnet.mineEmptyBlocks(5000); // get resource const resourceResponse = simnet.callReadOnlyFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "get-resource", [Cl.uint(1)], deployer ); // get user const userResponseOne = simnet.callReadOnlyFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "get-user-data-by-address", [Cl.standardPrincipal(address1)], deployer ); const userResponseTwo = simnet.callReadOnlyFn( - "stacks-m2m-v2", + "aibtcdev-resources-v1", "get-user-data-by-address", [Cl.standardPrincipal(address2)], deployer diff --git a/tests/stacks-m2m-v1.test.ts b/tests/stacks-m2m-v1.test.ts deleted file mode 100644 index d64d054..0000000 --- a/tests/stacks-m2m-v1.test.ts +++ /dev/null @@ -1,882 +0,0 @@ -import { Cl, cvToValue } from "@stacks/transactions"; -import { describe, expect, it } from "vitest"; - -enum ErrCode { - ERR_UNAUTHORIZED = 1000, - ERR_INVALID_PARAMS, - ERR_NAME_ALREADY_USED, - ERR_SAVING_RESOURCE_DATA, - ERR_DELETING_RESOURCE_DATA, - ERR_RESOURCE_NOT_FOUND, - ERR_USER_ALREADY_EXISTS, - ERR_SAVING_USER_DATA, - ERR_USER_NOT_FOUND, - ERR_INVOICE_ALREADY_PAID, - ERR_SAVING_INVOICE_DATA, - ERR_INVOICE_HASH_NOT_FOUND, - ERR_SETTING_MEMO_ON_TRANSFER, -} - -const createResource = (name: string, desc: string, price: number) => { - return [Cl.stringUtf8(name), Cl.stringUtf8(desc), Cl.uint(price)]; -}; - -const defaultPrice = 1_000_000; // 1 STX - -const testResource = [ - Cl.stringUtf8("Bitcoin Face"), - Cl.stringUtf8("Generate a unique Bitcoin face."), - Cl.uint(defaultPrice), -]; - -// based on hex value printed to console in first run -// curious to see if this is the same every time -// before we have a local function to compute the same data -// (some 0x053637c68fd20a0bdeef9712f0eb6c2b5c041a7f0ee6a6aed601267c25a39cb6) -// const expectedBlock0Resource0 = Buffer.from("053637c68fd20a0bdeef9712f0eb6c2b5c041a7f0ee6a6aed601267c25a39cb6", "hex") -// incremented resource count (and others) to start at 1 -// (some 0x38bc32acb79a15b7a0b04f4268eba0c5be61d17e0629aac508da25d8884b017e) -// changed hashing algorithm, now composed of: resource name, contract name, stacks block hash, bitcoin block hash, user pubkey -// (some 0x520b34a624e73b0533db4475f260febb048e7eb150f8773779b9fd9dab85d652) -// (some 0xffc41d187c6b7bdd89fec3b4cdf967f7ab81d1efb3358cee6df8f08c9d1c76e9) -const expectedBlock0Resource1 = Buffer.from( - "ffc41d187c6b7bdd89fec3b4cdf967f7ab81d1efb3358cee6df8f08c9d1c76e9", - "hex" -); - -describe("Adding a resource", () => { - it("add-resource() fails if not called by deployer", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const address1 = accounts.get("wallet_1")!; - // ACT - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - address1 - ); - // ASSERT - expect(response.result).toBeErr(Cl.uint(ErrCode.ERR_UNAUTHORIZED)); - }); - - it("add-resource() fails if name is blank", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - // ACT - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - createResource("", "description", defaultPrice), - deployer - ); - // ASSERT - expect(response.result).toBeErr(Cl.uint(ErrCode.ERR_INVALID_PARAMS)); - }); - - it("add-resource() fails if description is blank", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - // ACT - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - createResource("name", "", defaultPrice), - deployer - ); - // ASSERT - expect(response.result).toBeErr(Cl.uint(ErrCode.ERR_INVALID_PARAMS)); - }); - - it("add-resource() fails if price is 0", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - // ACT - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - createResource("name", "description", 0), - deployer - ); - // ASSERT - expect(response.result).toBeErr(Cl.uint(ErrCode.ERR_INVALID_PARAMS)); - }); - it("add-resource() fails if name already used", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - const expectedCount = 1; - // ACT - const firstResponse = simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - const secondResponse = simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - // ASSERT - expect(firstResponse.result).toBeOk(Cl.uint(expectedCount)); - expect(secondResponse.result).toBeErr( - Cl.uint(ErrCode.ERR_NAME_ALREADY_USED) - ); - }); - - it("add-resource() succeeds and increments resource count", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - const expectedCount = 1; - // ACT - const oldCount = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-total-resources", - [], - deployer - ); - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - const newCount = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-total-resources", - [], - deployer - ); - // ASSERT - expect(oldCount.result).toBeUint(expectedCount - 1); - expect(response.result).toBeOk(Cl.uint(expectedCount)); - expect(newCount.result).toBeUint(expectedCount); - }); -}); - -describe("Deleting a Resource", () => { - it("delete-resource() fails if not called by deployer", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - const address1 = accounts.get("wallet_1")!; - // ACT - // create resource - simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - // progress the chain - simnet.mineEmptyBlocks(5000); - // delete resource - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "delete-resource", - [Cl.uint(1)], - address1 - ); - // ASSERT - expect(response.result).toBeErr(Cl.uint(ErrCode.ERR_UNAUTHORIZED)); - }); - - it("delete-resource() fails if provided index is greater than current resource count", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - // ACT - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "delete-resource", - [Cl.uint(1)], - deployer - ); - // ASSERT - expect(response.result).toBeErr(Cl.uint(ErrCode.ERR_INVALID_PARAMS)); - }); - - it("delete-resource() fails if executed twice on the same resource", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - // ACT - // create resource - simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - // progress the chain - simnet.mineEmptyBlocks(5000); - // delete resource - simnet.callPublicFn( - "stacks-m2m-v1", - "delete-resource", - [Cl.uint(1)], - deployer - ); - // progress the chain - simnet.mineEmptyBlocks(5000); - // delete resource again - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "delete-resource", - [Cl.uint(1)], - deployer - ); - // ASSERT - expect(response.result).toBeErr( - Cl.uint(ErrCode.ERR_DELETING_RESOURCE_DATA) - ); - }); - - it("delete-resource-by-name() fails if not called by deployer", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - const address1 = accounts.get("wallet_1")!; - // ACT - // create resource - simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - // progress the chain - simnet.mineEmptyBlocks(5000); - // delete resource - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "delete-resource-by-name", - [Cl.stringUtf8("Bitcoin Face")], - address1 - ); - // ASSERT - expect(response.result).toBeErr(Cl.uint(ErrCode.ERR_UNAUTHORIZED)); - }); - - it("delete-resource-by-name() fails if provided name is not found", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - // ACT - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "delete-resource-by-name", - [Cl.stringUtf8("Nothingburger")], - deployer - ); - // ASSERT - expect(response.result).toBeErr(Cl.uint(ErrCode.ERR_INVALID_PARAMS)); - }); - - it("pay-invoice() fails for a deleted resource", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const address1 = accounts.get("wallet_1")!; - const deployer = accounts.get("deployer")!; - // ACT - // create resource - simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - // progress the chain - simnet.mineEmptyBlocks(5000); - // delete resource - simnet.callPublicFn( - "stacks-m2m-v1", - "delete-resource", - [Cl.uint(1)], - deployer - ); - // progress the chain - simnet.mineEmptyBlocks(5000); - // pay invoice - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "pay-invoice", - [ - Cl.uint(1), // resource index - Cl.none(), // memo - ], - address1 - ); - // ASSERT - expect(response.result).toBeErr(Cl.uint(ErrCode.ERR_RESOURCE_NOT_FOUND)); - }); -}); - -describe("Setting a Payment Address", () => { - it("set-payment-address() fails if not called by deployer", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const address1 = accounts.get("wallet_1")!; - // ACT - // get current payment address - const currentPaymentAddressResponse = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-payment-address", - [], - address1 - ); - // parse into an object we can read - const currentPaymentAddress = cvToValue( - currentPaymentAddressResponse.result - ); - // set payment address - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "set-payment-address", - [ - Cl.standardPrincipal(currentPaymentAddress.value), - Cl.standardPrincipal(address1), - ], - address1 - ); - // ASSERT - expect(response.result).toBeErr(Cl.uint(ErrCode.ERR_UNAUTHORIZED)); - }); - - it("set-payment-address() fails if old address param is incorrect", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const address1 = accounts.get("wallet_1")!; - const deployer = accounts.get("deployer")!; - // ACT - // set payment address - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "set-payment-address", - [Cl.standardPrincipal(address1), Cl.standardPrincipal(address1)], - deployer - ); - // ASSERT - expect(response.result).toBeErr(Cl.uint(ErrCode.ERR_UNAUTHORIZED)); - }); - - it("set-payment-address() succeeds if called by the deployer", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - const address1 = accounts.get("wallet_1")!; - - // ACT - // get current payment address - const currentPaymentAddressResponse = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-payment-address", - [], - address1 - ); - // parse into an object we can read - const currentPaymentAddress = cvToValue( - currentPaymentAddressResponse.result - ); - // set payment address - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "set-payment-address", - [ - Cl.standardPrincipal(currentPaymentAddress.value), - Cl.standardPrincipal(address1), - ], - deployer - ); - // ASSERT - expect(response.result).toBeOk(Cl.bool(true)); - }); - - it("set-payment-address() succeeds if called by current payment address", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - const address1 = accounts.get("wallet_1")!; - const address2 = accounts.get("wallet_2")!; - - // ACT - // get current payment address - const currentPaymentAddressResponse = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-payment-address", - [], - address1 - ); - // parse into an object we can read - const currentPaymentAddress = cvToValue( - currentPaymentAddressResponse.result - ); - // set payment address - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "set-payment-address", - [ - Cl.standardPrincipal(currentPaymentAddress.value), - Cl.standardPrincipal(address1), - ], - deployer - ); - // progress the chain - simnet.mineEmptyBlocks(5000); - // get current payment address again - const updatedPaymentAddressResponse = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-payment-address", - [], - address1 - ); - // parse into an object we can read - const updatedPaymentAddress = cvToValue( - updatedPaymentAddressResponse.result - ); - // set payment address again - const secondResponse = simnet.callPublicFn( - "stacks-m2m-v1", - "set-payment-address", - [Cl.standardPrincipal(address1), Cl.standardPrincipal(address2)], - address1 - ); - // ASSERT - expect(updatedPaymentAddress.value).toEqual(address1); - expect(response.result).toBeOk(Cl.bool(true)); - expect(secondResponse.result).toBeOk(Cl.bool(true)); - }); -}); - -describe("Generating an invoice hash", () => { - it("get-invoice-hash() returns none if resource is not found", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - // ACT - const response = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-invoice-hash", - [ - Cl.standardPrincipal(deployer), // user - Cl.uint(0), // resource index - Cl.uint(0), // block height - ], - deployer - ); - // ASSERT - expect(response.result).toBeNone(); - }); - - it("get-invoice-hash() succeeds and returns the correct value", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - // ACT - simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - const response = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-invoice-hash", - [ - Cl.standardPrincipal(deployer), // user - Cl.uint(1), // resource index - Cl.uint(0), // block height - ], - deployer - ); - // ASSERT - expect(response.result).toBeSome(Cl.buffer(expectedBlock0Resource1)); - }); - - it("get-invoice-hash() succeeds and returns the correct value after the chain progresses", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - // ACT - simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - simnet.mineEmptyBlocks(5000); - const response = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-invoice-hash", - [ - Cl.standardPrincipal(deployer), // user - Cl.uint(1), // resource index - Cl.uint(0), // block height - ], - deployer - ); - // ASSERT - expect(response.result).toBeSome(Cl.buffer(expectedBlock0Resource1)); - }); - - it("get-invoice-hash() succeeds and generates unique values for different users at different block heights", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - const address1 = accounts.get("wallet_1")!; - const address2 = accounts.get("wallet_2")!; - const wallets = [deployer, address1, address2]; - - // ACT - // add a resource - simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - // progress the chain - simnet.mineEmptyBlocks(5000); - // create an array of invoice hashes for 500 blocks - const invoiceHashes = []; - for (const wallet of wallets) { - for (let i = 0; i < 500; i++) { - const response = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-invoice-hash", - [ - Cl.standardPrincipal(wallet), // user - Cl.uint(1), // resource index - Cl.uint(i), // block height - ], - wallet - ); - invoiceHashes.push(response.result); - } - } - - // ASSERT - // check that each invoice hash is unique - const uniqueInvoiceHashes = new Set(invoiceHashes); - expect(uniqueInvoiceHashes.size).toEqual(invoiceHashes.length); - }); - - it("get-invoice-hash() succeeds and generates consistent values for different users at different block heights", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - const address1 = accounts.get("wallet_1")!; - const address2 = accounts.get("wallet_2")!; - const wallets = [deployer, address1, address2]; - - // ACT - // add a resource - simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - // progress the chain - simnet.mineEmptyBlocks(5000); - // create an array of invoice hashes for 500 blocks - const firstInvoiceHashes = []; - for (const wallet of wallets) { - for (let i = 0; i < 500; i++) { - const response = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-invoice-hash", - [ - Cl.standardPrincipal(wallet), // user - Cl.uint(1), // resource index - Cl.uint(i), // block height - ], - wallet - ); - firstInvoiceHashes.push(response.result); - } - } - // progress the chain - simnet.mineEmptyBlocks(5000); - // create an array of invoice hashes for 500 blocks - const secondInvoiceHashes = []; - for (const wallet of wallets) { - for (let i = 0; i < 500; i++) { - const response = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-invoice-hash", - [ - Cl.standardPrincipal(wallet), // user - Cl.uint(1), // resource index - Cl.uint(i), // block height - ], - wallet - ); - secondInvoiceHashes.push(response.result); - } - } - - // ASSERT - // check that the arrays are equal - expect(firstInvoiceHashes).toEqual(secondInvoiceHashes); - }); -}); - -describe("Paying an invoice", () => { - it("pay-invoice() fails if resource is not found", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const address1 = accounts.get("wallet_1")!; - // ACT - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "pay-invoice", - [ - Cl.uint(0), // resource index - Cl.none(), // memo - ], - address1 - ); - // ASSERT - expect(response.result).toBeErr(Cl.uint(ErrCode.ERR_RESOURCE_NOT_FOUND)); - }); - // not expecting ERR_USER_NOT_FOUND, not sure if we can force? - // it("pay-invoice() fails if user cannot be created or found", () => {}) - // not expecting ERR_INVOICE_HASH_NOT_FOUND, same as above - // it("pay-invoice() fails if invoice hash cannot be found", () => {}) - // not expecting ERR_SAVING_INVOICE in two spots, same as above - // it("pay-invoice() fails if invoice cannot be saved", () => {}) - it("pay-invoice() succeeds and returns invoice count without memo", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - const address1 = accounts.get("wallet_1")!; - const expectedCount = 1; - // ACT - // add a resource - simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - // pay invoice for resource - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "pay-invoice", - [ - Cl.uint(1), // resource index - Cl.none(), // memo - ], - address1 - ); - // ASSERT - expect(response.result).toBeOk(Cl.uint(expectedCount)); - }); - - it("pay-invoice() succeeds and returns invoice count with memo", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - const address1 = accounts.get("wallet_1")!; - const expectedCount = 1; - const memo = Buffer.from("This is a memo test!"); - // ACT - // add a resource - simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - // pay invoice for resource - const response = simnet.callPublicFn( - "stacks-m2m-v1", - "pay-invoice", - [ - Cl.uint(1), // resource index - Cl.some(Cl.buffer(memo)), // memo - ], - address1 - ); - // ASSERT - expect(response.result).toBeOk(Cl.uint(expectedCount)); - }); - - it("pay-invoice() succeeds and returns invoice count with memo over several blocks", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const deployer = accounts.get("deployer")!; - const address1 = accounts.get("wallet_1")!; - const address2 = accounts.get("wallet_2")!; - const address3 = accounts.get("wallet_3")!; - const expectedCount = 1; - const memo = Cl.none(); - // ACT - // add a resource - simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - // pay invoice once for 3 users - const blockResponses = [ - simnet.callPublicFn( - "stacks-m2m-v1", - "pay-invoice", - [ - Cl.uint(1), // resource index - memo, // memo - ], - address1 - ), - simnet.callPublicFn( - "stacks-m2m-v1", - "pay-invoice", - [ - Cl.uint(1), // resource index - memo, // memo - ], - address2 - ), - simnet.callPublicFn( - "stacks-m2m-v1", - "pay-invoice", - [ - Cl.uint(1), // resource index - memo, // memo - ], - address3 - ), - ]; - // progress by 5000 blocks - simnet.mineEmptyBlocks(5000); - // pay invoice again for 3 users - blockResponses.push( - simnet.callPublicFn( - "stacks-m2m-v1", - "pay-invoice", - [ - Cl.uint(1), // resource index - memo, // memo - ], - address1 - ), - simnet.callPublicFn( - "stacks-m2m-v1", - "pay-invoice", - [ - Cl.uint(1), // resource index - memo, // memo - ], - address2 - ), - simnet.callPublicFn( - "stacks-m2m-v1", - "pay-invoice", - [ - Cl.uint(1), // resource index - memo, // memo - ], - address3 - ) - ); - // ASSERT - for (let i = 0; i < blockResponses.length; i++) { - expect(blockResponses[i].result).toBeOk(Cl.uint(expectedCount + i)); - } - }); - - it("pay-invoice() succeeds and updates resource and user data", () => { - // ARRANGE - const accounts = simnet.getAccounts(); - const address1 = accounts.get("wallet_1")!; - const address2 = accounts.get("wallet_2")!; - const deployer = accounts.get("deployer")!; - const memo = Cl.none(); - // ACT - // add a resource - simnet.callPublicFn( - "stacks-m2m-v1", - "add-resource", - testResource, - deployer - ); - // pay invoice for resource - simnet.callPublicFn( - "stacks-m2m-v1", - "pay-invoice", - [ - Cl.uint(1), // resource index - memo, // memo - ], - address1 - ); - // progress the chain - simnet.mineEmptyBlocks(5000); - // pay invoice again for resource - simnet.callPublicFn( - "stacks-m2m-v1", - "pay-invoice", - [ - Cl.uint(1), // resource index - memo, // memo - ], - address2 - ); - // progress the chain - simnet.mineEmptyBlocks(5000); - // get resource - const resourceResponse = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-resource", - [Cl.uint(1)], - deployer - ); - // get user - const userResponseOne = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-user-data-by-address", - [Cl.standardPrincipal(address1)], - deployer - ); - const userResponseTwo = simnet.callReadOnlyFn( - "stacks-m2m-v1", - "get-user-data-by-address", - [Cl.standardPrincipal(address2)], - deployer - ); - // ASSERT - expect(resourceResponse.result).toBeSome( - Cl.tuple({ - createdAt: Cl.uint(2), - description: Cl.stringUtf8("Generate a unique Bitcoin face."), - name: Cl.stringUtf8("Bitcoin Face"), - price: Cl.uint(defaultPrice), - totalSpent: Cl.uint(defaultPrice * 2), - totalUsed: Cl.uint(2), - }) - ); - expect(userResponseOne.result).toBeSome( - Cl.tuple({ - address: Cl.standardPrincipal(address1), - totalSpent: Cl.uint(defaultPrice), - totalUsed: Cl.uint(1), - }) - ); - expect(userResponseTwo.result).toBeSome( - Cl.tuple({ - address: Cl.standardPrincipal(address2), - totalSpent: Cl.uint(defaultPrice), - totalUsed: Cl.uint(1), - }) - ); - }); -});