Integrating Shutter API and Shutter SDK for dApp Development
This document is designed to be LLM friendly, work with current web standards, enabling fast and accurate responses for seamless communication with AI models.
This guide is designed to teach you (or a generative AI) how to integrate the Shutter API and Shutter SDK into your decentralized application (dApp). The Shutter protocol provides a secure and decentralized commit-and-reveal mechanism that leverages threshold encryption, distributed cryptographic operations, and multi-party computation (MPC) techniques.
Disclaimer:
The Shutter API is in early development stage. Do not use it to manage high-value assets until the network is fully matured and decentralized. Use at your own risk.
Overview of Shutter
The Shutter system facilitates secure commit-and-reveal workflows using:
- Threshold Encryption: No single entity can compromise the encrypted data.
- Distributed Keypers: Nodes that monitor on-chain events and handle decryption key releases.
- API Endpoints & SDK: Simplified interfaces for dApp developers to integrate encryption and decryption functionalities.
The system consists of:
- Registry Contract: Where identities and time-based decryption triggers are registered.
- Keypers: Responsible for cryptographic operations and decentralized key management.
- Shutter API: Provides endpoints for encryption and decryption operations.
- Shutter SDK: A TypeScript library that abstracts encryption/decryption operations, easing integration in modern dApps.
System Architecture
Key components include:
- Client dApp: Your application that calls the Shutter API or uses the SDK.
- Registry Contract: Deployed on-chain (e.g., Chiado or Gnosis) that holds identity and trigger information.
- Shutter API: Endpoints for identity registration, encryption data retrieval, key retrieval, and decryption.
- Keypers: Nodes that perform threshold cryptography.
Prerequisites and Setup
Before integrating Shutter into your dApp, ensure you have:
-
API Endpoint Access
For example:
-
Chiado (testnet)
- API Base URL: https://shutter-api.chiado.staging.shutter.network/api/
- Registry Contract Address: 0x2693a4Fb363AdD4356e6b80Ac5A27fF05FeA6D9F
- API Address: 0xb9C303443c9af84777e60D5C987AbF0c43844918
-
Mainnet (Gnosis)
- API Base URL: https://shutter-api.shutter.network/api/
- Registry Contract Address: 0x694e5de9345d39C148DA90e6939A3fd2142267D9
- API Address: 0x228DefCF37Da29475F0EE2B9E4dfAeDc3b0746bc
-
-
Familiarity with RESTful APIs and Ethereum-based dApp development.
-
Installation of Node.js and npm (if you plan to use the TypeScript SDK).
Integrating the Shutter API
The Shutter API exposes several endpoints to handle the full lifecycle of encryption and decryption in your dApp. The key endpoints are:
Registering an Identity with a Decryption Trigger
This is your first step. Register your identity along with a time-based decryption trigger (a Unix timestamp). The API computes your identity based on your API account address unless you register directly with the registry contract.
Endpoint: /register_identity
Method: POST
Example Request
curl -X POST https://`API_BASE_URL`/register_identity
-H "Content-Type: application/json"
-d '{
"decryptionTimestamp": 1735044061,
"identityPrefix": "0x79bc8f6b4fcb02c651d6a702b7ad965c7fca19e94a9646d21ae90c8b54c030a0"
}'
Example Response
{
"eon": 1,
"eon_key": "0x57af5437a84ef50e5ed75772c18ae38b168bb07c50cadb65fc6136604e662255",
"identity": "0x8c232eae4f957259e9d6b68301d529e9851b8642874c8f59d2bd0fb84a570c75",
"identity_prefix": "0x79bc8f6b4fcb02c651d6a702b7ad965c7fca19e94a9646d21ae90c8b54c030a0",
"tx_hash": "0x3026ad202ca611551377eef069fb6ed894eae65329ce73c56f300129694f12ba"
}
Note: Replace API_BASE_URL
with the appropriate API base URL (Chiado or Mainnet).
Retrieving Encryption Data
After registration, you need to obtain the encryption parameters that are required to encrypt your commitments.
Endpoint: /get_data_for_encryption
Method: GET
Example Request
curl -X GET "https://`API_BASE_URL`/get_data_for_encryption?address=0xb9C303443c9af84777e60D5C987AbF0c43844918&identityPrefix=0x79bc8f6b4fcb02c651d6a702b7ad965c7fca19e94a9646d21ae90c8b54c030a0"
Example Response
{
"eon": 1,
"eon_key": "0x57af5437a84ef50e5ed75772c18ae38b168bb07c50cadb65fc6136604e662255",
"identity": "0x8c232eae4f957259e9d6b68301d529e9851b8642874c8f59d2bd0fb84a570c75",
"identity_prefix": "0x79bc8f6b4fcb02c651d6a702b7ad965c7fca19e94a9646d21ae90c8b54c030a0",
"epoch_id": "0x88f2495d1240f9c5523db589996a50a4984ee7a08a8a8f4b269e4345b383310abd2dc1cd9c9c2b8718ed3f486d5242f5"
}
Retrieving the Decryption Key
Once the specified decryption timestamp has passed, you can retrieve the decryption key. This key is used later to decrypt the encrypted commitments.
Endpoint: /get_decryption_key
Method: GET
Example Request
curl -X GET "https://`API_BASE_URL`/get_decryption_key?identity=0x8c232eae4f957259e9d6b68301d529e9851b8642874c8f59d2bd0fb84a570c75"
Example Response
{
"decryption_key": "0x99a805fc26812c13041126b25e91eccf3de464d1df7a95d1edca8831a9ec02dd",
"decryption_timestamp": 1735044061,
"identity": "0x8c232eae4f957259e9d6b68301d529e9851b8642874c8f59d2bd0fb84a570c75"
}
Decrypting Commitments
With the decryption key in hand, you can now decrypt any previously encrypted commitments.
Endpoint: /decrypt_commitment
Method: GET
Example Request
curl -X GET "https://`API_BASE_URL`/decrypt_commitment?identity=0x8c232eae4f957259e9d6b68301d529e9851b8642874c8f59d2bd0fb84a570c75&encryptedCommitment=0x03b5685a460a95ba628e04b24155d6722f7c4e376a1627f714a4ae9cecd2982e005eff12ac8150b8842c29f8d5eaf4d0da0b626f762b4826d779d8969b577acb28df96cab026aa57c00cd74b07ca51e8c0c1a59933e29a728311900ebfc26c6804260914c96cb10dbd6d2ed3f6cb77788a74b5aae5f4ce6f40be53310a0524d42d5a6f03b5c1517ec097553733e228276fcdfc4b569f7ef4311a461d68819d634c"
Example Response
{
"decrypted_message": "0x706c6561736520686964652074686973206d657373616765"
}
Tip: The decrypted message is returned in hexadecimal format. Convert it to a string (e.g., using web3.js or ethers.js) to reveal the original message.
Integrating the Shutter SDK
The Shutter SDK is a TypeScript library that simplifies the encryption and decryption process. It wraps the lower-level operations of the Shutter protocol into high-level functions that you can easily integrate into your dApp.
Installation
To install the Shutter SDK in your project, run:
npm install @shutter-network/shutter-sdk
Encrypting Data (TypeScript)
Below is a sample code snippet that demonstrates how to use the SDK to encrypt data:
import { encryptData } from "@shutter-network/shutter-sdk";
import { stringToHex } from "viem";
// Define the encryption data from the API
const eonKeyHex = "0x57af5437a84ef50e5ed75772c18ae38b168bb07c50cadb65fc6136604e662255";
const identityPreimageHex = "0x8c232eae4f957259e9d6b68301d529e9851b8642874c8f59d2bd0fb84a570c75";
const message = "please hide this message";
const sigmaHex = "0x312c10b186086d502ba683cffc2ae650d53b508904b3c430df8e7d5aa336c0f5";
// Encrypt the message
const encryptedCommitment = await encryptData(message, eonKeyHex, identityPreimageHex, sigmaHex);
console.log("Encrypted Commitment:", encryptedCommitment);
Decrypting Data (TypeScript)
To decrypt an encrypted commitment, use the decrypt function provided by the SDK:
import { decrypt } from "@shutter-network/shutter-sdk";
const encryptedData = "0x03a975256b0098bc981da31762a73e50a07c79f5bf3e17c44121b9567033cedaf9e203f0300b709dec3458a88baa18963c0e503f437bff7adb31231941585ea1bb14e8ce98c7dc1471666e4b07c592cbeda30acc22f23dcb84d58d41848e72af0804d348d5c5cb65a52dc3b697ea4caae9679b97e395a30807f9657ebc85bbf2fcadaa9a458a86bffb78dde89f7626a26eb84f4781d3b6759c06629ea321a8b757";
const decryptionKey = "0x81cfcfceebfc69b3cb3fe074f4b3751e7844f6d62b3040563ccb3a2430110f259d109519c73682735f4c02651492c740";
// Decrypt the data
const decryptedData = await decrypt(encryptedData, decryptionKey);
console.log("Decrypted Message:", decryptedData);
Example in Go
For dApps or backend services written in Go, you can use the provided example to encrypt commitments. This sample leverages the shcrypto package from Shutter's repository.
package main
import (
"crypto/rand"
"encoding/hex"
"fmt"
"log"
"strings"
"github.com/shutter-network/shutter/shlib/shcrypto"
)
func main() {
// Encryption data from the Shutter API
identityHex := "0x8c232eae4f957259e9d6b68301d529e9851b8642874c8f59d2bd0fb84a570c75"
eonPublicKeyHex := "0x57af5437a84ef50e5ed75772c18ae38b168bb07c50cadb65fc6136604e662255"
message := []byte("please hide this message")
identityHex = strings.TrimPrefix(identityHex, "0x")
eonPublicKeyHex = strings.TrimPrefix(eonPublicKeyHex, "0x")
// Convert hex strings to bytes
identity, err := hex.DecodeString(identityHex)
if err != nil {
log.Fatalf("Failed to decode identity: %v", err)
}
eonPublicKeyBytes, err := hex.DecodeString(eonPublicKeyHex)
if err != nil {
log.Fatalf("Failed to decode eon public key: %v", err)
}
// Unmarshal the public key
eonPublicKey := &shcrypto.EonPublicKey{}
if err := eonPublicKey.Unmarshal(eonPublicKeyBytes); err != nil {
log.Fatalf("Failed to unmarshal EonPublicKey: %v", err)
}
// Compute the Epoch ID from the identity
epochID := shcrypto.ComputeEpochID(identity)
// Generate a random sigma value
sigma, err := shcrypto.RandomSigma(rand.Reader)
if err != nil {
log.Fatalf("Failed to generate random sigma: %v", err)
}
// Encrypt the message
encryptedCommitment := shcrypto.Encrypt(message, eonPublicKey, epochID, sigma)
// Marshal the encrypted commitment and encode to hex
encryptedCommitmentBytes := encryptedCommitment.Marshal()
encryptedCommitmentHex := "0x" + hex.EncodeToString(encryptedCommitmentBytes)
fmt.Printf("Encrypted Commitment: %s\n", encryptedCommitmentHex)
}
Building a New dApp with Shutter API
When building a new dApp with the Shutter API, follow these steps:
-
Set Up Your Environment
Ensure you have access to the appropriate API endpoints (Chiado or Mainnet) and that your project includes a web3 provider for interacting with the registry contract.
-
Register an Identity
Use the /register_identity endpoint to register a new identity and set a decryption trigger.
Tip: If you want full control over your address, consider registering directly with the registry contract.
-
Retrieve Encryption Data
Once your identity is registered, call /get_data_for_encryption to obtain the parameters needed to encrypt commitments.
-
Encrypt Commitments
Use either the provided SDK (TypeScript) or native implementations (Go, etc.) to encrypt your sensitive messages or commitments before storing or transmitting them.
-
Store and Manage Encrypted Data
Persist the encrypted commitments within your dApp (for example, on-chain, in IPFS, or a centralized database) until the decryption trigger is activated.
-
Retrieve and Decrypt Data
After the trigger time passes, use the /get_decryption_key endpoint to fetch the decryption key, and then call /decrypt_commitment or use the SDK's decrypt function to reveal the original message.
-
User Interaction and UI
Integrate WebSocket or polling mechanisms to notify your users when decryption keys become available. Future releases of Shutter will enhance real-time notifications.
Future Features and Considerations
-
Event-Based & Block-Based Triggers
In future versions, decryption triggers may be based on blockchain events or block numbers, offering more flexibility.
-
Real-Time Notifications
Planned WebSocket integrations will enable real-time notifications for key releases.
-
Enhanced Decentralization
As more Keypers join the network, the decentralization and resilience of the system will continue to improve.
FAQs
What if a Keyper goes offline?
The system is designed to handle downtime gracefully. Any missed decryption key releases will be processed when the network recovers.
How secure is the Shutter system?
Using threshold encryption and distributed key management, Shutter ensures that no single party can compromise the encrypted data. However, as the network evolves, always monitor updates and recommendations.
Can I use my own address for identity registration?
Yes. By default, the API uses the account address to compute the identity. If you prefer to use your own address, register directly with the registry contract.
Additional Resources
- Swagger Documentation
- Shutter SDK Repository
- Registry Contract Code
- Shutter API Examples