Skip to content

A Typescript library for encrypting data that can't be decrypted until a future chain height has been reached. Compatible with a variety of blockchains.

License

Notifications You must be signed in to change notification settings

randa-mu/blocklock-js

Repository files navigation

blocklock-js

blocklock-js is a TypeScript library that simplifies generating encrypted data off-chain for use with the dcrypt network. It allows developers to securely encrypt data tied to a user-defined condition, e.g., a future block height. This encrypted payload can then be referenced in on-chain timelock encryption requests via smart contracts. Once the specified block is mined, the decryption key is automatically delivered to the smart contract via a callback, enabling conditional data access on-chain.

The library also enables developers to track the status of a conditional encryption request.

On-Chain Integration

Solidity interfaces and associated documentation can be found in the blocklock-solidity repository.

Smart Contract Addresses

BlocklockSender Proxy

A lightweight proxy contract that enables upgradeability for the BlocklockSender implementation. It delegates all calls to the underlying implementation and serves as the primary interface for user interaction.

Network Address
Filecoin Mainnet 0x34092470CC59A097d770523931E3bC179370B44b
Filecoin Calibration Testnet 0xF00aB3B64c81b6Ce51f8220EB2bFaa2D469cf702
Base Sepolia 0x82Fed730CbdeC5A2D8724F2e3b316a70A565e27e
Polygon PoS 0x82Fed730CbdeC5A2D8724F2e3b316a70A565e27e
Optimism Sepolia 0xd22302849a87d5B00f13e504581BC086300DA080
Arbitrum Sepolia 0xd22302849a87d5B00f13e504581BC086300DA080
Avalanche (C-Chain) Testnet 0xd22302849a87d5B00f13e504581BC086300DA080
Sei Testnet 0xd22302849a87d5B00f13e504581BC086300DA080

Other contract addresses You should only need the BlocklockSender proxy above, but a full list of contract addresses can be found in the solidity repo's README.

Installation

To install the library, install the latest version using:

npm install blocklock-js

Usage Example

Prerequisites

  • ethers for wallet setup and message encoding.
  • Node.js v22+

Setup

  1. Create a .env file or set the following environment variables, e.g., for Filecoin mainnet:
RPC_URL=https://your-rpc-url
PRIVATE_KEY=your_private_key
  1. Install dependencies:
npm install

Usage example

This example demonstrates encrypting a uint256 value and using the Ciphertext and condition bytes in a user smart contract that implements the createTimelockRequestWithDirectFunding function to create a timelock encryption request on-chain. An example use case is a sealed-bid auction where bid amounts are encrypted and only decrypted at the auction ending block number.

The example user smart contract source code can be found here.

This script shows how to encrypt data with a future block as a condition

import { createProvider, Blocklock, encodeCiphertextToSolidity, encodeCondition } from "blocklock-js"
import { Wallet, Provider, NonceManager, ethers, getBytes } from "ethers"
import { MockBlocklockReceiver__factory } from "../types"; // Users' solidity contract TypeScript binding

async function main() {
  const rpc = createProvider(process.env.RPC_URL || "")
  const wallet = new NonceManager(new Wallet(process.env.PRIVATE_KEY || "", rpc))
  // Create a Blocklock instance for the Filecoin mainnet
  const blocklock = Blocklock.createFilecoinMainnet(wallet)

  // Value to encrypt (4 ETH as uint256)
  const msg = ethers.utils.parseEther("4");
  const plaintext = Buffer.from(msg)
  const currentBlock = await rpc.getBlockNumber()
  const targetBlock = BigInt(currentBlock + 5)

  console.log(`Encrypting for block ${targetBlock} (current: ${currentBlock})`)
  const ciphertext = await blocklock.encrypt(plaintext, targetBlock)

  // User contract
  const mockBlocklockReceiver = MockBlocklockReceiver__factory.connect("user blocklcok receiver contract address", wallet);

  // Generate the timelock encryption condition bytes string
  const conditionBytes = encodeCondition(targetBlock);
  // Amount of gas users callback function is expected to consume, i.e., the function that will be called with the decryption key
  const callbackGasLimit = 500_000;

  // Compute the request price to pay for the direct funding request
  const requestPrice = await blocklock.calculateRequestPriceNative(callbackGasLimit);

  // Call `createTimelockRequestWithDirectFunding` on the user's contract 
  // for a direct or ad hoc funding request with the following parameters:
  // uint32 callbackGasLimit, 
  // bytes calldata condition and,
  // TypesLib.Ciphertext calldata encryptedData
  const tx = await mockBlocklockReceiver
    .connect(wallet)
    .createTimelockRequestWithDirectFunding(callbackGasLimit, conditionBytes, encodeCiphertextToSolidity(ciphertext), 
    {value: requestPrice});
  // Note: for subscription-based funding, 
  // use createTimelockRequestWithSubscription instead.

  const receipt = await tx.wait(1);

  if (!receipt) {
    throw new Error("Transaction has not been mined");
  }

  console.log("Timelock request created!");
}

