Task 4: Precompiles
The fourth task is about the precompiles
Pre-compiles in Ethereum are specialized functions implemented at the protocol level, designed to execute complex operations more efficiently than standard smart contract code. They allow developers to perform tasks such as cryptographic hashing (e.g., SHA-256), elliptic curve operations, and more, without having to manually implement these algorithms in Solidity. This enhances both performance and security.
Now, moving to Sapphire: as we mentioned earlier regarding gas padding, Sapphire offers additional pre-compiles that are tailored for its unique confidentiality features. For instance, it includes a built-in random number generator for securely generating random numbers or secret keys directly on-chain. Sapphire also supports advanced cryptographic operations like X25519 key exchange and Deoxys encryption/decryption, enabling confidential computation and secure data handling.
This added functionality is part of what makes Sapphire distinct, as it extends the standard pre-compiles found in Ethereum to support privacy-preserving applications.
For a full list visit our docs, where you will find a nice list of precompiles there.
In order to use the precompiles you will need to import them and they are offered by the pnpm packaged:
First we will need to run pnpm install in the backend folder:
and then inside the contract we will need to import them:
Next, let’s implement a simple on-chain CAPTCHA to deter spammers from flooding our contract with spam messages.
To keep things straightforward, the CAPTCHA will involve adding two numbers together. If the user provides the correct sum, the message will be posted. If not, the transaction will be reverted.
To achieve this,we will use something called the captcha seed, which we will combine this captcha seed which is confidential with the number of already submitted messages, so for each message you post there will be a new captcha generated for you and you will need to manually compute the result of that captcha and submit it along your messageal number of messages already submitted.
We will add a Captcha seed and a message counter:
When you deploy the contract you will want to initialize this captcha seed, so we will write a constructor. We will also use the random bytes precompile offered by Sapphire which basically produces any number of bytes. We will use the 32 bytes here and with no additional entropy provided just an empty string in our case
dck
when a user wants to set message he first needs to receive the captcha question or the captcha riddle and then submit the answer along the set message function. To compute the captcha riddle we will have this function :
which will return the two numbers that we will need to add. For the entropy we will use our captchaSeed, our message counter and we will use something more unique because this is the same inside the same call: a is 0 and b is 1. Now we will get a bunch of random bytes which will transform into our unsign integer inside of some range
This computes the captcha. In the set message we want the correct answer to be provided. First we will want to compute capthca using the same logic we used for the generation of captcha.
If a+b=captcha the message will be set, if not we will revert. We will also need to add the _messageCounter.
Now let’s deploy it using the same testnet and the same key
if we try to call the set message with this, we have a problem because we don’ have the hardhead task to provide the captcha yet so let’s add the support to our set message as well
This is fine , but we also need a task for obtaining the captcha
This will connect to our contract and it will call this compute captcha and print it on screen
Let’s also check if the message was stored
This example demonstrates how the on-chain random number generator can effectively solve the captcha problem without relying on intermediaries, bridges, or external web servers. With this, our hands-on workshop comes to a close.