The Nouns SDK contains useful tooling for interacting with the Nouns protocol.
This sdk uses Wagmi CLI to generate type-safe react hooks and actions for each contract method of all Nouns Contracts:
- NounsGovernor: The governance logic
- NounsTreasury: Current treasury and timelock
- NounsLegacyTreasury: Legacy treasury and timelock
- NounsData: Proposal Candidate and onchain comments contract
- NounsToken: The NFT
- NounsAuctionHouse: The auction contract
- NounsDescriptor: The NFT Descriptor (onchain artwork + metadata)
- NounsStreamFactory: A utility contract for streaming payments
- NounsUSDCPayer: Contract used to hold and disperse USDC payments for the DAO
- NounsUSDCTokenBuyer: Contract used to buy USDC tokens for the treasury
You can check the linked contracts above for all the available methods.
They're generated from the contract ABIs and include type-safe methods for all of the read and write functions.
[!TIP] To get full IDE completions on the contract methods, ensure you have
strict
mode enabled in yourtsconfig.json
and that you fullfill all the wagmi requirements
Actions are framework-agnostic named wrappers around wagmi's methods that supply the abi, address and functionName parameters, so all you have to provide is the wagmi config and the call args.
The actions for each contract are available as a separate import:
@nouns/sdk/governor
@nouns/sdk/legacy-treasury
@nouns/sdk/treasury
@nouns/sdk/data
@nouns/sdk/token
@nouns/sdk/auction-house
@nouns/sdk/descriptor
@nouns/sdk/stream-factory
@nouns/sdk/usdc-payer
@nouns/sdk/usdc-token-buyer
Every read function is available as a wrapper around wagmi's readContract in the format: read<ContractName><FunctionName>(wagmiConfig, args)
e.g.:
import { createConfig, http } from "@wagmi/core";
import { mainnet } from "@wagmi/core/chains";
import { readNounsAuctionHouseAuction } from '@nouns/sdk/auction-house';
const config = createConfig({
chains: [mainnet],
transports: {
[mainnet.id]: http(),
},
});
const currentAuction = await readNounsAuctionHouseAuction(config, {})
// {
// nounId: 1558n;
// amount: 500000000000000000n;
// startTime: 1753166495;
// endTime: 1753252895;
// bidder: "0xa0Cf2260CA43252A3620e80A5CFE40968f042634";
// settled: false;
// }
Write functions are available both as wrappers around writeContract and simulateContract:
write<ContractName><FunctionName>(wagmiConfig, args)
simulate<ContractName><FunctionName>(wagmiConfig, args)
e.g.:
import { createConfig, http } from "@wagmi/core";
import { mainnet } from "@wagmi/core/chains";
import { simulateNounsAuctionHouseCreateBid, writeNounsAuctionHouseCreateBid } from '@nouns/sdk/auctionHouse';
const config = createConfig({
chains: [mainnet],
transports: {
[mainnet.id]: http(),
},
});
const nounId = 1500n;
// Throws an error if the simulation fails
await simulateNounsAuctionHouseCreateBid(config, {
args: [nounId],
value: 1_000_000_000n, // 1 ETH
});
const txHash = await writeNounsAuctionHouseCreateBid(config, {
args: [nounId],
value: 1_000_000_000n, // 1 ETH
});
Contract events can be watched using the watchContractEvent wrappers: watch<ContractName><EventName>Event(wagmiConfig, args)
e.g.:
import { createConfig, http } from "@wagmi/core";
import { mainnet } from "@wagmi/core/chains";
import { watchNounsAuctionHouseAuctionBidEvent } from '@nouns/sdk/auction-house';
const config = createConfig({
chains: [mainnet],
transports: {
[mainnet.id]: http(),
},
});
const unwatch = watchNounsAuctionHouseAuctionBidEvent(config, {
onLogs(logs) {
logs.forEach((log) => {
console.log("Auction bid event:", log.args);
// {
// extended: false,
// sender: "0xa0Cf2260CA43252A3620e80A5CFE40968f042634",
// nounId: 1558n,
// value: 500000000000000000n,
// }
});
},
})
unwatch(); //unsubscribe
Every read/write/simulate/watch action also has a corresponding wagmi react hook. They're wrappers around Wagmi React's hooks, leveraging WagmiProvider for the config and TanstackQuery for state management:
useRead<ContractName><FunctionName>(params)
useWrite<ContractName><FunctionName>(params)
useSimulate<ContractName><FunctionName>(params)
useWatch<ContractName><EventName>Event(params)
The hooks for each contract are available as a separate import:
@nouns/sdk/react/governor
@nouns/sdk/react/legacy-treasury
@nouns/sdk/react/treasury
@nouns/sdk/react/data
@nouns/sdk/react/token
@nouns/sdk/react/auction-house
@nouns/sdk/react/descriptor
@nouns/sdk/react/stream-factory
@nouns/sdk/react/usdc-payer
@nouns/sdk/react/usdc-token-buyer
example:
import { useReadNounsGovernorVotingPeriod } from '@nouns/sdk/react/governor';
// Must be used inside of a <WagmiProvider/>
const { data } = useReadNounsGovernorVotingPeriod();
// data: 28800n
Run-length Encode Images
import { PNGCollectionEncoder } from '@nouns/sdk';
import { readPngFile } from 'node-libpng';
import { promises as fs } from 'fs';
import path from 'path';
const DESTINATION = path.join(__dirname, './output/image-data.json');
const encode = async () => {
const encoder = new PNGCollectionEncoder();
const folders = ['bodies', 'accessories', 'heads', 'glasses'];
for (const folder of folders) {
const folderpath = path.join(__dirname, './images', folder);
const files = await fs.readdir(folderpath);
for (const file of files) {
const image = await readPngFile(path.join(folderpath, file));
encoder.encodeImage(file.replace(/\.png$/, ''), image, folder);
}
}
await encoder.writeToFile(DESTINATION);
};
encode();
Create SVGs from Run-length Encoded Data
import { buildSVG } from '@nouns/sdk';
const svg = buildSVG(RLE_PARTS, PALETTE_COLORS, BACKGROUND_COLOR);
pnpm install
pnpm test
pnpm codegen
pnpm build