Create Sender and Recipient smart contracts adhering to the structure shown below
Set mailbox and CCIP read ISM to the ISM address in references in INCO recipient during deployment
Now deploy sender contract to any chain of choice and configure the mailbox to the mailbox of that chain and the recipient to the contract you just deployed on INCO
Now send the necessary Ciphertext to the read server using the URL and request format below
Next send the same calldata to the function which commits Ciphertext hash to inco.
Now the relayer will query the ciphertext corresponding to the hash during relay time and finally process both ciphertext and hash by calling the handlewithCiphertext function on the recipient.
constaxios=require('axios');// Function to send POST request with ciphertextconstpostCiphertext=async (ciphertext) => {try {constresponse=awaitaxios.post('https://hyperlane-ccip.vercel.app/token', { ciphertext: ciphertext });returnresponse.data;//returns hash that can be used to commit to Contract } catch (error) {console.error('Error posting ciphertext:', error);throw error; }};// Function to send GET request with hashconstgetCiphertextByHash=async (hash) => {try {constresponse=awaitaxios.get('https://hyperlane-ccip.vercel.app/token', { params: { hash: hash } });returnresponse.data; } catch (error) {console.error('Error getting ciphertext by hash:', error);throw error; }};// Example usageconstmain=async () => {//call API and commit hash to contract};// Run the main functionmain();
The post request returns the Hash of the Ciphertext which has to be dispatched from your contract on any EVM chain to the contract on INCO.
now the hash and corresponding Ciphertext is available in the CCIP read server and can be retrieved by relayer.
Formatting Ciphertext before uploading to the Server.
The fhevmjs library returns the ciphertext in the form of uint8 array. However we will be converting it to bytes and uploading it to the server.
use the following format technique to convert the ciphertext to bytes and remove additional padding provided by ethers.
constCiphertext=fhevmInstance.alice.encrypt8(1);constbytes_ciphertext=AbiCoder.defaultAbiCoder().encode(["bytes"], [Ciphertext]);let padded_ciphertext:string="0x"+bytes_ciphertext.slice(130,33146);//commit to CCIP-server //Dispatch transaction to origin chain
This formatting is important and allows correct dispatch and verification of ciphertext.
Smart Contract Integration
We will have to configure our smart contract logic on both origin chain and Inco to make sure this setup works. The guide and template code for integration is shown below
Origin Chain Contract
The integration to smart contracts on origin chain will be same as dispatch function used in Hyperlane. The origin chain can hold the hash of the ciphertext. while the metadata is made available on Inco for the corresponding hash.
We can also retrieve the hash of the ciphertext from the function for verification purpose.
You can refer the following template code.
Additional metadata can be sent alongside hash, but hash must be the first variable while encoding according to ABI followed by arbitrary data of your choice.
Inco gentry smart contract
The smart contract on Inco has slight modifications needed to process our message
We will have to define the ISM to be CCIP-read ISM by specifying the address of ISM in the reference section by calling the setInterchainSecurityModule in the contract.
Additional handler function called handleWithCiphertext is called by ISM to pass the Ciphertext
Use the following template code
Recipient.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity ^0.8.19;import {IMailbox} from"@hyperlane-xyz/core/contracts/interfaces/IMailbox.sol";import {IPostDispatchHook} from"@hyperlane-xyz/core/contracts/interfaces/hooks/IPostDispatchHook.sol";import {IInterchainSecurityModule} from"@hyperlane-xyz/core/contracts/interfaces/IInterchainSecurityModule.sol";import {Address} from"@openzeppelin/contracts/utils/Address.sol";import {OwnableUpgradeable} from"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";contractCipherTextProcessorisOwnableUpgradeable{ IInterchainSecurityModule public interchainSecurityModule;functionsetInterchainSecurityModule(address_module) public { interchainSecurityModule =IInterchainSecurityModule(_module); }eventhandled (bytes32hash);address mailbox; // address of mailbox contractaddresspublic ISM;constructor(address_mailbox,address_interchainSecurityModule) { mailbox = _mailbox; ISM=_interchainSecurityModule;//Sets CCIP read ISM as ISM to be usedsetInterchainSecurityModule(_interchainSecurityModule); }// Modifier so that only mailbox can call particular functionsmodifieronlyMailbox() {require( msg.sender == mailbox,"Only mailbox can call this function !!!" ); _; }modifieronlyISM() {require( msg.sender == ISM,"Only mailbox can call this function !!!" ); _; }//no-op function but called by mailbox pos verification so keepfunctionhandle( uint32_origin,bytes32_sender,bytesmemory_body)externalonlyMailbox { (bytes32 committedHash)= abi.decode(_body, (bytes32));emithandled(committedHash); }//*************************************************************************// Necessary function to be implemented with no changes as it is called by ISM with metadatafunctionhandleWithCiphertext( uint32_origin,bytes32_sender,bytesmemory_message)externalonlyISM { (bytesmemory message,bytesmemory ciphertext)=abi.decode(_message,(bytes,bytes)); (bytes32 committedHash)= abi.decode(message, (bytes32));//rest of function logic }//**************************************************************************}
This will allow the ISM to write the ciphertext and hash on your recipient.
In case the application requires using both Ciphertext services and general message passing, refer the Routing ISM section.