This is a CLI tool to decode Compound governance proposals. It was developed by ChainSecurity as part of the Security Service Provider engagement with Compound to streamline proposal support. The decoder fetches proposal data from the blockchain, retrieves ABIs and contract names from Etherscan, and then decodes the proposal's actions into a human-readable format.
- Fetches proposal details directly from the Compound Governor contract.
- Retrieves contract ABIs and names from Etherscan.
- Caches ABIs and contract names locally to speed up subsequent runs.
- Decodes proposal actions, including function calls and parameters.
- Specialized handlers for decoding transactions through bridges (e.g., Linea).
- Colorized terminal output for readability.
- Adjustable logging levels.
This is the recommended way to run the proposal decoder, as it requires no local setup other than Docker.
-
Build the Docker image:
docker build -t proposal-decoder . -
Run the decoder in a container: Create a
.envfile (you can copy.env.example) with your Etherscan API key and RPC URLs. Then, run the container, passing the proposal ID as an argument.docker run --env-file .env -v "$(pwd)/.cache:/usr/src/app/.cache" -it proposal-decoder <proposalIdNumber>
For example:
docker run --env-file .env -v "$(pwd)/.cache:/usr/src/app/.cache" -it proposal-decoder 474Using the
-v "$(pwd)/.cache:/usr/src/app/.cache"flag is recommended. It maps the container's cache directory to your local filesystem, which speeds up subsequent runs by reusing already downloaded data.The
-itflags are important to preserve color and formatting in the output.Logging: You can control the log level using the
--log-level(or-l) flag. This is useful for debugging.docker run --env-file .env -it proposal-decoder 221 --log-level debug
- Node.js (v20 or higher)
- pnpm
- An Etherscan API key
- RPC URLs for Ethereum mainnet and Linea
-
Clone the repository:
git clone https://github.com/Arr00/proposal-decoder.git cd proposal-decoder -
Install dependencies:
pnpm install
-
Set up environment variables: Create a
.envfile by copying the example:cp .env.example .env
Then, edit the
.envfile with your Etherscan API key and RPC URLs:ETHERSCAN_API_KEY=your_etherscan_api_key ETH_RPC_URL=your_ethereum_rpc_url LINEA_RPC_URL=your_linea_rpc_url
To decode a proposal, run the decode script with the proposal ID:
pnpm run decode <proposalIdNumber>For example, to decode proposal 221:
pnpm run decode 221You can also set the log level for more detailed output:
pnpm run decode 221 --log-level debugThe <proposal> argument accepts three different formats, allowing you to work with live proposals, saved JSON exports, or raw calldata captured from the governor:
- A numeric Compound proposal ID (as shown above).
- A proposal JSON object (inline or via file path).
- A raw calldata blob for
propose(address[],uint256[],bytes[],string).
Pass either a file path or an inline JSON blob. The decoder looks for the targets, values, calldatas, and descriptionHash fields either at the top level or under a details object, and the arrays must all have the same length. Optional metadata (governor, proposalId, chainId) can be supplied either at the top level or under a metadata object to improve logging.
# Using a JSON file
pnpm run decode ./proposal-474.json
# Inline JSON (quote with single quotes so the shell leaves it untouched)
pnpm run decode '{ "details": { "targets": ["0x..."], "values": ["0"], "calldatas": ["0x..."], "descriptionHash": "0x..." }, "metadata": { "governor": "0x...", "proposalId": "474", "chainId": 1 } }'Fields in values and proposalId can be numbers, numeric strings, or bigint literals. If you already have data that matches the Compound GovernorAlpha/Bravo.propose arguments, you can drop the details wrapper and provide those fields at the top level instead.
You can feed the tool a calldata blob captured from a propose transaction (e.g. from Tenderly or an RPC trace). The blob must be valid hex (0x-prefixed, even length) encoding of the propose(address[],uint256[],bytes[],string) call. The decoder will extract the proposal details and continue as if they were provided via JSON.
pnpm run decode 0x2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c...If the calldata cannot be parsed as the Compound propose function, the decoder exits with an explanatory error.