diff --git a/README.md b/README.md index da7c171..83f9c90 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,40 @@ The BatchProcessor automatically: This is particularly useful when you need to make multiple blockchain reads and want to optimize network calls. +### 3. Clarity API Utilities + +The SDK provides convenient utilities for reading data from Clarity contracts: + +```typescript +import { callReadonly, readVariable, readMap } from 'stxer'; +import { SIP010TraitABI } from 'clarity-abi/abis'; +import { unwrapResponse } from 'ts-clarity'; + +// Read from a contract function +const supply = await callReadonly({ + abi: SIP010TraitABI.functions, + functionName: 'get-total-supply', + contract: 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-alex', +}).then(unwrapResponse); + +// Read a contract variable +const paused = await readVariable({ + abi: [{ name: 'paused', type: 'bool', access: 'variable' }], + variableName: 'paused', + contract: 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-vault-v2-01', +}); + +// Read from a contract map +const approved = await readMap({ + abi: [{ key: 'principal', name: 'approved-tokens', value: 'bool' }], + mapName: 'approved-tokens', + key: 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-alex', + contract: 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-vault-v2-01', +}); +``` + +These utilities provide type-safe ways to interact with Clarity contracts, with built-in ABI support and response unwrapping. + ## Configuration You can customize the API endpoints: diff --git a/package.json b/package.json index 18c91c4..ff138af 100644 --- a/package.json +++ b/package.json @@ -55,18 +55,20 @@ "devDependencies": { "@size-limit/preset-small-lib": "^11.1.5", "@tsconfig/recommended": "^1.0.7", + "@types/node": "^22.13.5", "dts-cli": "^2.0.5", "husky": "^9.1.6", "size-limit": "^11.1.5", "tslib": "^2.7.0", - "typescript": "^5.6.2", - "tsx": "^4.19.2" + "tsx": "^4.19.2", + "typescript": "^5.6.2" }, "dependencies": { "@stacks/network": "^7.0.0", "@stacks/stacks-blockchain-api-types": "^7.14.1", "@stacks/transactions": "^7.0.0", "c32check": "^2.0.0", - "ts-clarity": "^0.0.26" + "clarity-abi": "^0.1.0", + "ts-clarity": "^0.1.0-pre.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 850b020..3d458b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,9 +20,12 @@ importers: c32check: specifier: ^2.0.0 version: 2.0.0 + clarity-abi: + specifier: ^0.1.0 + version: 0.1.0(typescript@5.6.2) ts-clarity: - specifier: ^0.0.26 - version: 0.0.26(typescript@5.6.2) + specifier: ^0.1.0-pre.2 + version: 0.1.0-pre.2(typescript@5.6.2) devDependencies: '@size-limit/preset-small-lib': specifier: ^11.1.5 @@ -30,9 +33,12 @@ importers: '@tsconfig/recommended': specifier: ^1.0.7 version: 1.0.7 + '@types/node': + specifier: ^22.13.5 + version: 22.13.5 dts-cli: specifier: ^2.0.5 - version: 2.0.5(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@jest/transform@29.7.0)(@jest/types@29.6.3)(@types/babel__core@7.20.5)(@types/node@22.6.1) + version: 2.0.5(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@jest/transform@29.7.0)(@jest/types@29.6.3)(@types/babel__core@7.20.5)(@types/node@22.13.5) husky: specifier: ^9.1.6 version: 9.1.6 @@ -1065,27 +1071,27 @@ packages: peerDependencies: size-limit: 11.1.5 - '@stacks/common@6.16.0': - resolution: {integrity: sha512-PnzvhrdGRMVZvxTulitlYafSK4l02gPCBBoI9QEoTqgSnv62oaOXhYAUUkTMFKxdHW1seVEwZsrahuXiZPIAwg==} - '@stacks/common@7.0.0': resolution: {integrity: sha512-/BKBK9S9GEuGjbnc2fBAwsG21f8cfNekG/9mXLSMwBqnh4qaQY2hxK+6wRI2YXJgpkXrpZilpZy2sdPGlVUdQA==} - '@stacks/network@6.16.0': - resolution: {integrity: sha512-uqz9Nb6uf+SeyCKENJN+idt51HAfEeggQKrOMfGjpAeFgZV2CR66soB/ci9+OVQR/SURvasncAz2ScI1blfS8A==} + '@stacks/common@7.0.2': + resolution: {integrity: sha512-+RSecHdkxOtswmE4tDDoZlYEuULpnTQVeDIG5eZ32opK8cFxf4EugAcK9CsIsHx/Se1yTEaQ21WGATmJGK84lQ==} '@stacks/network@7.0.0': resolution: {integrity: sha512-4diddT0ii85BQ4PW6ww3l4cS7Oo0a5VIsJ7umBcCPAArIc4Sm/MIOEXIg9joKK8fVHLnWyh1p4D+febJQFfa+Q==} + '@stacks/network@7.0.2': + resolution: {integrity: sha512-XzHnoWqku/jRrTgMXhmh3c+I0O9vDH24KlhzGDZtBu+8CGGyHNPAZzGwvoUShonMXrXjEnfO9IYQwV5aJhfv6g==} + '@stacks/stacks-blockchain-api-types@7.14.1': resolution: {integrity: sha512-65hvhXxC+EUqHJAQsqlBCqXB+zwfxZICSKYJugdg6BCp9I9qniyfz5XyQeC4RMVo0tgEoRdS/b5ZCFo5kLWmxA==} - '@stacks/transactions@6.16.1': - resolution: {integrity: sha512-yCtUM+8IN0QJbnnlFhY1wBW7Q30Cxje3Zmy8DgqdBoM/EPPWadez/8wNWFANVAMyUZeQ9V/FY+8MAw4E+pCReA==} - '@stacks/transactions@7.0.0': resolution: {integrity: sha512-9kGTnJLwRQPugLzbdJ8MmFED+eRhlJKIXpz2mshyy238hvBc4T0jynsoJMi4qGqvJYzsiRYCLDPJVkkUde85vA==} + '@stacks/transactions@7.0.4': + resolution: {integrity: sha512-OTQSqb+xaq+QeHB1m83SONVjVs9DD1SMqX7UvvLmzQx3ppZNoRqbWx9UGjm1bVwpQ+mw44TVOe7w3Zm0HRYFfw==} + '@tootallnate/once@2.0.0': resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} @@ -1117,9 +1123,6 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} - '@types/bn.js@5.1.6': - resolution: {integrity: sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==} - '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -1153,11 +1156,8 @@ packages: '@types/minimatch@5.1.2': resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} - '@types/node@18.19.50': - resolution: {integrity: sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==} - - '@types/node@22.6.1': - resolution: {integrity: sha512-V48tCfcKb/e6cVUigLAaJDAILdMP0fUW6BidkPK4GpGjXcfbnoHasCZDwz3N3yVt5we2RHm4XTQCpv0KJz9zqw==} + '@types/node@22.13.5': + resolution: {integrity: sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -1548,8 +1548,8 @@ packages: cjs-module-lexer@1.4.1: resolution: {integrity: sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==} - clarity-abi@0.0.20: - resolution: {integrity: sha512-uAkKEp84Z3LnM8qruLJyB1LP2NEGwns6Sl6zBOiKeIgTfFMm3GrQlTgkFUboVV/RQwk0dJYc72sV8pvSrtn+Sg==} + clarity-abi@0.1.0: + resolution: {integrity: sha512-+hf+M/MXsZY7DeGsVOXwc6p8Fbq3xNm1Gdp2R3ASdUtXMDmdnAZPEg2RSI5kQu7sFhAkBvgLdpXvsv8E05m29g==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: @@ -1641,8 +1641,8 @@ packages: cross-fetch@3.1.8: resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} - cross-fetch@4.0.0: - resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + cross-fetch@4.1.0: + resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} @@ -3453,8 +3453,8 @@ packages: resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} engines: {node: '>=12'} - ts-clarity@0.0.26: - resolution: {integrity: sha512-XRwLquuSTDkw6iYEpznMEEgehqaIR9t313chiiZTfvtRCq/kWYXPHQhgdTw6wJBAHsqdVPuN69enwlKtu2IQPg==} + ts-clarity@0.1.0-pre.2: + resolution: {integrity: sha512-VejdNZm4+8e07BPdCvQShkcRd6TCT6l0OkjTxdv93EqIXUAdDXdm4YcowV0KYSD/U3VqSeJbGAzgnFmO0vEnhQ==} ts-jest@29.2.5: resolution: {integrity: sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==} @@ -3558,11 +3558,8 @@ packages: unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} @@ -4660,27 +4657,27 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.6.1 + '@types/node': 22.13.5 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2))': + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.6.1 + '@types/node': 22.13.5 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)) + jest-config: 29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -4705,7 +4702,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.6.1 + '@types/node': 22.13.5 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -4723,7 +4720,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.6.1 + '@types/node': 22.13.5 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -4745,7 +4742,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 22.6.1 + '@types/node': 22.13.5 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -4815,7 +4812,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.6.1 + '@types/node': 22.13.5 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -4958,19 +4955,9 @@ snapshots: '@size-limit/file': 11.1.5(size-limit@11.1.5) size-limit: 11.1.5 - '@stacks/common@6.16.0': - dependencies: - '@types/bn.js': 5.1.6 - '@types/node': 18.19.50 - '@stacks/common@7.0.0': {} - '@stacks/network@6.16.0': - dependencies: - '@stacks/common': 6.16.0 - cross-fetch: 3.1.8 - transitivePeerDependencies: - - encoding + '@stacks/common@7.0.2': {} '@stacks/network@7.0.0': dependencies: @@ -4979,19 +4966,15 @@ snapshots: transitivePeerDependencies: - encoding - '@stacks/stacks-blockchain-api-types@7.14.1': {} - - '@stacks/transactions@6.16.1': + '@stacks/network@7.0.2': dependencies: - '@noble/hashes': 1.1.5 - '@noble/secp256k1': 1.7.1 - '@stacks/common': 6.16.0 - '@stacks/network': 6.16.0 - c32check: 2.0.0 - lodash.clonedeep: 4.5.0 + '@stacks/common': 7.0.2 + cross-fetch: 3.1.8 transitivePeerDependencies: - encoding + '@stacks/stacks-blockchain-api-types@7.14.1': {} + '@stacks/transactions@7.0.0': dependencies: '@noble/hashes': 1.1.5 @@ -5003,6 +4986,17 @@ snapshots: transitivePeerDependencies: - encoding + '@stacks/transactions@7.0.4': + dependencies: + '@noble/hashes': 1.1.5 + '@noble/secp256k1': 1.7.1 + '@stacks/common': 7.0.2 + '@stacks/network': 7.0.2 + c32check: 2.0.0 + lodash.clonedeep: 4.5.0 + transitivePeerDependencies: + - encoding + '@tootallnate/once@2.0.0': {} '@tsconfig/node10@1.0.11': {} @@ -5036,20 +5030,16 @@ snapshots: dependencies: '@babel/types': 7.25.6 - '@types/bn.js@5.1.6': - dependencies: - '@types/node': 18.19.50 - '@types/estree@1.0.6': {} '@types/glob@7.2.0': dependencies: '@types/minimatch': 5.1.2 - '@types/node': 22.6.1 + '@types/node': 22.13.5 '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 22.6.1 + '@types/node': 22.13.5 '@types/istanbul-lib-coverage@2.0.6': {} @@ -5068,7 +5058,7 @@ snapshots: '@types/jsdom@20.0.1': dependencies: - '@types/node': 22.6.1 + '@types/node': 22.13.5 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 @@ -5078,13 +5068,9 @@ snapshots: '@types/minimatch@5.1.2': {} - '@types/node@18.19.50': + '@types/node@22.13.5': dependencies: - undici-types: 5.26.5 - - '@types/node@22.6.1': - dependencies: - undici-types: 6.19.8 + undici-types: 6.20.0 '@types/parse-json@4.0.2': {} @@ -5549,7 +5535,7 @@ snapshots: cjs-module-lexer@1.4.1: {} - clarity-abi@0.0.20(typescript@5.6.2): + clarity-abi@0.1.0(typescript@5.6.2): optionalDependencies: typescript: 5.6.2 @@ -5617,13 +5603,13 @@ snapshots: path-type: 4.0.0 yaml: 1.10.2 - create-jest@29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)): + create-jest@29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)) + jest-config: 29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -5640,7 +5626,7 @@ snapshots: transitivePeerDependencies: - encoding - cross-fetch@4.0.0: + cross-fetch@4.1.0: dependencies: node-fetch: 2.7.0 transitivePeerDependencies: @@ -5778,7 +5764,7 @@ snapshots: dependencies: webidl-conversions: 7.0.0 - dts-cli@2.0.5(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@jest/transform@29.7.0)(@jest/types@29.6.3)(@types/babel__core@7.20.5)(@types/node@22.6.1): + dts-cli@2.0.5(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@jest/transform@29.7.0)(@jest/types@29.6.3)(@types/babel__core@7.20.5)(@types/node@22.13.5): dependencies: '@babel/core': 7.25.2 '@babel/helper-module-imports': 7.24.7 @@ -5811,7 +5797,7 @@ snapshots: eslint-config-prettier: 8.10.0(eslint@8.57.1) eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint@8.57.1) eslint-plugin-import: 2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1) - eslint-plugin-jest: 27.9.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(jest@29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)))(typescript@5.6.2) + eslint-plugin-jest: 27.9.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(jest@29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)))(typescript@5.6.2) eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1) eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.1))(eslint@8.57.1)(prettier@2.8.8) eslint-plugin-react: 7.36.1(eslint@8.57.1) @@ -5820,9 +5806,9 @@ snapshots: execa: 4.1.0 figlet: 1.7.0 fs-extra: 10.1.0 - jest: 29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)) + jest: 29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)) jest-environment-jsdom: 29.7.0 - jest-watch-typeahead: 2.2.2(jest@29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2))) + jest-watch-typeahead: 2.2.2(jest@29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2))) jpjs: 1.2.1 lodash.merge: 4.6.2 ora: 5.4.1 @@ -5840,8 +5826,8 @@ snapshots: shelljs: 0.8.5 sort-package-json: 1.57.0 tiny-glob: 0.2.9 - ts-jest: 29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)))(typescript@5.6.2) - ts-node: 10.9.2(@types/node@22.6.1)(typescript@5.6.2) + ts-jest: 29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)))(typescript@5.6.2) + ts-node: 10.9.2(@types/node@22.13.5)(typescript@5.6.2) tslib: 2.7.0 type-fest: 2.19.0 typescript: 5.6.2 @@ -6095,13 +6081,13 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(jest@29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)))(typescript@5.6.2): + eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(jest@29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)))(typescript@5.6.2): dependencies: '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.6.2) eslint: 8.57.1 optionalDependencies: '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2) - jest: 29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)) + jest: 29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)) transitivePeerDependencies: - supports-color - typescript @@ -6777,7 +6763,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.6.1 + '@types/node': 22.13.5 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.3(babel-plugin-macros@3.1.0) @@ -6797,16 +6783,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)): + jest-cli@29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)) + create-jest: 29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)) + jest-config: 29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -6816,7 +6802,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)): + jest-config@29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)): dependencies: '@babel/core': 7.25.2 '@jest/test-sequencer': 29.7.0 @@ -6841,8 +6827,8 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 22.6.1 - ts-node: 10.9.2(@types/node@22.6.1)(typescript@5.6.2) + '@types/node': 22.13.5 + ts-node: 10.9.2(@types/node@22.13.5)(typescript@5.6.2) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -6872,7 +6858,7 @@ snapshots: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 22.6.1 + '@types/node': 22.13.5 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3 @@ -6886,7 +6872,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.6.1 + '@types/node': 22.13.5 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -6896,7 +6882,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 22.6.1 + '@types/node': 22.13.5 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -6935,7 +6921,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.6.1 + '@types/node': 22.13.5 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -6970,7 +6956,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.6.1 + '@types/node': 22.13.5 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -6998,7 +6984,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.6.1 + '@types/node': 22.13.5 chalk: 4.1.2 cjs-module-lexer: 1.4.1 collect-v8-coverage: 1.0.2 @@ -7044,7 +7030,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.6.1 + '@types/node': 22.13.5 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -7059,11 +7045,11 @@ snapshots: leven: 3.1.0 pretty-format: 29.7.0 - jest-watch-typeahead@2.2.2(jest@29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2))): + jest-watch-typeahead@2.2.2(jest@29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2))): dependencies: ansi-escapes: 6.2.1 chalk: 5.3.0 - jest: 29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)) + jest: 29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)) jest-regex-util: 29.6.3 jest-watcher: 29.7.0 slash: 5.1.0 @@ -7074,7 +7060,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.6.1 + '@types/node': 22.13.5 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -7083,17 +7069,17 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 22.6.1 + '@types/node': 22.13.5 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)): + jest@29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)) + jest-cli: 29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -7941,22 +7927,22 @@ snapshots: dependencies: punycode: 2.3.1 - ts-clarity@0.0.26(typescript@5.6.2): + ts-clarity@0.1.0-pre.2(typescript@5.6.2): dependencies: '@stacks/stacks-blockchain-api-types': 7.14.1 - '@stacks/transactions': 6.16.1 - clarity-abi: 0.0.20(typescript@5.6.2) - cross-fetch: 4.0.0 + '@stacks/transactions': 7.0.4 + clarity-abi: 0.1.0(typescript@5.6.2) + cross-fetch: 4.1.0 transitivePeerDependencies: - encoding - typescript - ts-jest@29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)))(typescript@5.6.2): + ts-jest@29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)))(typescript@5.6.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.6.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2)) + jest: 29.7.0(@types/node@22.13.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -7970,14 +7956,14 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.25.2) - ts-node@10.9.2(@types/node@22.6.1)(typescript@5.6.2): + ts-node@10.9.2(@types/node@22.13.5)(typescript@5.6.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.6.1 + '@types/node': 22.13.5 acorn: 8.12.1 acorn-walk: 8.3.4 arg: 4.1.3 @@ -8064,9 +8050,7 @@ snapshots: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 - undici-types@5.26.5: {} - - undici-types@6.19.8: {} + undici-types@6.20.0: {} unicode-canonical-property-names-ecmascript@2.0.1: {} diff --git a/src/clarity-api.ts b/src/clarity-api.ts new file mode 100644 index 0000000..512e993 --- /dev/null +++ b/src/clarity-api.ts @@ -0,0 +1,186 @@ +import { + ClarityType, + type ClarityValue, + type OptionalCV, +} from '@stacks/transactions'; +import type { + ClarityAbiFunction, + ClarityAbiMap, + ClarityAbiVariable, + TContractPrincipal, + TPrincipal, +} from 'clarity-abi'; +import { decodeAbi, encodeAbi } from 'ts-clarity'; +import type { + InferReadonlyCallParameterType, + InferReadonlyCallResultType, + InferMapValueType, + InferReadMapParameterType, + InferReadVariableParameterType, + InferVariableType, +} from 'ts-clarity'; +import { BatchProcessor } from './BatchProcessor'; + +// Shared processor instance with default settings +const defaultProcessor = new BatchProcessor({ + batchDelayMs: 100, +}); + +export type ReadonlyCallRuntimeOptions = { + sender?: TPrincipal; + contract: TContractPrincipal; + stacksEndpoint?: string; + indexBlockHash?: string; + batchProcessor?: BatchProcessor; +}; + +export type ReadMapRuntimeParameters = { + contract: TContractPrincipal; + stacksEndpoint?: string; + proof?: boolean; + indexBlockHash?: string; + batchProcessor?: BatchProcessor; +}; + +export type ReadVariableRuntimeParameterType = { + contract: TContractPrincipal; + stacksEndpoint?: string; + proof?: boolean; + indexBlockHash?: string; + batchProcessor?: BatchProcessor; +}; + +export async function callReadonly< + Functions extends readonly ClarityAbiFunction[] | readonly unknown[], + FunctionName extends string, +>( + params: InferReadonlyCallParameterType & + ReadonlyCallRuntimeOptions, +): Promise> { + const processor = params.batchProcessor ?? defaultProcessor; + const [deployer, contractName] = params.contract.split('.', 2); + const fn = String(params.functionName); + + const functionDef = (params.abi as readonly ClarityAbiFunction[]).find( + (def) => def.name === params.functionName, + ); + if (!functionDef) { + throw new Error(`failed to find function definition for ${params.functionName}`); + } + + const argsKV = (params as unknown as { args: Record }).args; + const args: ClarityValue[] = []; + for (const argDef of functionDef.args) { + args.push(encodeAbi(argDef.type, argsKV[argDef.name])); + } + + return new Promise((resolve, reject) => { + processor.enqueue({ + request: { + mode: 'readonly', + contractAddress: deployer, + contractName: contractName, + functionName: fn, + functionArgs: args, + }, + tip: params.indexBlockHash, + resolve: (result: ClarityValue | OptionalCV) => { + try { + const decoded = decodeAbi(functionDef.outputs.type, result); + resolve(decoded as InferReadonlyCallResultType); + } catch (error) { + reject(error); + } + }, + reject, + }); + }); +} + +export async function readMap< + Maps extends readonly ClarityAbiMap[] | readonly unknown[] = readonly ClarityAbiMap[], + MapName extends string = string, +>( + params: InferReadMapParameterType & ReadMapRuntimeParameters, +): Promise | null> { + const processor = params.batchProcessor ?? defaultProcessor; + const [deployer, contractName] = params.contract.split('.', 2); + + const mapDef = (params.abi as readonly ClarityAbiMap[]).find( + (m) => m.name === params.mapName, + ); + if (!mapDef) { + throw new Error(`failed to find map definition for ${params.mapName}`); + } + + const key: ClarityValue = encodeAbi(mapDef.key, params.key); + + return new Promise((resolve, reject) => { + processor.enqueue({ + request: { + mode: 'mapEntry', + contractAddress: deployer, + contractName: contractName, + mapName: params.mapName, + mapKey: key, + }, + tip: params.indexBlockHash, + resolve: (result: ClarityValue | OptionalCV) => { + try { + if (result.type === ClarityType.OptionalNone) { + resolve(null); + return; + } + if (result.type !== ClarityType.OptionalSome) { + throw new Error(`unexpected map value: ${result}`); + } + const someCV = result as { type: ClarityType.OptionalSome; value: ClarityValue }; + const decoded = decodeAbi(mapDef.value, someCV.value); + resolve(decoded as InferMapValueType); + } catch (error) { + reject(error); + } + }, + reject, + }); + }); +} + +export async function readVariable< + Variables extends readonly ClarityAbiVariable[] | readonly unknown[] = readonly ClarityAbiVariable[], + VariableName extends string = string, +>( + params: InferReadVariableParameterType & + ReadVariableRuntimeParameterType, +): Promise> { + const processor = params.batchProcessor ?? defaultProcessor; + const [deployer, contractName] = params.contract.split('.', 2); + + const varDef = (params.abi as readonly ClarityAbiVariable[]).find( + (def) => def.name === params.variableName, + ); + if (!varDef) { + throw new Error(`failed to find variable definition for ${params.variableName}`); + } + + return new Promise((resolve, reject) => { + processor.enqueue({ + request: { + mode: 'variable', + contractAddress: deployer, + contractName: contractName, + variableName: params.variableName, + }, + tip: params.indexBlockHash, + resolve: (result: ClarityValue | OptionalCV) => { + try { + const decoded = decodeAbi(varDef.type, result); + resolve(decoded as InferVariableType); + } catch (error) { + reject(error); + } + }, + reject, + }); + }); +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 1ba9b7f..b9544ea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,3 @@ export * from './BatchAPI'; export * from './simulation'; +export * from './clarity-api'; \ No newline at end of file diff --git a/src/sample/read.ts b/src/sample/read.ts index 06f27e4..a6e010d 100644 --- a/src/sample/read.ts +++ b/src/sample/read.ts @@ -1,12 +1,14 @@ import { contractPrincipalCV, principalCV, - stringAsciiCV, tupleCV, uintCV, } from '@stacks/transactions'; +import { SIP010TraitABI } from 'clarity-abi/abis' import { batchRead } from '../BatchAPI'; import { BatchProcessor } from '../BatchProcessor'; +import { callReadonly, readMap, readVariable } from '../clarity-api'; +import { unwrapResponse } from 'ts-clarity'; async function batchReadsExample() { const rs = await batchRead({ @@ -110,9 +112,71 @@ async function batchQueueProcessorExample() { console.log(result); } +// https://explorer.hiro.so/txid/SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.trait-sip-010 +const sip010 = { + functions: [ + { + name: 'get-balance', + access: 'read_only', + args: [ + { + name: 'who', + type: 'principal', + }, + ], + outputs: { + type: { + response: { + ok: 'uint128', + error: 'none', + }, + }, + }, + }, + ], + variables: [], + maps: [], + fungible_tokens: [], + non_fungible_tokens: [], + epoch: 'Epoch2_05', + clarity_version: 'Clarity1', +} as const; + +async function batchSip010Example() { + const supply = callReadonly({ + abi: SIP010TraitABI.functions, + functionName: 'get-total-supply', + contract: 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-alex', + }).then(unwrapResponse) + const balance = callReadonly({ + abi: SIP010TraitABI.functions, + functionName: 'get-balance', + contract: 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-alex', + args: { + who: 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-vault-v2-01', + }, + }).then(unwrapResponse) + const paused = readVariable({ + abi: [{ name: 'paused', type: 'bool', access: 'variable' },], + variableName: 'paused', + contract: 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-vault-v2-01', + }); + const approved = readMap({ + abi: [ + { key: 'principal', name: 'approved-tokens', value: 'bool' }, + ], + mapName: 'approved-tokens', + key: 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-alex', + contract: 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-vault-v2-01', + }) + const result = await Promise.all([supply, balance, paused, approved]); + console.log(result); +} + async function main() { await batchReadsExample(); await batchQueueProcessorExample(); + await batchSip010Example(); } if (require.main === module) {