main().catch((error) => {
  console.error("Error:", error);
});

How It Works

  1. Encoding and Encryption:
  • Use blocklock-js to:

    • Create a provider and signer using Wallet and NonceManager.

    • Initialize a Blocklock instance for your target network (e.g., Filecoin mainnet).

    • Prepare the message to encrypt (e.g., 4 ETH encoded as a uint256).

    • Select a future block height as the decryption condition.

    const msg = ethers.utils.parseEther("4");
    const plaintext = Buffer.from(msg);
    const currentBlock = await rpc.getBlockNumber();
    const targetBlock = BigInt(currentBlock + 5);
    
    const ciphertext = await blocklock.encrypt(plaintext, targetBlock);
    const conditionBytes = encodeCondition(targetBlock);
  1. On-Chain Interaction:

    • Use your own smart contract (e.g., MockBlocklockReceiver) to create a timelock encryption request.

    • Call createTimelockRequestWithDirectFunding, which:

      • Stores the encrypted data (Ciphertext) and decryption condition on-chain via the BlocklockSender contract.

      • Funds the request by paying the requestPrice via the transaction.

      • Generates a unique request ID.Call the appropriate function in the user contract with the encrypted data and the chain height used during off-chain encryption.

      const callbackGasLimit = 500_000;
      
      const requestPrice = await blocklock.calculateRequestPriceNative(callbackGasLimit);
      
      await mockBlocklockReceiver.createTimelockRequestWithDirectFunding(
        callbackGasLimit,
        conditionBytes,
        encodeCiphertextToSolidity(ciphertext),
        {value: requestPrice}
      );
  2. Automatic Decryption and Callback:

  • Once the specified condition is met (e.g., target block is mined):

    • The BlocklockSender contract receives the decryption key from the dcipher Threshold Network.

    • It automatically triggers a callback to the users contract with the key.

    • The users contract can optionally call decrypt to recover the original message on-chain.

Common Errors

Webpack Configuration

When using the library in web applications, there might be webpack errors such as the one displayed below:

Webpack require error

To resolve this error, one solution is to update your next.config.ts configuration file if you use one with similar configurations for webpack below.

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* config options here */
   webpack: (config) => {
     config.externals.push({
       'node:crypto': 'crypto',
     });
     return config;
  },
};

export default nextConfig;

In the library's Webpack configuration, the following setting is used:

externals: {
    'node:crypto': 'commonjs crypto',
}

This configuration tells the bundler not to include the crypto module in the final bundle. Instead, it treats 'node:crypto' as an external dependency and expects it to be available in the runtime environment. 'commonjs crypto' ensures that the module is required using the CommonJS format, making it compatible with Node.js. This ensures compatibility with environments where the crypto module is natively available.

Licensing

This library is licensed under the MIT License which can be accessed here.

Contributing

Contributions are welcome! If you find a bug, have a feature request, or want to improve the code, feel free to open an issue or submit a pull request.

Acknowledgements

Special thanks to the Filecoin Foundation for supporting the development of this library.

About

A Typescript library for encrypting data that can't be decrypted until a future chain height has been reached. Compatible with a variety of blockchains.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •