zama/gateway-decrypt
Relayer SDK & Gateway Decrypt
zamatechnical-doc👥 Communityconfidence mediumhealth 100%
v1.0.0·Updated 4/5/2026
Use this skill when building frontend TypeScript apps that interact with fhEVM contracts, or when writing Hardhat tests for confidential contracts.
Relayer SDK — Installation & Setup
npm install @zama-fhe/relayer-sdk
import { createInstance, SepoliaConfig } from "@zama-fhe/relayer-sdk";
const instance = await createInstance(SepoliaConfig);
Available configs: SepoliaConfig, MainnetConfig.
Encrypt User Input
const encrypted = await instance
.createEncryptedInput(contractAddress, userAddress)
.add64(transferAmount) // match Solidity type: add8 / add16 / add32 / add64 / add128 / add256
.encrypt();
// Pass to contract:
await contract.myFunction(encrypted.handles[0], encrypted.inputProof);
Multiple values in one input:
const enc = await instance
.createEncryptedInput(contractAddress, userAddress)
.add64(amount)
.addBool(isActive)
.encrypt();
// enc.handles[0] → amount handle
// enc.handles[1] → isActive handle
await contract.myFunction(enc.handles[0], enc.handles[1], enc.inputProof);
Type mapping (Solidity → SDK method):
| Solidity type | SDK method |
|---|---|
externalEuint8 | .add8(value) |
externalEuint16 | .add16(value) |
externalEuint32 | .add32(value) |
externalEuint64 | .add64(value) |
externalEuint128 | .add128(value) |
externalEuint256 | .add256(value) |
externalEbool | .addBool(value) |
externalEaddress | .addAddress(value) |
User Decrypt
The user signs a decryption request — only they can see the result.
// Get handle from contract
const handle = await contract.getBalance(userAddress);
// Decrypt — requires user's ethers.js signer
const clearValue = await instance.userDecrypt(
handle,
contractAddress,
signer // ethers.js Signer
);
console.log("Balance:", clearValue); // bigint
Public Decrypt (after makePubliclyDecryptable)
Used for auction resolution, voting reveals, etc.
const results = await instance.publicDecrypt([encryptedHandle]);
// results.abiEncodedClearValues — pass to resolveAuction() etc.
// results.decryptionProof — verification proof
await contract.resolveAuction(results.abiEncodedClearValues, results.decryptionProof);
Hardhat Testing
import { ethers, fhevm } from "hardhat";
import { FhevmType } from "@fhevm/hardhat-plugin";
it("test encrypted counter", async function () {
const [alice] = await ethers.getSigners();
const contract = await ethers.deployContract("FHECounter");
const address = await contract.getAddress();
// Encrypt input
const enc = await fhevm
.createEncryptedInput(address, alice.address)
.add32(5)
.encrypt();
await contract.connect(alice).increment(enc.handles[0], enc.inputProof);
// Decrypt and verify
const handle = await contract.getCount();
const value = await fhevm.userDecryptEuint(FhevmType.euint32, handle, address, alice);
expect(value).to.equal(5n);
});
userDecryptEuint type mapping:
| Solidity type | FhevmType |
|---|---|
euint8 | FhevmType.euint8 |
euint16 | FhevmType.euint16 |
euint32 | FhevmType.euint32 |
euint64 | FhevmType.euint64 |
euint128 | FhevmType.euint128 |
euint256 | FhevmType.euint256 |
Public decrypt in tests:
const results = await fhevm.publicDecrypt([encryptedHandle]);
await contract.resolveAuction(results.abiEncodedClearValues, results.decryptionProof);
Hardhat config (hardhat.config.ts):
import "@fhevm/hardhat-plugin";
const config: HardhatUserConfig = {
solidity: "0.8.27",
networks: {
sepolia: {
url: process.env.SEPOLIA_RPC_URL,
accounts: [process.env.PRIVATE_KEY!],
},
},
};