UNPKG

blocklock-js

Version:

A library for encrypting and decrypting data for the future

237 lines (155 loc) 9.98 kB
## 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](https://github.com/randa-mu/blocklock-solidity.git) 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](https://filfox.info/en/address/0x34092470CC59A097d770523931E3bC179370B44b) | | Filecoin Calibration Testnet | [0xF00aB3B64c81b6Ce51f8220EB2bFaa2D469cf702](https://calibration.filfox.info/en/address/0xF00aB3B64c81b6Ce51f8220EB2bFaa2D469cf702) | | Base Sepolia | [0x82Fed730CbdeC5A2D8724F2e3b316a70A565e27e](https://sepolia.basescan.org/address/0x82Fed730CbdeC5A2D8724F2e3b316a70A565e27e) | | Polygon PoS | [0x82Fed730CbdeC5A2D8724F2e3b316a70A565e27e](https://polygonscan.com/address/0x82Fed730CbdeC5A2D8724F2e3b316a70A565e27e) | | Optimism Sepolia | [0xd22302849a87d5B00f13e504581BC086300DA080](https://sepolia-optimism.etherscan.io/address/0xd22302849a87d5B00f13e504581BC086300DA080) | | Arbitrum Sepolia | [0xd22302849a87d5B00f13e504581BC086300DA080](https://sepolia.arbiscan.io/address/0xd22302849a87d5B00f13e504581BC086300DA080) | | Avalanche (C-Chain) Testnet | [0xd22302849a87d5B00f13e504581BC086300DA080](https://testnet.snowtrace.io/address/0xd22302849a87d5B00f13e504581BC086300DA080) | | Sei Testnet | [0xd22302849a87d5B00f13e504581BC086300DA080](https://seitrace.com/address/0xd22302849a87d5B00f13e504581BC086300DA080?chain=atlantic-2) | **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](https://github.com/randa-mu/blocklock-solidity). ### Installation To install the library, install the latest version using: ```sh npm install blocklock-js ``` ### Usage Example #### Prerequisites * [ethers](https://www.npmjs.com/package/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: ```bash RPC_URL=https://your-rpc-url PRIVATE_KEY=your_private_key ``` 2. Install dependencies: ```bash 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](https://github.com/randa-mu/blocklock-solidity/blob/main/src/mocks/MockBlocklockReceiver.sol). This script shows how to encrypt data with a future block as a condition ```ts 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. ```ts 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); ``` 2. 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. ```ts const callbackGasLimit = 500_000; const requestPrice = await blocklock.calculateRequestPriceNative(callbackGasLimit); await mockBlocklockReceiver.createTimelockRequestWithDirectFunding( callbackGasLimit, conditionBytes, encodeCiphertextToSolidity(ciphertext), {value: requestPrice} ); ``` 3. 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](./documentation/images/webpack-require.png) 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. ```ts 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: ```javascript 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](LICENSE). ### 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.