Non-Network Specific Precompiled Smart Contracts¶
Introduction¶
A precompiled contract, or precompile, is a set of programmed functionalities hard-coded into the blockchain client. Precompiles perform computationally heavy tasks, such as cryptographic processes like hashing. Moving these functionalities to the blockchain client serves the dual purpose of making the computation more efficient than using a traditional smart contract and ensuring everyone has access to the complete and accurate set of processes and algorithms required to operate correctly.
Precompile functionality is bundled and shared under a smart contract address, which allows interactions similar to those of a traditional smart contract. Some precompiled contracts are not specific to Ethereum or Moonbeam, but are supported for use in your Moonbeam application.
The nonspecific precompiles currently included in this category include StorageCleaner
, ECRecoverPublicKey
, and SHA3FIPS256
.
In the next section, you will learn more about the functionalities included in these precompiles.
Clear Storage Entries with StorageCleaner¶
The primary function of the StorageCleaner
precompile is to clear storage entry key-value pairs for a smart contract marked as self-destructed, previously referred to as 'suicided.' StorageCleaner
includes functionality to iterate over a list of addresses to identify self-destructed contracts and delete the appropriate storage entries associated with identified addresses. You can also input a numeric limit to prevent the precompile from consuming too much gas.
With the implementation of EIP-6780: SELFDESTRUCT as part of the Ethereum Cancun/Dencun upgrade, contracts can only be self-destructed in the same transaction where they are created. This limitation keeps storage entries small and allows them to be automatically deleted during destruction. The StorageCleaner
precompile remains available when a legacy contract needs storage entries cleared.
Retrieve a Public Key with ECRecoverPublicKey¶
The primary function of the ECRecoverPublicKey
precompile is to recover the public key used to create a digital signature from a given message hash and signature. This precompile is similar to ECRecover, with the exception of returning the public key of the account that signed the message rather than the account address.
In the following sections, you will learn how to use the ECRecoverPublicKey
precompile.
Checking Prerequisites¶
You need to install Node.js (for this example, you can use v16.x) and the npm package manager. You can download directly from Node.js or in your terminal:
curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash -
sudo apt install -y nodejs
# You can use homebrew (https://docs.brew.sh/Installation)
brew install node
# Or you can use nvm (https://github.com/nvm-sh/nvm)
nvm install node
You can verify that everything is installed correctly by querying the version for each package:
node -v
npm -v
The versions used in this example are v20.15.0 (Node.js) and 10.7.0 (npm). You will also need to install the Web3 package by executing:
npm install --save web3
To verify the installed version of Web3, you can use the ls
command:
npm ls web3
This example uses version 4.11.1. You will also use Remix, connecting it to the Moonbase Alpha TestNet via MetaMask.
To test out the examples in this guide on Moonbeam or Moonriver, you will need to have your own endpoint and API key, which you can get from one of the supported Endpoint Providers.
Retrieve Transaction Signature Values¶
To use the ECRecoverPublicKey
precompile, you must first sign a message to create and retrieve the message hash and transaction signature values (v
, r
, s
) to pass as arguments in the contract call. Always use security best practices when handling private keys.
Create a new file called signMessage.js
in your project directory:
touch signMessage.js
Open signMessage.js
in your code editor and add the following script to initialize Web3 with Moonbase Alpha TestNet, sign and hash the message, and return the signature values:
const { Web3 } = require('web3');
const web3 = new Web3('https://rpc.api.moonbase.moonbeam.network');
// Address and private key
const address = 'INSERT_RECEIVER_ADDRESS';
const pk1 =
'INSERT_SENDER_PRIVATE_KEY';
const msg = web3.utils.sha3('supercooltestmessage');
async function signMessage(pk) {
try {
// Sign and get signed message
const smsg = await web3.eth.accounts.sign(msg, pk);
console.log(smsg);
} catch (error) {
console.error(error);
}
}
signMessage(pk1);
Return to your terminal command line to run the script with this command:
node signMessage.js
This code will return the following object in the terminal:
Save these values as you will need them in the next section.
Test ECRecoverPublicKey Contract¶
You can now visit Remix to test the precompiled contract. Note that you could also use the Web3.js library, but in this case, you can go to Remix to ensure it is using the precompiled contract on the blockchain. The Solidity code you can use to retrieve the public key is the following:
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;
contract RecoverPublicKey {
function recoverPublicKey(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) public view returns (bytes memory) {
address precompileAddress = 0x0000000000000000000000000000000000000402;
(bool success, bytes memory publicKey) = precompileAddress.staticcall(
abi.encodeWithSignature(
"ECRecoverPublicKey(bytes32,uint8,bytes32,bytes32)",
hash,
v,
r,
s
)
);
require(success, "ECRecoverPublicKey failed");
return publicKey;
}
}
Using the Remix compiler and deployment and with MetaMask pointing to Moonbase Alpha, you can deploy the contract and call the recoverPublicKey()
method which returns the public key for the account that signed the message. You can now use this public key value for other cryptographic functions and verifications.
Create a Hash with SHA3FIPS256¶
SHA3-256 is part of the SHA-3 family of cryptographic hashes codified in FIPS202 that produces an output 256 bits in length. Although the name is similar to SHA256, the SHA-3 family is built with an entirely different algorithm and accordingly produces a different hash output than SHA256 for the same input. You can verify this yourself using this SHA3-256 Hash Calculator tool. After calculating the SHA3-256 output, change the algorithm in the drop-down selector to SHA256 and take note of the resulting output.
Currently there is no SHA3-256 support in Solidity, so it needs to be called with inline assembly. The following sample code can be used to call this precompile.
pragma solidity ^0.7.0;
contract Precompiles {
function sha3fips(bytes memory data) public view returns (bytes32) {
bytes32[1] memory h;
assembly {
if iszero(
staticcall(not(0), 0x400, add(data, 32), mload(data), h, 32)
) {
invalid()
}
}
return h[0];
}
}
Using Remix with MetaMask pointing to Moonbase Alpha, you can deploy the contract and call the sha3fips(bytes memory data)
method to return the encoded string of the data parameter.
| Created: August 8, 2024