{"id":30117324,"url":"https://github.com/distributed-lab/spv-contracts","last_synced_at":"2025-08-10T10:41:26.398Z","repository":{"id":298732924,"uuid":"962667357","full_name":"distributed-lab/spv-contracts","owner":"distributed-lab","description":"SPV Contract: Bitcoin Light Client on EVM","archived":false,"fork":false,"pushed_at":"2025-08-07T12:16:56.000Z","size":5815,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-07T13:04:02.257Z","etag":null,"topics":["bitcoin","evm","smart-contracts","spv"],"latest_commit_sha":null,"homepage":"https://distributed-lab.github.io/spv-contracts/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/distributed-lab.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-08T13:51:41.000Z","updated_at":"2025-07-29T12:19:28.000Z","dependencies_parsed_at":"2025-07-29T13:21:55.246Z","dependency_job_id":"118550e3-689f-4ab7-b25c-c844dc02c1c4","html_url":"https://github.com/distributed-lab/spv-contracts","commit_stats":null,"previous_names":["distributed-lab/spv-contracts"],"tags_count":0,"template":false,"template_full_name":"dl-solarity/hardhat-template","purl":"pkg:github/distributed-lab/spv-contracts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/distributed-lab%2Fspv-contracts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/distributed-lab%2Fspv-contracts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/distributed-lab%2Fspv-contracts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/distributed-lab%2Fspv-contracts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/distributed-lab","download_url":"https://codeload.github.com/distributed-lab/spv-contracts/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/distributed-lab%2Fspv-contracts/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269712876,"owners_count":24463215,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bitcoin","evm","smart-contracts","spv"],"created_at":"2025-08-10T10:41:22.054Z","updated_at":"2025-08-10T10:41:26.367Z","avatar_url":"https://github.com/distributed-lab.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🛡️ SPV Contract: Bitcoin Light Client on EVM\nWelcome to the **SPV Contract**, a robust and efficient Solidity implementation for verifying Bitcoin block headers directly on an EVM-compatible blockchain. This contract empowers dApps to act as a **Simplified Payment Verification (SPV)** client, allowing them to validate the existence and inclusion of Bitcoin transactions without needing to run a full Bitcoin node.\n\n# ✨ Why this SPV Contract?\nIn the decentralized world, connecting different blockchain ecosystems securely is paramount. This SPV contract provides a trust-minimized bridge, enabling smart contracts on EVM chains to cryptographically verify the state of the Bitcoin blockchain. This opens doors for exciting use cases like:\n- **Cross-chain bridges** for Bitcoin-backed assets\n- **Light clients** for dApps that need to confirm Bitcoin transaction finality\n- **Decentralized custodianship** solutions\n- **Oracle services** for Bitcoin data on EVM\n\n# 🚀 Key Features\n- **Block Header Submission:** Efficiently add individual or batches of Bitcoin block headers to the contract.\n- **Mainchain Tracking:** Automatically identifies and updates the \"main\" Bitcoin chain based on accumulated work.\n- **Block Validation:** Verifies block headers against Bitcoin's consensus rules, including:\n  - Proof-of-Work (target difficulty)\n  - Block time validity (median time past)\n  - Chain continuity (previous block hash)\n- **Block Information Retrieval:** Query detailed information about any stored block, such as:\n  - Its Merkle root\n  - Its height\n  - Its inclusion status in the mainchain\n  - Its cumulative work (difficulty)\n  - Its confirmation count relative to the mainchain head\n- **Difficulty Adjustment:** Integrates Bitcoin's precise difficulty adjustment algorithm to accurately calculate current and future targets.\n\n# ⚙️ How it Works (Under the Hood)\nThe contract operates by receiving raw Bitcoin block headers, which are then parsed and validated against Bitcoin's strict consensus rules.\n\n1. **Header Parsing:** Raw 80-byte Bitcoin block headers are parsed into a structured *BlockHeaderData* format. This involves handling Bitcoin's unique little-endian byte ordering.\n2. **Double SHA256 Hashing:** Each block header is double SHA256 hashed to derive its unique block hash, which is then byte-reversed for standard representation.\n3. **Proof-of-Work Verification:** The calculated block hash is checked against the current network difficulty target (derived from the *bits* field in the header).\n4. **Chain Extension \u0026 Reorganization:** New blocks are added to a data structure that allows for tracking multiple chains. When a new block extends a chain with higher cumulative work, the *mainchainHead* is updated, reflecting potential chain reorganizations.\n5. **Difficulty Adjustment:** Every 2016 blocks, the contract calculates a new difficulty target based on the time taken to mine the preceding epoch. This ensures the 10-minute average block time is maintained.\n\n# 📊 Flow Diagrams\nThese diagrams outline the step-by-step process for adding block headers to the SPV Contract.\n\n### `addBlockHeader(bytes calldata blockHeaderRaw_)` Sequence Diagram\n\n```mermaid\nsequenceDiagram\n  participant Caller\n  participant SPVContract\n  participant BlockHeaderLib\n  participant TargetsHelperLib\n\n  Caller-\u003e\u003eSPVContract: addBlockHeader(blockHeaderRaw)\n  activate SPVContract\n\n  SPVContract-\u003e\u003eBlockHeaderLib: 1. Parse blockHeaderRaw_ (parseBlockHeaderData)\n  activate BlockHeaderLib\n  BlockHeaderLib--\u003e\u003eSPVContract: 1.1. Check length (80 bytes) \u0026 LE to BE\n  alt Length Invalid\n      BlockHeaderLib--xSPVContract: Error: InvalidBlockHeaderDataLength\n      SPVContract--xCaller: Revert\n  end\n  BlockHeaderLib--\u003e\u003eSPVContract: 1.2. Return BlockHeaderData \u0026 blockHash\n  deactivate BlockHeaderLib\n\n  SPVContract-\u003e\u003eSPVContract: 1.3. Check blockHash existence\n  alt BlockHash Exists\n      SPVContract--xCaller: Error: BlockAlreadyExists\n  end\n\n  SPVContract-\u003e\u003eSPVContract: 2. Check prevBlockHash existence\n  alt Prev Block Missing\n      SPVContract--xCaller: Error: PrevBlockDoesNotExist\n  end\n\n  SPVContract-\u003e\u003eSPVContract: 3. Calculate newBlockHeight = prevBlockHeight + 1\n\n  SPVContract-\u003e\u003eSPVContract: 4. Get Current Target\n  SPVContract-\u003e\u003eSPVContract: 4.1. Get target from prevBlockBits\n  SPVContract-\u003e\u003eTargetsHelperLib: Check if newBlockHeight is Recalculation Block (isTargetAdjustmentBlock)\n  activate TargetsHelperLib\n  alt Recalculation Block\n      SPVContract-\u003e\u003eSPVContract: Recalculate target \u0026 Save lastEpochCumulativeWork\n      TargetsHelperLib--\u003e\u003eSPVContract: Return newNetworkTarget\n  else Not Recalculation Block\n      TargetsHelperLib--\u003e\u003eSPVContract: Use prevBlockTarget as networkTarget\n  end\n  deactivate TargetsHelperLib\n\n  SPVContract-\u003e\u003eSPVContract: 5. Check Block Rules\n  SPVContract-\u003e\u003eTargetsHelperLib: 5.1. Check Header Target == Contract Target\n  activate TargetsHelperLib\n  TargetsHelperLib--\u003e\u003eSPVContract: Result\n  deactivate TargetsHelperLib\n  alt Invalid Target\n      SPVContract--xCaller: Error: InvalidTarget\n  end\n\n  SPVContract-\u003e\u003eSPVContract: 5.2. Check newBlockHash \u003c= networkTarget (PoW)\n  alt Invalid Block Hash\n      SPVContract--xCaller: Error: InvalidBlockHash\n  end\n\n  SPVContract-\u003e\u003eSPVContract: 5.3. Check newBlockTime \u003e= medianTime\n  alt Invalid Block Time\n      SPVContract--xCaller: Error: InvalidBlockTime\n  end\n\n  SPVContract-\u003e\u003eSPVContract: 6. Add Block To Chain\n  SPVContract-\u003e\u003eSPVContract: 6.1. Save newBlockHeader \u0026 newBlockHash to Storage\n\n  SPVContract-\u003e\u003eSPVContract: 6.2. Update Mainchain\n  alt 6.2.1. prevBlockHash == mainchainHead?\n      SPVContract-\u003e\u003eSPVContract: Move mainchainHead to newBlockHash\n  else\n      SPVContract-\u003e\u003eSPVContract: 6.2.2. Calculate New Block \u0026 Current Head Cumulative Work\n      SPVContract-\u003e\u003eSPVContract: 6.2.3. newBlock Cumulative Work \u003e Current Head?\n      alt New Block Has Higher Work\n          SPVContract-\u003e\u003eSPVContract: Set New Block as mainchainHead\n          SPVContract-\u003e\u003eSPVContract: Recursively update mainchain path backwards (do-while loop)\n      end\n  end\n\n  SPVContract-\u003e\u003eSPVContract: Emit BlockHeaderAdded\n  SPVContract--\u003e\u003eCaller: Transaction Complete\n  deactivate SPVContract\n```\n\n### `addBlockHeaderBatch(bytes[] calldata blockHeaderRawArr_)` Sequence Diagram\n\nThis function processes multiple block headers in a single transaction, iterating through the array and validating each sequentially.\n\n```mermaid\nsequenceDiagram\n  participant Caller\n  participant SPVContract\n  participant BlockHeaderLib\n  participant TargetsHelperLib\n\n  Caller-\u003e\u003eSPVContract: addBlockHeaderBatch(blockHeaderRawArray_)\n  activate SPVContract\n\n  SPVContract-\u003e\u003eSPVContract: Check if Header Array is Empty\n  alt Array Empty\n      SPVContract--xCaller: Error: EmptyBlockHeaderArray\n  end\n\n  SPVContract-\u003e\u003eBlockHeaderLib: 1. Parse Block Headers Array (_parseBlockHeadersRaw)\n  activate BlockHeaderLib\n  BlockHeaderLib--\u003e\u003eSPVContract: Returns BlockHeaderData[] \u0026 bytes32[]\n  deactivate BlockHeaderLib\n\n  loop For each blockHeader in parsed array (from i=0 to length-1)\n      SPVContract-\u003e\u003eSPVContract: 2. Check prevBlockHash for current block\n      alt First block in batch\n          SPVContract-\u003e\u003eSPVContract: Check prevBlockHash existence (like addBlockHeader)\n          alt Prev Block Missing\n              SPVContract--xCaller: Error: PrevBlockDoesNotExist\n          end\n      else Subsequent blocks\n          SPVContract-\u003e\u003eSPVContract: Check prevBlockHash == blockHash of (i-1)th block\n          alt Order Invalid\n              SPVContract--xCaller: Error: InvalidBlockHeadersOrder\n          end\n      end\n\n      SPVContract-\u003e\u003eSPVContract: 3. Calculate currentBlockHeight = prevBlockHeight + 1\n\n      SPVContract-\u003e\u003eSPVContract: 4. Get Current Target (like addBlockHeader)\n      SPVContract-\u003e\u003eTargetsHelperLib: Check for Recalculation Block \u0026 Recalculate if needed\n      activate TargetsHelperLib\n      TargetsHelperLib--\u003e\u003eSPVContract: Return networkTarget\n      deactivate TargetsHelperLib\n\n      SPVContract-\u003e\u003eSPVContract: 5. Get Median Time\n      alt 5.1. Num blocks added \u003c 12\n          SPVContract-\u003e\u003eSPVContract: Use _getStorageMedianTime (like addBlockHeader)\n      else 5.2. Num blocks added \u003e= 12\n          SPVContract-\u003e\u003eSPVContract: Use _getMemoryMedianTime (from batch data)\n      end\n\n      SPVContract-\u003e\u003eSPVContract: 6. Validate Block Rules (_validateBlockRules)\n      alt Validation Fails\n          SPVContract--xCaller: Error: InvalidTarget / InvalidBlockHash / InvalidBlockTime\n      end\n\n      SPVContract-\u003e\u003eSPVContract: 7. Add Block To Chain (_addBlock)\n      SPVContract-\u003e\u003eSPVContract: Emit BlockHeaderAdded\n  end\n\n  SPVContract--\u003e\u003eCaller: Transaction Complete\n  deactivate SPVContract\n```\n\n\n# 📦 Contract Components\nThe solution is primarily composed of the main SPV contract and two essential helper libraries that manage the intricacies of Bitcoin's block structure and difficulty adjustments.\n\n## SPVContract\nThis is the central contract that users will interact with. It serves as the primary interface for managing Bitcoin block headers on the EVM. It handles the core logic for adding and validating blocks, tracking the main Bitcoin chain, and providing querying functionalities. All custom errors and events related to the SPV operations are defined here, ensuring clear feedback and transparency during contract execution.\n\n## BlockHeader Library\nThis is a pure utility library specifically designed to handle the low-level details of Bitcoin block headers. It's responsible for the precise parsing of raw 80-byte Bitcoin block header data into a structured format that Solidity can easily work with. Crucially, it manages the byte order conversions, translating Bitcoin's little-endian format to Solidity's big-endian, and vice-versa. It also provides the essential function for calculating the double SHA256 hash of a block header, which is fundamental for verifying Proof-of-Work.\n\n## TargetsHelper Library\nThis library encapsulates all the complex mathematical and logical operations related to Bitcoin's difficulty targets. It provides functions to accurately calculate new difficulty targets based on elapsed time between blocks, ensuring the contract adheres to Bitcoin's dynamic difficulty adjustment rules. Additionally, it offers utilities for converting between the compact \"bits\" format (as found in Bitcoin block headers) and the full 256-bit target value, and it calculates the cumulative work associated with a given block or epoch, which is vital for determining the most valid chain.\n\n# 💻 Dev Info\n## Compilation\nTo compile the contracts, use the next script:\n\n```bash\nnpm run compile\n```\n\n## Test\nTo run the tests, execute the following command:\n\n```bash\nnpm run test\n```\n\nOr to see the coverage, run:\n\n```bash\nnpm run coverage\n```\n\n## Local deployment\nTo deploy the contracts locally, run the following commands (in the different terminals):\n\n```bash\nnpm run private-network\nnpm run deploy-localhost\n```\n\n## Bindings\nThe command to generate the bindings is as follows:\n\n```bash\nnpm run generate-types\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdistributed-lab%2Fspv-contracts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdistributed-lab%2Fspv-contracts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdistributed-lab%2Fspv-contracts/lists"}