From 349797955c7193e736a28aba8db17c16bd20509b Mon Sep 17 00:00:00 2001 From: toyinb Date: Wed, 23 Oct 2024 17:03:15 +0100 Subject: [PATCH 1/2] Added Readme file and Batch transfer smart contract and also fixed some errors --- batch-payment/.gitignore | 4 + batch-payment/.vscode/settings.json | 4 + batch-payment/.vscode/tasks.json | 18 ++ batch-payment/Clarinet.toml | 11 + batch-payment/README.md | 0 batch-payment/contracts/batch-transfer.clar | 314 ++++++++++++++++++++ batch-payment/settings/Devnet.toml | 127 ++++++++ batch-payment/tests/batch-transfer_test.ts | 26 ++ 8 files changed, 504 insertions(+) create mode 100644 batch-payment/.gitignore create mode 100644 batch-payment/.vscode/settings.json create mode 100644 batch-payment/.vscode/tasks.json create mode 100644 batch-payment/Clarinet.toml create mode 100644 batch-payment/README.md create mode 100644 batch-payment/contracts/batch-transfer.clar create mode 100644 batch-payment/settings/Devnet.toml create mode 100644 batch-payment/tests/batch-transfer_test.ts diff --git a/batch-payment/.gitignore b/batch-payment/.gitignore new file mode 100644 index 0000000..f18b582 --- /dev/null +++ b/batch-payment/.gitignore @@ -0,0 +1,4 @@ + +settings/Mainnet.toml +settings/Testnet.toml +history.txt diff --git a/batch-payment/.vscode/settings.json b/batch-payment/.vscode/settings.json new file mode 100644 index 0000000..02e21eb --- /dev/null +++ b/batch-payment/.vscode/settings.json @@ -0,0 +1,4 @@ + +{ + "deno.enable": true, +} diff --git a/batch-payment/.vscode/tasks.json b/batch-payment/.vscode/tasks.json new file mode 100644 index 0000000..22af91c --- /dev/null +++ b/batch-payment/.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/batch-payment/Clarinet.toml b/batch-payment/Clarinet.toml new file mode 100644 index 0000000..aa70ad6 --- /dev/null +++ b/batch-payment/Clarinet.toml @@ -0,0 +1,11 @@ +[project] +name = "batch-payment" +authors = [] +description = "" +telemetry = true +requirements = [] +analysis = ["check_checker"] +costs_version = 2 +[contracts.batch-transfer] +path = "contracts/batch-transfer.clar" +depends_on = [] diff --git a/batch-payment/README.md b/batch-payment/README.md new file mode 100644 index 0000000..e69de29 diff --git a/batch-payment/contracts/batch-transfer.clar b/batch-payment/contracts/batch-transfer.clar new file mode 100644 index 0000000..1477dbb --- /dev/null +++ b/batch-payment/contracts/batch-transfer.clar @@ -0,0 +1,314 @@ +;; Batch Transfer Smart Contract + +;; Error Constants - Clearly defined error codes for all possible failure scenarios +(define-constant ERROR-UNAUTHORIZED-ACCESS (err u1)) +(define-constant ERROR-INVALID-TRANSFER-AMOUNT (err u2)) +(define-constant ERROR-TRANSFER-EXECUTION-FAILED (err u3)) +(define-constant ERROR-INSUFFICIENT-TOKEN-BALANCE (err u4)) +(define-constant ERROR-INVALID-RECIPIENT-ADDRESS (err u5)) +(define-constant ERROR-BATCH-SIZE-EXCEEDED (err u6)) +(define-constant ERROR-ADDRESS-BLACKLISTED (err u7)) +(define-constant ERROR-TRANSFER-RATE-LIMIT-EXCEEDED (err u8)) +(define-constant ERROR-CONTRACT-PAUSED (err u9)) +(define-constant ERROR-INVALID-MEMO-LENGTH (err u10)) +(define-constant ERROR-DUPLICATE-TRANSACTION (err u11)) +(define-constant ERROR-TOKEN-RECOVERY-FAILED (err u12)) + +;; System Constants - Core configuration parameters +(define-constant CONTRACT-ADMINISTRATOR tx-sender) +(define-constant MAXIMUM-BATCH-TRANSFERS u50) +(define-constant MINIMUM-TRANSFER-THRESHOLD u1) +(define-constant DAILY-RATE-LIMIT-BLOCKS u144) ;; 24 hours in blocks +(define-constant MAXIMUM-MEMO-CHARACTERS u50) +(define-constant MAXIMUM-FEE-BASIS-POINTS u100) ;; 10% maximum fee + +;; Contract State Variables - Primary contract operational parameters +(define-data-var contract-operational-status bool true) +(define-data-var lifetime-transfer-count uint u0) +(define-data-var transaction-fee-basis-points uint u5) ;; 0.5% default fee +(define-data-var treasury-wallet-address principal CONTRACT-ADMINISTRATOR) + +;; Transaction History Storage - Comprehensive record keeping +(define-map transaction-history + { transaction-identifier: uint } + { + sender-address: principal, + block-timestamp: uint, + transaction-value: uint, + transaction-status: (string-ascii 20), + transaction-memo: (optional (string-ascii 50)), + collected-fee-amount: uint + } +) + +;; Security and Compliance Management +(define-map restricted-addresses + { wallet-address: principal } + { restriction-timestamp: uint } +) + +(define-map transfer-velocity-tracking + { wallet-address: principal } + { + last-transaction-block: uint, + daily-transfer-count: uint, + cumulative-amount: uint + } +) + +(define-map privileged-addresses + { wallet-address: principal } + { unlimited-transfer-privileges: bool } +) + +;; Transaction Security +(define-map transaction-nonce-registry + { sender-address: principal, transaction-nonce: uint } + { nonce-used: bool } +) + +;; Event Management +(define-data-var event-sequence-number uint u0) + +;; Event Emission Function +(define-private (emit-transaction-event + (recipient-address principal) + (transfer-amount uint) + (transaction-memo (optional (string-ascii 50))) +) + (let ((event-identifier (+ (var-get event-sequence-number) u1))) + (var-set event-sequence-number event-identifier) + (print { + event-type: "token-transfer", + event-id: event-identifier, + sender: tx-sender, + recipient: recipient-address, + amount: transfer-amount, + memo: transaction-memo, + block-timestamp: block-height + }) + ) +) + +;; Read-Only Query Functions +(define-read-only (get-transaction-details (transaction-id uint)) + (map-get? transaction-history { transaction-identifier: transaction-id }) +) + +(define-read-only (get-operational-status) + (ok (var-get contract-operational-status)) +) + +(define-read-only (get-lifetime-transfers) + (ok (var-get lifetime-transfer-count)) +) + +(define-read-only (check-address-restrictions (wallet-address principal)) + (is-some (map-get? restricted-addresses { wallet-address: wallet-address })) +) + +(define-read-only (get-address-velocity-metrics (wallet-address principal)) + (map-get? transfer-velocity-tracking { wallet-address: wallet-address }) +) + +;; Core Transaction Processing Functions +(define-private (calculate-transaction-fee (transfer-amount uint)) + (/ (* transfer-amount (var-get transaction-fee-basis-points)) u1000) +) + +(define-private (validate-transaction-parameters (recipient-address principal) (transfer-amount uint)) + (let ( + (sender-token-balance (stx-get-balance tx-sender)) + (recipient-restricted (check-address-restrictions recipient-address)) + ) + (asserts! (>= sender-token-balance transfer-amount) ERROR-INSUFFICIENT-TOKEN-BALANCE) + (asserts! (>= transfer-amount MINIMUM-TRANSFER-THRESHOLD) ERROR-INVALID-TRANSFER-AMOUNT) + (asserts! (not recipient-restricted) ERROR-ADDRESS-BLACKLISTED) + (ok true) + ) +) + +(define-private (execute-single-transfer (recipient-address principal) (transfer-amount uint)) + (let ( + (transaction-fee (calculate-transaction-fee transfer-amount)) + (net-transfer-amount (- transfer-amount transaction-fee)) + ) + (match (stx-transfer? net-transfer-amount tx-sender recipient-address) + transfer-success (begin + (match (stx-transfer? transaction-fee tx-sender (var-get treasury-wallet-address)) + fee-success (begin + (emit-transaction-event recipient-address net-transfer-amount none) + (ok true)) + fee-error ERROR-TRANSFER-EXECUTION-FAILED)) + transfer-error ERROR-TRANSFER-EXECUTION-FAILED) + ) +) + +;; Rate Limiting Implementation +(define-private (verify-transfer-velocity (wallet-address principal) (transfer-count uint)) + (let ((current-metrics (get-address-velocity-metrics wallet-address))) + (match current-metrics + existing-metrics + (if (> (- block-height (get last-transaction-block existing-metrics)) DAILY-RATE-LIMIT-BLOCKS) + (begin + (map-set transfer-velocity-tracking + { wallet-address: wallet-address } + { + last-transaction-block: block-height, + daily-transfer-count: transfer-count, + cumulative-amount: u0 + } + ) + true) + (<= (+ (get daily-transfer-count existing-metrics) transfer-count) MAXIMUM-BATCH-TRANSFERS)) + (begin + (map-set transfer-velocity-tracking + { wallet-address: wallet-address } + { + last-transaction-block: block-height, + daily-transfer-count: transfer-count, + cumulative-amount: u0 + } + ) + true) + ) + ) +) + +;; Primary Public Functions +(define-public (execute-batch-transfer-with-memo + (recipient-addresses (list 50 principal)) + (transfer-amounts (list 50 uint)) + (transaction-memo (optional (string-ascii 50))) + (transaction-nonce uint) +) + (let + ( + (batch-size (len recipient-addresses)) + (current-transaction-id (+ (var-get lifetime-transfer-count) u1)) + (velocity-metrics (get-address-velocity-metrics tx-sender)) + ) + ;; Core validations + (asserts! (var-get contract-operational-status) ERROR-CONTRACT-PAUSED) + (asserts! (is-eq (len recipient-addresses) (len transfer-amounts)) ERROR-INVALID-TRANSFER-AMOUNT) + (asserts! (<= batch-size MAXIMUM-BATCH-TRANSFERS) ERROR-BATCH-SIZE-EXCEEDED) + + ;; Nonce validation + (asserts! (is-none (map-get? transaction-nonce-registry + { sender-address: tx-sender, transaction-nonce: transaction-nonce })) + ERROR-DUPLICATE-TRANSACTION) + + ;; Velocity check for non-privileged addresses + (match (map-get? privileged-addresses { wallet-address: tx-sender }) + privilege-info (if (get unlimited-transfer-privileges privilege-info) + true ;; Skip velocity check for privileged addresses + (verify-transfer-velocity tx-sender batch-size)) ;; Perform velocity check for non-privileged + (verify-transfer-velocity tx-sender batch-size) ;; No privilege info found, perform velocity check + ) + + ;; Process transfers + (map execute-single-transfer recipient-addresses transfer-amounts) + + ;; Record transaction + (map-set transaction-history + { transaction-identifier: current-transaction-id } + { + sender-address: tx-sender, + block-timestamp: block-height, + transaction-value: (fold + transfer-amounts u0), + transaction-status: "completed", + transaction-memo: transaction-memo, + collected-fee-amount: (fold + (map calculate-transaction-fee transfer-amounts) u0) + } + ) + + ;; Update nonce registry + (map-set transaction-nonce-registry + { sender-address: tx-sender, transaction-nonce: transaction-nonce } + { nonce-used: true } + ) + + ;; Update transfer count + (var-set lifetime-transfer-count current-transaction-id) + (ok true) + ) +) + +;; Administrative Functions +(define-public (update-operational-status (new-status bool)) + (begin + (asserts! (is-eq tx-sender CONTRACT-ADMINISTRATOR) ERROR-UNAUTHORIZED-ACCESS) + (ok (var-set contract-operational-status new-status)) + ) +) + +(define-public (update-fee-rate (new-fee-basis-points uint)) + (begin + (asserts! (is-eq tx-sender CONTRACT-ADMINISTRATOR) ERROR-UNAUTHORIZED-ACCESS) + (asserts! (<= new-fee-basis-points MAXIMUM-FEE-BASIS-POINTS) ERROR-INVALID-TRANSFER-AMOUNT) + (ok (var-set transaction-fee-basis-points new-fee-basis-points)) + ) +) + +(define-public (update-treasury-address (new-treasury-address principal)) + (begin + (asserts! (is-eq tx-sender CONTRACT-ADMINISTRATOR) ERROR-UNAUTHORIZED-ACCESS) + (ok (var-set treasury-wallet-address new-treasury-address)) + ) +) + +;; Address Management Functions +(define-public (add-restricted-address (wallet-address principal)) + (begin + (asserts! (is-eq tx-sender CONTRACT-ADMINISTRATOR) ERROR-UNAUTHORIZED-ACCESS) + (ok (map-set restricted-addresses + { wallet-address: wallet-address } + { restriction-timestamp: block-height })) + ) +) + +(define-public (remove-restricted-address (wallet-address principal)) + (begin + (asserts! (is-eq tx-sender CONTRACT-ADMINISTRATOR) ERROR-UNAUTHORIZED-ACCESS) + (ok (map-delete restricted-addresses { wallet-address: wallet-address })) + ) +) + +(define-public (add-privileged-address (wallet-address principal)) + (begin + (asserts! (is-eq tx-sender CONTRACT-ADMINISTRATOR) ERROR-UNAUTHORIZED-ACCESS) + (ok (map-set privileged-addresses + { wallet-address: wallet-address } + { unlimited-transfer-privileges: true })) + ) +) + +;; Emergency Functions +(define-public (execute-emergency-withdrawal (withdrawal-amount uint)) + (begin + (asserts! (is-eq tx-sender CONTRACT-ADMINISTRATOR) ERROR-UNAUTHORIZED-ACCESS) + (asserts! (>= (stx-get-balance (as-contract tx-sender)) withdrawal-amount) + ERROR-INSUFFICIENT-TOKEN-BALANCE) + (as-contract (stx-transfer? withdrawal-amount tx-sender CONTRACT-ADMINISTRATOR)) + ) +) + +(define-public (recover-stuck-tokens + (token-contract-address principal) + (recovery-amount uint) +) + (begin + (asserts! (is-eq tx-sender CONTRACT-ADMINISTRATOR) ERROR-UNAUTHORIZED-ACCESS) + (contract-call? token-contract-address transfer + recovery-amount + (as-contract tx-sender) + CONTRACT-ADMINISTRATOR + none) + ) +) + +;; Contract initialization +(begin + (asserts! (is-eq tx-sender CONTRACT-ADMINISTRATOR) ERROR-UNAUTHORIZED-ACCESS) + (ok true) +) \ No newline at end of file diff --git a/batch-payment/settings/Devnet.toml b/batch-payment/settings/Devnet.toml new file mode 100644 index 0000000..8a5ff75 --- /dev/null +++ b/batch-payment/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/batch-payment/tests/batch-transfer_test.ts b/batch-payment/tests/batch-transfer_test.ts new file mode 100644 index 0000000..9a18ae0 --- /dev/null +++ b/batch-payment/tests/batch-transfer_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); + }, +}); From 65bb3d54753127c2c1472c4a0813d8a0c2e21c22 Mon Sep 17 00:00:00 2001 From: toyinb Date: Wed, 23 Oct 2024 17:21:27 +0100 Subject: [PATCH 2/2] Fixed all errors --- batch-payment/Clarinet.toml | 2 +- batch-payment/README.md | 119 ++++++++++++++++++++ batch-payment/contracts/batch-transfer.clar | 30 ++++- 3 files changed, 148 insertions(+), 3 deletions(-) diff --git a/batch-payment/Clarinet.toml b/batch-payment/Clarinet.toml index aa70ad6..962e3bd 100644 --- a/batch-payment/Clarinet.toml +++ b/batch-payment/Clarinet.toml @@ -8,4 +8,4 @@ analysis = ["check_checker"] costs_version = 2 [contracts.batch-transfer] path = "contracts/batch-transfer.clar" -depends_on = [] +depends_on = [] \ No newline at end of file diff --git a/batch-payment/README.md b/batch-payment/README.md index e69de29..25bd4d2 100644 --- a/batch-payment/README.md +++ b/batch-payment/README.md @@ -0,0 +1,119 @@ +# Batch Transfer Smart Contract + +A secure and efficient Clarity smart contract for executing batch token transfers on the Stacks blockchain with comprehensive transaction monitoring, security controls, and administrative features. + +## Overview + +This smart contract enables efficient batch transfers of tokens while implementing robust security measures, rate limiting, and comprehensive transaction tracking. It's designed for scenarios requiring high-volume transfers while maintaining security and transparency. + +### Key Benefits +- Reduced gas costs through batch processing +- Built-in security controls and rate limiting +- Comprehensive transaction history +- Flexible administrative controls +- Emergency safety mechanisms + +## Features + +### Core Functionality +1. **Batch Transfers** + - Process up to 50 transfers in a single transaction + - Automatic fee calculation and distribution + - Transaction memo support + +2. **Security Controls** + - Address blacklisting + - Rate limiting + - Transaction nonce validation + - Privileged address management + +3. **Transaction Monitoring** + - Detailed transaction history + - Event logging + - Transfer velocity tracking + +4. **Administrative Functions** + - Contract pause/resume + - Fee rate adjustment + - Treasury address management + - Emergency fund recovery + +## Security Measures + +1. **Rate Limiting** + - Maximum batch size: 50 transfers + - Daily transaction limits + - Configurable rate limiting per address + +2. **Access Controls** + - Administrator-only functions + - Blacklist functionality + - Privileged address system + +3. **Transaction Safety** + - Nonce-based replay protection + - Balance verification + - Fee validation + +4. **Emergency Controls** + - Contract pause mechanism + - Emergency withdrawal function + - Stuck token recovery + +## Contract Configuration + +### System Constants +```clarity +MAXIMUM-BATCH-TRANSFERS: u50 +MINIMUM-TRANSFER-THRESHOLD: u1 +DAILY-RATE-LIMIT-BLOCKS: u144 +MAXIMUM-MEMO-CHARACTERS: u50 +MAXIMUM-FEE-BASIS-POINTS: u100 +``` + +### Default Settings +- Default fee rate: 0.5% (5 basis points) +- Contract operational status: Active +- Treasury wallet: Contract administrator + +## Security Considerations + +1. **Rate Limiting** + - Monitor transfer velocity + - Adjust DAILY_RATE_LIMIT_BLOCKS if needed + - Review privileged addresses regularly + +2. **Access Control** + - Secure administrator private key + - Regular review of restricted addresses + - Monitor privileged address usage + +3. **Transaction Safety** + - Verify nonce uniqueness + - Check recipient addresses + - Validate transfer amounts + +## Error Codes + +| Code | Description | +|------|-------------| +| u1 | Unauthorized Access | +| u2 | Invalid Transfer Amount | +| u3 | Transfer Execution Failed | +| u4 | Insufficient Token Balance | +| u5 | Invalid Recipient Address | +| u6 | Batch Size Exceeded | +| u7 | Address Blacklisted | +| u8 | Transfer Rate Limit Exceeded | +| u9 | Contract Paused | +| u10 | Invalid Memo Length | +| u11 | Duplicate Transaction | +| u12 | Token Recovery Failed | + + +## Contributing + +Contributions are welcome! Please follow these steps: +1. Fork the repository +2. Create a feature branch +3. Submit a pull request \ No newline at end of file diff --git a/batch-payment/contracts/batch-transfer.clar b/batch-payment/contracts/batch-transfer.clar index 1477dbb..641995e 100644 --- a/batch-payment/contracts/batch-transfer.clar +++ b/batch-payment/contracts/batch-transfer.clar @@ -1,5 +1,31 @@ ;; Batch Transfer Smart Contract +;; Define the SIP-010 trait directly in the contract +(define-trait sip-010-trait + ( + ;; Transfer from the caller to a new principal + (transfer (uint principal principal (optional (buff 34))) (response bool uint)) + + ;; the human-readable name of the token + (get-name () (response (string-ascii 32) uint)) + + ;; the ticker symbol, or empty if none + (get-symbol () (response (string-ascii 32) uint)) + + ;; the number of decimals used, e.g. 6 would mean 1_000_000 represents 1 token + (get-decimals () (response uint uint)) + + ;; the balance of the passed principal + (get-balance (principal) (response uint uint)) + + ;; the current total supply (which does not need to be a constant) + (get-total-supply () (response uint uint)) + + ;; an optional URI that represents metadata of this token + (get-token-uri () (response (optional (string-utf8 256)) uint)) + ) +) + ;; Error Constants - Clearly defined error codes for all possible failure scenarios (define-constant ERROR-UNAUTHORIZED-ACCESS (err u1)) (define-constant ERROR-INVALID-TRANSFER-AMOUNT (err u2)) @@ -294,12 +320,12 @@ ) (define-public (recover-stuck-tokens - (token-contract-address principal) + (token-contract ) (recovery-amount uint) ) (begin (asserts! (is-eq tx-sender CONTRACT-ADMINISTRATOR) ERROR-UNAUTHORIZED-ACCESS) - (contract-call? token-contract-address transfer + (contract-call? token-contract transfer recovery-amount (as-contract tx-sender) CONTRACT-ADMINISTRATOR