Skip to main content

On-chain Blocklock Encryption

This guide provides detailed instructions on how to implement your smart contract to request blocklock encryption using the blocklock-solidity library.

Overview

This guide demonstrates the implementation of the smart contract that uses the AbstractBlocklockReceiver interface, which is mandatory for all contracts making blocklock encryption requests. The AbstractBlocklockReceiver base smart contract contains functions for making conditional encryption requests depending on the form of payment for the request, direct funding or subscription:

  • Direct funding: _requestBlocklockPayInNative()
  • Subscription: _requestBlocklockWithSubscription()

Both functions return a requestId, which should be stored and can be used to verify the response when the decryption key is delivered through a callback from BlocklockSender.

Walkthrough

Step-1. Create a Solidity Contract

First, create a Solidity smart contract that implements the AbstractBlocklockReceiver interface to extend the blocklock encryption function:

pragma solidity ^0.8;

import {TypesLib} from "blocklock-solidity/src/libraries/TypesLib.sol";
import {AbstractBlocklockReceiver} from "blocklock-solidity/src/AbstractBlocklockReceiver.sol";

contract YourBlocklockReceiver is AbstractBlocklockReceiver {
……
constructor(address blocklockSender) AbstractBlocklockReceiver(blocklockSender) {}
……
}

The constructor takes the address of a deployed BlocklockSender(Proxy) contract, which serves as the entry point for registering encrypted payloads and triggering blocklock decryption in the dcipher network.

Refer to the Networks page for a list of supported chains and their corresponding contract addresses.

Step-2. Implement the Blocklock Request

The abstract contract AbstractBlocklockReceiver offers base functions for requesting blocklock decryption from the dcipher network with different payment options:

  • Direct funding: _requestBlocklockPayInNative()
  • Subscription: _requestBlocklockWithSubscription()

You can choose to implement one option based on your use case.

Option 1: Direct Funding

_requestBlocklockPayInNative() is the internal function provided by the AbstractBlocklockReceiver base contract. It allows you to register a new block-locked encryption request by sending native tokens directly, without relying on an active subscription from the dcipher network.

When calling _requestBlocklockPayInNative(), we need to fund the request via msg.value which should cover the estimated price for the request and decryption after the condition is met. It is recommended to add a buffer to account for fluctuations in network gas prices, thereby avoiding delays in processing the request.

Let's create a custom public wrapper function that registers a blocklock request and pays directly with native tokens by calling the internal _requestBlocklockPayInNative() method:

// When calling this function, we need to attach enough native tokens for the blocklock request fee
function createBlocklockRequestWithDirectFunding(
uint32 callbackGasLimit,
bytes calldata condition,
TypesLib.Ciphertext calldata _encryptedValue
) external payable returns (uint256, uint256) {
// create blocklock request
(uint256 requestID, uint256 requestPrice) =
_requestBlocklockPayInNative(callbackGasLimit, condition, _encryptedValue);
// store request id
requestId = requestID;
// store Ciphertext
encryptedValue = _encryptedValue;
emit BlocklockRequestCreated(requestID, callbackGasLimit, requestPrice);
return (requestID, requestPrice);
}

Option 2: Subscription-based Request

The dcipher network also provides subscription-based funding for dApp builders to create and fund their customized YourBlocklockReceiver contract with the dcipher network so that they can cover the blocklock request and decryption costs for their users. Learn more about Subscription-Based Funding.

1. Create a Subscription Account for Your Contract

The AbstractBlocklockReceiver base contract already offers subscription-based functions which will be inherited by your YourBlocklockReceiver contract. In this case, it is createSubscriptionAndFundNative() as shown below:

function createSubscriptionAndFundNative() external payable onlyOwner {
subscriptionId = _subscribe();
blocklock.fundSubscriptionWithNative{value: msg.value}(subscriptionId);
}

After deploying your YourBlocklockReceiver contract, you just need to call this function to create a subscription for your customized blocklock receiver contract with a certain amount of native tokens as payment.

Once the subscription is created, a subscriptionId will be assigned and recorded in your contract, which is used to make subscription-funded requests. A subscriptionId can be shared with multiple decryption key receiver smart contracts.

2. Implement Your Function to Request Subscription-based Blocklock

_requestBlocklockWithSubscription() is the internal function provided by the AbstractBlocklockReceiver base contract to allow builders to create requests with a subscription account.

Let's create a custom public wrapper function that registers a subscription-based blocklock request by calling the internal _requestBlocklockWithSubscription() method:

function createBlocklockRequestWithSubscription(
uint32 callbackGasLimit,
bytes calldata condition,
TypesLib.Ciphertext calldata _encryptedValue
) external returns (uint256) {
// create blocklock request
uint256 _requestId = _requestBlocklockWithSubscription(callbackGasLimit, condition, _encryptedValue);
// store request id
requestId = _requestId;
// store Ciphertext
encryptedValue = _encryptedValue;
return requestId;
}