diff --git a/README.md b/README.md index f5e63ba..cecfbeb 100644 --- a/README.md +++ b/README.md @@ -1 +1,92 @@ -Comprehensive Asset Tokenization Guideline +# Real World Asset Token Smart Contract + +## Overview + +This Clarity smart contract implements a comprehensive system for tokenizing real-world assets on the Stacks blockchain. It provides advanced features for asset management, token distribution, governance, and price feed integration. + +## Key Features + +1. Asset Registration and Management +2. Semi-Fungible Token (SFT) Implementation +3. Dividend Distribution System +4. Governance Proposal and Voting Mechanism +5. KYC (Know Your Customer) Integration +6. Price Feed Oracle Integration +7. Robust Input Validation and Error Handling + +## Core Functions + +### Asset Management + +- `register-asset`: Register a new real-world asset +- `claim-dividends`: Claim dividends for a specific asset + +### Governance + +- `create-proposal`: Create a new governance proposal +- `vote`: Vote on an existing proposal + +### Read-Only Functions + +- `get-asset-info`: Retrieve information about a specific asset +- `get-balance`: Get the token balance of an address for a specific asset +- `get-proposal`: Retrieve details of a specific proposal +- `get-vote`: Get voting information for a specific proposal and voter +- `get-price-feed`: Retrieve price feed information for an asset +- `get-last-claim`: Get the last dividend claim amount for an address + +## Data Structures + +- `assets`: Stores metadata and state for each registered asset +- `token-balances`: Tracks token ownership for each asset +- `kyc-status`: Manages KYC approval status and levels for addresses +- `proposals`: Stores governance proposal details +- `votes`: Records votes cast on proposals +- `dividend-claims`: Tracks dividend claims for each asset and address +- `price-feeds`: Manages price feed data for assets + +## Constants + +- `MAX-ASSET-VALUE`: 1 trillion (1,000,000,000,000) +- `MIN-ASSET-VALUE`: 1 thousand (1,000) +- `MAX-DURATION`: ~1 day in blocks (144) +- `MIN-DURATION`: ~1 hour in blocks (12) +- `MAX-KYC-LEVEL`: 5 +- `MAX-EXPIRY`: ~1 year in blocks (52,560) +- `tokens-per-asset`: 100,000 (number of SFTs per asset) + +## Error Handling + +The contract uses a comprehensive set of error codes for various scenarios, ensuring proper validation and error reporting. Some key error codes include: + +- `err-owner-only`: Operation restricted to contract owner +- `err-not-found`: Requested item not found +- `err-invalid-amount`: Invalid token or value amount +- `err-not-authorized`: User not authorized for the operation +- `err-kyc-required`: KYC approval required for the operation + +## Input Validation + +The contract implements several helper functions for input validation: + +- `validate-asset-value`: Ensures asset value is within allowed range +- `validate-duration`: Checks if proposal duration is valid +- `validate-kyc-level`: Verifies KYC level is within allowed range +- `validate-expiry`: Ensures expiry is set to a valid future block height +- `validate-minimum-votes`: Checks if minimum vote count is valid +- `validate-metadata-uri`: Verifies metadata URI length and format + +## Usage + +To use this contract, deploy it to the Stacks blockchain and interact with it using the provided public functions. Ensure that users have the necessary permissions and meet the required conditions (e.g., KYC approval) for their intended actions. + +## Security Considerations + +- Only the contract owner can register new assets +- KYC checks are implemented for certain operations +- Input validation is performed to prevent invalid data entry +- Proposal voting has minimum token holding requirements + +## Note on Completeness + +This README is based on the provided contract snippet. Some functionalities, such as KYC management and price feed updates, may not be fully represented here if they were not included in the provided code. diff --git a/tokenizer/.gitignore b/tokenizer/.gitignore new file mode 100644 index 0000000..f18b582 --- /dev/null +++ b/tokenizer/.gitignore @@ -0,0 +1,4 @@ + +settings/Mainnet.toml +settings/Testnet.toml +history.txt diff --git a/tokenizer/.vscode/settings.json b/tokenizer/.vscode/settings.json new file mode 100644 index 0000000..02e21eb --- /dev/null +++ b/tokenizer/.vscode/settings.json @@ -0,0 +1,4 @@ + +{ + "deno.enable": true, +} diff --git a/tokenizer/.vscode/tasks.json b/tokenizer/.vscode/tasks.json new file mode 100644 index 0000000..22af91c --- /dev/null +++ b/tokenizer/.vscode/tasks.json @@ -0,0 +1,18 @@ + +{ + "version": "2.0.0", + "tasks": [ + { + "label": "check contracts", + "group": "test", + "type": "shell", + "command": "clarinet check" + }, + { + "label": "test contracts", + "group": "test", + "type": "shell", + "command": "clarinet test" + } + ] +} diff --git a/tokenizer/Clarinet.toml b/tokenizer/Clarinet.toml new file mode 100644 index 0000000..59ba713 --- /dev/null +++ b/tokenizer/Clarinet.toml @@ -0,0 +1,22 @@ +[project] +name = "tokenizer" +authors = [] +description = "" +telemetry = true +requirements = [] +[contracts.rwa] +path = "contracts/rwa.clar" +depends_on = [] + +[repl] +costs_version = 2 +parser_version = 2 + +[repl.analysis] +passes = ["check_checker"] + +[repl.analysis.check_checker] +strict = false +trusted_sender = false +trusted_caller = false +callee_filter = false diff --git a/tokenizer/README.md b/tokenizer/README.md new file mode 100644 index 0000000..a553a38 --- /dev/null +++ b/tokenizer/README.md @@ -0,0 +1,77 @@ +## Overview + +The Real World Asset Tokenization Smart Contract enables the representation of physical assets (e.g., real estate, art, precious metals) as digital tokens. This contract introduces features like asset registration, dividend distribution, compliance via KYC checks, and voting-based governance. Tokenization allows fractional ownership, enabling smaller investors to invest in traditionally illiquid assets. + +## Features + +- **Asset Registration**: Register assets with metadata, value, and ownership details. +- **Fractional Ownership**: Assets are divided into smaller, tradeable tokens. +- **Dividend Distribution**: Distribute dividends to token holders based on ownership. +- **KYC Compliance**: Enforce KYC levels to ensure only verified addresses can hold assets. +- **Governance**: Implement on-chain voting for asset-related decisions. +- **Price Feed Integration**: Track asset prices and update them periodically. + +## Contract Components + +### Constants +- Define essential values like the maximum asset value, durations for proposals, and KYC levels. +- Include error codes for ease of debugging and user-friendly error handling. + +### Data Maps +- `assets`: Stores asset details, including ownership, metadata, and valuation. +- `token-balances`: Manages token balances for each asset holder. +- `kyc-status`: Keeps track of KYC approval levels for participants. +- `proposals` and `votes`: Implements governance features, allowing token holders to create and vote on proposals. +- `dividend-claims`: Records dividend claims for individual asset holders. +- `price-feeds`: Maintains price data for assets. + +### Helper Functions +- **Input Validators**: Ensure input values (e.g., asset value, duration) conform to specified criteria. +- **Utility Functions**: Retrieve asset and proposal IDs and verify KYC compliance. + +## Functions + +### Public Functions + +- **Asset Management** + - `register-asset(metadata-uri, asset-value)`: Registers a new asset, specifying metadata and valuation. Only the contract owner can register assets. + - `claim-dividends(asset-id)`: Allows token holders to claim dividends based on their share of ownership. + +- **Governance and Voting** + - `create-proposal(asset-id, title, duration, minimum-votes)`: Creates a proposal for asset-related decisions. A minimum amount of tokens is required to participate. + - `vote(proposal-id, vote-for, amount)`: Enables token holders to vote on proposals by committing a specific amount of tokens. + +### Read-Only Functions + +- `get-asset-info(asset-id)`: Retrieves detailed information about a specific asset. +- `get-balance(owner, asset-id)`: Checks the token balance of a particular owner for an asset. +- `get-proposal(proposal-id)`: Fetches details of a specific proposal. +- `get-price-feed(asset-id)`: Returns the current price information for an asset. + +### Private Functions + +- `get-next-asset-id()`, `get-next-proposal-id()`: Retrieve unique IDs for new assets or proposals. +- `validate-asset-value(value)`, `validate-duration(duration)`, etc.: Helper functions to validate input values, improving contract security and integrity. + +## Setup and Deployment + +1. **Prerequisites**: Ensure you have Clarity tools and a testnet wallet setup. +2. **Compilation**: Compile the contract using a Clarity-compatible development environment. +3. **Deployment**: Deploy the contract to the Stacks testnet or mainnet with a wallet holding enough STX for deployment fees. +4. **Post-deployment Configuration**: Configure price feeds and KYC information for users as needed. + + +## Error Codes + +| Code | Meaning | +|-----------------------|-----------------------------------| +| `err-owner-only` | Only the contract owner can execute this action | +| `err-not-found` | Asset or proposal not found | +| `err-already-listed` | Asset is already registered | +| `err-invalid-amount` | Invalid amount specified | +| `err-not-authorized` | Action requires additional permissions | +| `err-kyc-required` | KYC verification required | +| `err-invalid-duration`| Duration exceeds limits | +| `err-invalid-votes` | Minimum votes not met | + +-- Author: Amobi Ndubuisi diff --git a/tokenizer/contracts/rwa.clar b/tokenizer/contracts/rwa.clar new file mode 100644 index 0000000..3e708ff --- /dev/null +++ b/tokenizer/contracts/rwa.clar @@ -0,0 +1,312 @@ +;;Real World Asset Token Contract +;; Implements advanced features for real-world asset tokenization + +;; Constants +(define-constant contract-owner tx-sender) +(define-constant err-owner-only (err u100)) +(define-constant err-not-found (err u101)) +(define-constant err-already-listed (err u102)) +(define-constant err-invalid-amount (err u103)) +(define-constant err-not-authorized (err u104)) +(define-constant err-kyc-required (err u105)) +(define-constant err-vote-exists (err u106)) +(define-constant err-vote-ended (err u107)) +(define-constant err-price-expired (err u108)) + +(define-constant MAX-ASSET-VALUE u1000000000000) ;; 1 trillion +(define-constant MIN-ASSET-VALUE u1000) ;; 1 thousand +(define-constant MAX-DURATION u144) ;; ~1 day in blocks +(define-constant MIN-DURATION u12) ;; ~1 hour in blocks +(define-constant MAX-KYC-LEVEL u5) +(define-constant MAX-EXPIRY u52560) ;; ~1 year in blocks + +;; Add new error codes +(define-constant err-invalid-uri (err u110)) +(define-constant err-invalid-value (err u111)) +(define-constant err-invalid-duration (err u112)) +(define-constant err-invalid-kyc-level (err u113)) +(define-constant err-invalid-expiry (err u114)) +(define-constant err-invalid-votes (err u115)) +(define-constant err-invalid-address (err u116)) +(define-constant err-invalid-title (err u117)) + +;; Data Maps +(define-map assets + { asset-id: uint } + { + owner: principal, + metadata-uri: (string-ascii 256), + asset-value: uint, + is-locked: bool, + creation-height: uint, + last-price-update: uint, + total-dividends: uint + } +) + +(define-map token-balances + { owner: principal, asset-id: uint } + { balance: uint } +) + +(define-map kyc-status + { address: principal } + { + is-approved: bool, + level: uint, + expiry: uint + } +) + +(define-map proposals + { proposal-id: uint } + { + title: (string-ascii 256), + asset-id: uint, + start-height: uint, + end-height: uint, + executed: bool, + votes-for: uint, + votes-against: uint, + minimum-votes: uint + } +) + +(define-map votes + { proposal-id: uint, voter: principal } + { vote-amount: uint } +) + +(define-map dividend-claims + { asset-id: uint, claimer: principal } + { last-claimed-amount: uint } +) + +(define-map price-feeds + { asset-id: uint } + { + price: uint, + decimals: uint, + last-updated: uint, + oracle: principal + } +) + +;; SFTs per asset +(define-constant tokens-per-asset u100000) + +;; Helper functions for input validation +(define-private (validate-asset-value (value uint)) + (and + (>= value MIN-ASSET-VALUE) + (<= value MAX-ASSET-VALUE) + ) +) + +(define-private (validate-duration (duration uint)) + (and + (>= duration MIN-DURATION) + (<= duration MAX-DURATION) + ) +) + +(define-private (validate-kyc-level (level uint)) + (<= level MAX-KYC-LEVEL) +) + +(define-private (validate-expiry (expiry uint)) + (and + (> expiry block-height) + (<= (- expiry block-height) MAX-EXPIRY) + ) +) + +(define-private (validate-minimum-votes (vote-count uint)) + (and + (> vote-count u0) + (<= vote-count tokens-per-asset) + ) +) + +(define-private (validate-metadata-uri (uri (string-ascii 256))) + (and + (> (len uri) u0) + (<= (len uri) u256) + ) +) + +(define-public (register-asset + (metadata-uri (string-ascii 256)) + (asset-value uint)) + (begin + (asserts! (is-eq tx-sender contract-owner) err-owner-only) + (asserts! (validate-metadata-uri metadata-uri) err-invalid-uri) + (asserts! (validate-asset-value asset-value) err-invalid-value) + + (let + ((asset-id (get-next-asset-id))) + (map-set assets + { asset-id: asset-id } + { + owner: contract-owner, + metadata-uri: metadata-uri, + asset-value: asset-value, + is-locked: false, + creation-height: block-height, + last-price-update: block-height, + total-dividends: u0 + } + ) + (map-set token-balances + { owner: contract-owner, asset-id: asset-id } + { balance: tokens-per-asset } + ) + (ok asset-id) + ) + ) +) + + + +(define-public (claim-dividends (asset-id uint)) + (let + ( + (asset (unwrap! (get-asset-info asset-id) err-not-found)) + (balance (get-balance tx-sender asset-id)) + (last-claim (get-last-claim asset-id tx-sender)) + (total-dividends (get total-dividends asset)) + (claimable-amount (/ (* balance (- total-dividends last-claim)) tokens-per-asset)) + ) + (asserts! (> claimable-amount u0) err-invalid-amount) + (ok (map-set dividend-claims + { asset-id: asset-id, claimer: tx-sender } + { last-claimed-amount: total-dividends } + )) + ) +) + +(define-public (create-proposal + (asset-id uint) + (title (string-ascii 256)) + (duration uint) + (minimum-votes uint)) + (begin + (asserts! (validate-duration duration) err-invalid-duration) + (asserts! (validate-minimum-votes minimum-votes) err-invalid-votes) + (asserts! (validate-metadata-uri title) err-invalid-title) + (asserts! (>= (get-balance tx-sender asset-id) (/ tokens-per-asset u10)) err-not-authorized) + + (let + ((proposal-id (get-next-proposal-id))) + (ok (map-set proposals + { proposal-id: proposal-id } + { + title: title, + asset-id: asset-id, + start-height: block-height, + end-height: (+ block-height duration), + executed: false, + votes-for: u0, + votes-against: u0, + minimum-votes: minimum-votes + } + )) + ) + ) +) + +(define-public (vote + (proposal-id uint) + (vote-for bool) + (amount uint)) + (let + ( + (proposal (unwrap! (get-proposal proposal-id) err-not-found)) + (asset-id (get asset-id proposal)) + (balance (get-balance tx-sender asset-id)) + ) + (begin + (asserts! (>= balance amount) err-invalid-amount) + (asserts! (< block-height (get end-height proposal)) err-vote-ended) + (asserts! (is-none (get-vote proposal-id tx-sender)) err-vote-exists) + + (map-set votes + { proposal-id: proposal-id, voter: tx-sender } + { vote-amount: amount } + ) + (ok (map-set proposals + { proposal-id: proposal-id } + (merge proposal + { + votes-for: (if vote-for + (+ (get votes-for proposal) amount) + (get votes-for proposal) + ), + votes-against: (if vote-for + (get votes-against proposal) + (+ (get votes-against proposal) amount) + ) + } + )) + ) + ) + ) +) + +;; Read Functions +(define-read-only (get-asset-info (asset-id uint)) + (map-get? assets { asset-id: asset-id }) +) + +(define-read-only (get-balance (owner principal) (asset-id uint)) + (default-to u0 + (get balance + (map-get? token-balances + { owner: owner, asset-id: asset-id } + ) + ) + ) +) + +(define-read-only (get-proposal (proposal-id uint)) + (map-get? proposals { proposal-id: proposal-id }) +) + +(define-read-only (get-vote (proposal-id uint) (voter principal)) + (map-get? votes { proposal-id: proposal-id, voter: voter }) +) + +(define-read-only (get-price-feed (asset-id uint)) + (map-get? price-feeds { asset-id: asset-id }) +) + +(define-read-only (get-last-claim (asset-id uint) (claimer principal)) + (default-to u0 + (get last-claimed-amount + (map-get? dividend-claims + { asset-id: asset-id, claimer: claimer } + ) + ) + ) +) + +;; Private Functions +(define-private (get-next-asset-id) + (default-to u1 + (get-last-asset-id) + ) +) + +(define-private (get-next-proposal-id) + (default-to u1 + (get-last-proposal-id) + ) +) + +(define-private (get-last-asset-id) + none +) + +(define-private (get-last-proposal-id) + none +) diff --git a/tokenizer/settings/Devnet.toml b/tokenizer/settings/Devnet.toml new file mode 100644 index 0000000..8a5ff75 --- /dev/null +++ b/tokenizer/settings/Devnet.toml @@ -0,0 +1,127 @@ +[network] +name = "devnet" +deployment_fee_rate = 10 + +[accounts.deployer] +mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw" +balance = 100_000_000_000_000 +# secret_key: 753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601 +# stx_address: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM +# btc_address: mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH + +[accounts.wallet_1] +mnemonic = "sell invite acquire kitten bamboo drastic jelly vivid peace spawn twice guilt pave pen trash pretty park cube fragile unaware remain midnight betray rebuild" +balance = 100_000_000_000_000 +# secret_key: 7287ba251d44a4d3fd9276c88ce34c5c52a038955511cccaf77e61068649c17801 +# stx_address: ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 +# btc_address: mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC + +[accounts.wallet_2] +mnemonic = "hold excess usual excess ring elephant install account glad dry fragile donkey gaze humble truck breeze nation gasp vacuum limb head keep delay hospital" +balance = 100_000_000_000_000 +# secret_key: 530d9f61984c888536871c6573073bdfc0058896dc1adfe9a6a10dfacadc209101 +# stx_address: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG +# btc_address: muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG + +[accounts.wallet_3] +mnemonic = "cycle puppy glare enroll cost improve round trend wrist mushroom scorpion tower claim oppose clever elephant dinosaur eight problem before frozen dune wagon high" +balance = 100_000_000_000_000 +# secret_key: d655b2523bcd65e34889725c73064feb17ceb796831c0e111ba1a552b0f31b3901 +# stx_address: ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC +# btc_address: mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7 + +[accounts.wallet_4] +mnemonic = "board list obtain sugar hour worth raven scout denial thunder horse logic fury scorpion fold genuine phrase wealth news aim below celery when cabin" +balance = 100_000_000_000_000 +# secret_key: f9d7206a47f14d2870c163ebab4bf3e70d18f5d14ce1031f3902fbbc894fe4c701 +# stx_address: ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND +# btc_address: mg1C76bNTutiCDV3t9nWhZs3Dc8LzUufj8 + +[accounts.wallet_5] +mnemonic = "hurry aunt blame peanut heavy update captain human rice crime juice adult scale device promote vast project quiz unit note reform update climb purchase" +balance = 100_000_000_000_000 +# secret_key: 3eccc5dac8056590432db6a35d52b9896876a3d5cbdea53b72400bc9c2099fe801 +# stx_address: ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB +# btc_address: mweN5WVqadScHdA81aATSdcVr4B6dNokqx + +[accounts.wallet_6] +mnemonic = "area desk dutch sign gold cricket dawn toward giggle vibrant indoor bench warfare wagon number tiny universe sand talk dilemma pottery bone trap buddy" +balance = 100_000_000_000_000 +# secret_key: 7036b29cb5e235e5fd9b09ae3e8eec4404e44906814d5d01cbca968a60ed4bfb01 +# stx_address: ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0 +# btc_address: mzxXgV6e4BZSsz8zVHm3TmqbECt7mbuErt + +[accounts.wallet_7] +mnemonic = "prevent gallery kind limb income control noise together echo rival record wedding sense uncover school version force bleak nuclear include danger skirt enact arrow" +balance = 100_000_000_000_000 +# secret_key: b463f0df6c05d2f156393eee73f8016c5372caa0e9e29a901bb7171d90dc4f1401 +# stx_address: ST3PF13W7Z0RRM42A8VZRVFQ75SV1K26RXEP8YGKJ +# btc_address: n37mwmru2oaVosgfuvzBwgV2ysCQRrLko7 + +[accounts.wallet_8] +mnemonic = "female adjust gallery certain visit token during great side clown fitness like hurt clip knife warm bench start reunion globe detail dream depend fortune" +balance = 100_000_000_000_000 +# secret_key: 6a1a754ba863d7bab14adbbc3f8ebb090af9e871ace621d3e5ab634e1422885e01 +# stx_address: ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP +# btc_address: n2v875jbJ4RjBnTjgbfikDfnwsDV5iUByw + +[accounts.wallet_9] +mnemonic = "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform" +balance = 100_000_000_000_000 +# secret_key: de433bdfa14ec43aa1098d5be594c8ffb20a31485ff9de2923b2689471c401b801 +# stx_address: STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 +# btc_address: mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d + +[devnet] +disable_bitcoin_explorer = true +# disable_stacks_explorer = true +# disable_stacks_api = true +# working_dir = "tmp/devnet" +# stacks_node_events_observers = ["host.docker.internal:8002"] +# miner_mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw" +# miner_derivation_path = "m/44'/5757'/0'/0/0" +# orchestrator_port = 20445 +# bitcoin_node_p2p_port = 18444 +# bitcoin_node_rpc_port = 18443 +# bitcoin_node_username = "devnet" +# bitcoin_node_password = "devnet" +# bitcoin_controller_port = 18442 +# bitcoin_controller_block_time = 30_000 +# stacks_node_rpc_port = 20443 +# stacks_node_p2p_port = 20444 +# stacks_api_port = 3999 +# stacks_api_events_port = 3700 +# bitcoin_explorer_port = 8001 +# stacks_explorer_port = 8000 +# postgres_port = 5432 +# postgres_username = "postgres" +# postgres_password = "postgres" +# postgres_database = "postgres" +# bitcoin_node_image_url = "quay.io/hirosystems/bitcoind:devnet" +# stacks_node_image_url = "localhost:5000/stacks-node:devnet" +# stacks_api_image_url = "blockstack/stacks-blockchain-api:latest" +# stacks_explorer_image_url = "blockstack/explorer:latest" +# bitcoin_explorer_image_url = "quay.io/hirosystems/bitcoin-explorer:devnet" +# postgres_image_url = "postgres:alpine" + +# Send some stacking orders +[[devnet.pox_stacking_orders]] +start_at_cycle = 3 +duration = 12 +wallet = "wallet_1" +slots = 2 +btc_address = "mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC" + +[[devnet.pox_stacking_orders]] +start_at_cycle = 3 +duration = 12 +wallet = "wallet_2" +slots = 1 +btc_address = "muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG" + +[[devnet.pox_stacking_orders]] +start_at_cycle = 3 +duration = 12 +wallet = "wallet_3" +slots = 1 +btc_address = "mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7" diff --git a/tokenizer/tests/rwa_test.ts b/tokenizer/tests/rwa_test.ts new file mode 100644 index 0000000..9a18ae0 --- /dev/null +++ b/tokenizer/tests/rwa_test.ts @@ -0,0 +1,26 @@ + +import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet@v0.14.0/index.ts'; +import { assertEquals } from 'https://deno.land/std@0.90.0/testing/asserts.ts'; + +Clarinet.test({ + name: "Ensure that <...>", + async fn(chain: Chain, accounts: Map) { + let block = chain.mineBlock([ + /* + * Add transactions with: + * Tx.contractCall(...) + */ + ]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + block = chain.mineBlock([ + /* + * Add transactions with: + * Tx.contractCall(...) + */ + ]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 3); + }, +});