This document provides a comprehensive reference for all functions in the AbrahamSeeds.sol smart contract and its base contract EdenAgent.sol, with code examples for each function.
- Contract Overview
- Core Concepts
- Structs & Types
- Seed Management Functions
- Blessing Functions
- Commandment Functions
- Winner Selection Functions
- Edition Functions
- Query Functions (Read-Only)
- Configuration Functions (Admin)
- Role Management Functions (Admin)
- Delegation Functions
- Events
- Code Examples
AbrahamSeeds is an ERC1155-based NFT contract that extends the EdenAgent base contract. It manages a daily competition system where users submit "seeds" (artwork proposals) and vote on them through "blessings". The contract features:
- ERC1155 Editions - Winners receive ERC1155 NFTs with multiple editions (creator, curator, public)
- Quadratic (sqrt) scoring - Prevents whales from dominating using sqrt of per-user blessings
- Period-based competitions - Configurable voting periods with automatic winner selection
- Cross-chain NFT-gated voting - Only FirstWorks NFT holders can bless (verified via Merkle proofs)
- Role-based access control - ADMIN, CREATOR, and OPERATOR roles
- Modular gating - Pluggable MerkleGating module for ownership verification
- Commandments - Messages/comments on seeds
Architecture:
AbrahamSeeds (Abraham-specific wrapper)
└── EdenAgent (Core functionality)
├── AccessControl (Role management)
├── ERC1155 (NFT standard)
├── ERC1155Supply (Supply tracking)
└── ReentrancyGuard (Security)
Deployed Contracts:
| Network | Contract | Address |
|---|---|---|
| Base Sepolia | AbrahamSeeds | 0x0b95d25463b7a937b3df28368456f2c40e95c730 |
| Base Sepolia | MerkleGating | 0x46657b69308d90a4756369094c5d78781f3f5979 |
A Seed is a proposal for artwork, stored on-chain with IPFS metadata. Seeds have:
- Unique ID (auto-incremented)
- Creator address
- IPFS hash (containing title, description, image)
- Blessing count (raw count)
- Blessing score (sqrt-scaled anti-whale score)
- Commandment count (messages)
- Submission round tracking
- Creation round (when/if selected as winner)
Blessings are votes on seeds. The blessing system features:
- NFT-gated: Only FirstWorks NFT holders can bless
- Daily limit: 1 blessing per NFT owned per 24 hours (configurable)
- Anti-whale: Uses sqrt(blessings_per_user) for scoring
- Merkle proof verification: Cross-chain ownership verification
For each user's blessings on a seed:
previous_score = sqrt(previous_blessing_count * SCALE)
new_score = sqrt((previous_blessing_count + 1) * SCALE)
delta = new_score - previous_score
Total seed score = sum of all deltas (quadratic scoring)
SCALE = 1e6 (for precision)
- Duration: Configurable (default 24 hours)
- Winner selection: Highest scoring seed wins
- NFT minting: Winner gets ERC1155 editions
- New period: Automatically starts after winner selection
When a seed wins, ERC1155 editions are minted:
- Creator editions: Sent directly to seed creator
- Curator editions: Distributed by operator to top blessers
- Public editions: Available for purchase
struct Seed {
uint256 id; // Unique seed identifier
address creator; // Address that submitted the seed
string ipfsHash; // IPFS hash of seed metadata
uint256 blessings; // Raw blessing count
uint256 score; // Quadratic blessing score
uint256 commandmentCount; // Number of commandments (messages)
uint256 createdAt; // Timestamp of creation
uint256 submittedInRound; // Round when submitted
uint256 creationRound; // Round when selected (0 if not winner)
bool isRetracted; // Whether seed was retracted
}struct Session {
uint256 id;
address creator;
string contentHash;
uint256 reactionCount; // Raw count
uint256 reactionScore; // Quadratic score
uint256 messageCount;
uint256 createdAt;
uint256 submittedInPeriod;
uint256 selectedInPeriod; // 0 if not selected
bool isRetracted;
}struct Config {
uint256 periodDuration; // Duration of each voting period (default: 1 day)
uint256 reactionsPerToken; // Blessings allowed per NFT per day (default: 1)
uint256 messagesPerToken; // Messages allowed per NFT per day (default: 1)
uint256 editionPrice; // Price per public edition (default: 0)
}struct EditionAlloc {
uint256 creatorAmount; // Editions minted to creator
uint256 curatorAmount; // Editions for top curators
uint256 publicAmount; // Editions available for purchase
}bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
bytes32 public constant CREATOR_ROLE = keccak256("CREATOR_ROLE");
// DEFAULT_ADMIN_ROLE from AccessControlSubmit a new seed to the current round.
Signature:
function submitSeed(string calldata ipfsHash)
external
whenNotPaused
onlyRole(CREATOR_ROLE)
returns (uint256)Access: CREATOR_ROLE required
Parameters:
ipfsHash- IPFS hash containing seed metadata
Returns: Seed ID (uint256)
Validation:
- IPFS hash must be 10-100 characters
- Contract not paused
Events Emitted:
event SeedSubmitted(uint256 indexed seedId, address indexed creator, string ipfsHash, uint256 round);
event SessionSubmitted(uint256 indexed sessionId, address indexed creator, string contentHash, uint256 period);Example Usage:
import { abrahamSeedsABI } from './abi/AbrahamSeeds';
// Submit a seed
const tx = await walletClient.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'submitSeed',
args: ['ipfs://QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG']
});
const receipt = await publicClient.waitForTransactionReceipt({ hash: tx });
// Get seed ID from event logs
const seedSubmittedLog = receipt.logs.find(log =>
log.topics[0] === '0x...' // SeedSubmitted event signature
);
const seedId = Number(seedSubmittedLog.topics[1]);
console.log(`Seed ${seedId} submitted!`);Retract a seed (creator only, cannot retract winners).
Signature:
function retractSeed(uint256 seedId) externalAccess: Seed creator only
Parameters:
seedId- ID of seed to retract
Requirements:
- Caller must be seed creator
- Seed must exist
- Seed must not have been selected as winner
- Seed must not already be retracted
Events Emitted:
event SessionRetracted(uint256 indexed seedId, address indexed creator);Example Usage:
// Retract your seed
const tx = await walletClient.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'retractSeed',
args: [42n] // seed ID
});
await publicClient.waitForTransactionReceipt({ hash: tx });
console.log('Seed retracted');Get complete seed information.
Signature:
function getSeed(uint256 seedId) external view returns (Seed memory)Access: Public (read-only)
Parameters:
seedId- Seed ID to query
Returns: Seed struct
Example Usage:
const seed = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getSeed',
args: [42n]
});
console.log({
id: Number(seed.id),
creator: seed.creator,
ipfsHash: seed.ipfsHash,
blessings: Number(seed.blessings),
score: Number(seed.score),
commandmentCount: Number(seed.commandmentCount),
createdAt: Number(seed.createdAt),
submittedInRound: Number(seed.submittedInRound),
creationRound: Number(seed.creationRound),
isRetracted: seed.isRetracted
});Bless a seed with your own NFTs (user signs transaction).
Signature:
function blessSeed(
uint256 seedId,
uint256[] calldata tokenIds,
bytes calldata proof
) external payableAccess: Public (requires NFT ownership proof)
Parameters:
seedId- ID of seed to blesstokenIds- Array of FirstWorks NFT token IDs you ownproof- Merkle proof of ownership (encoded)
Requirements:
- Must own the NFTs (verified via MerkleGating)
- Seed must exist and not be winner/retracted
- Voting period must be active
- Must not exceed daily blessing limit
Events Emitted:
event BlessingSubmitted(uint256 indexed seedId, address indexed blesser, uint256 score);
event ReactionSubmitted(uint256 indexed sessionId, address indexed reactor, uint256 newScore);Example Usage:
// Get your NFT token IDs and merkle proof from API
const tokenIds = [1n, 5n, 10n];
const merkleProof = '0x...'; // Encoded proof from merkle tree
// Bless a seed
const tx = await walletClient.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'blessSeed',
args: [42n, tokenIds, merkleProof]
});
await publicClient.waitForTransactionReceipt({ hash: tx });
console.log('Blessing submitted!');Bless a seed on behalf of another user (relayer/delegate).
Signature:
function blessSeedFor(
uint256 seedId,
address blesser,
uint256[] calldata tokenIds,
bytes calldata proof
) external payableAccess: OPERATOR_ROLE or approved delegate
Parameters:
seedId- ID of seed to blessblesser- Address of the user blessingtokenIds- Array of NFT token IDs owned by blesserproof- Merkle proof of blesser's ownership
Requirements:
- Caller must have OPERATOR_ROLE OR be approved delegate for blesser
- Blesser must own the NFTs (verified via MerkleGating)
- Same requirements as blessSeed
Events Emitted: Same as blessSeed
Example Usage:
// Backend relayer blessing on behalf of user (gasless)
const tx = await relayerWallet.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'blessSeedFor',
args: [
seedId,
userAddress,
tokenIds,
merkleProof
]
});
await publicClient.waitForTransactionReceipt({ hash: tx });Add a commandment (message) to a seed.
Signature:
function addCommandment(
uint256 seedId,
string calldata ipfsHash,
uint256[] calldata tokenIds,
bytes calldata proof
) external payableAccess: Public (requires NFT ownership proof)
Parameters:
seedId- ID of seed to comment onipfsHash- IPFS hash of commandment contenttokenIds- Array of NFT token IDs for verificationproof- Merkle proof of ownership
Requirements:
- Must own NFTs (verified via MerkleGating)
- Seed must exist
- Must not exceed daily message limit
- IPFS hash must be valid (10-100 chars)
Events Emitted:
event CommandmentSubmitted(uint256 indexed id, uint256 indexed seedId, address indexed author, string ipfsHash);
event MessageSubmitted(uint256 indexed messageId, uint256 indexed sessionId, address indexed sender, string contentHash);Example Usage:
const tx = await walletClient.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'addCommandment',
args: [
seedId,
'ipfs://QmCommandmentContent...',
tokenIds,
merkleProof
]
});
await publicClient.waitForTransactionReceipt({ hash: tx });
console.log('Commandment added!');Select the winner of the current round and start new round.
Signature:
function selectDailyWinner() external returns (uint256 seedId)Access: Public (but typically called by backend/operator)
Requirements:
- Voting period must have ended
- At least one eligible seed with score > 0
Side Effects:
- Marks winning seed as selected
- Mints ERC1155 editions (creator, curator, public)
- Increments period number
- Starts new voting period
- Removes winner from eligible seeds
Returns: Winning seed ID
Events Emitted:
event CreationMinted(uint256 indexed round, uint256 indexed seedId, uint256 tokenId);
event RoundStarted(uint256 indexed round);
event SessionSelected(uint256 indexed period, uint256 indexed sessionId, uint256 score);
event EditionMinted(uint256 indexed sessionId, uint256 indexed tokenId, uint256 supply);
event PeriodStarted(uint256 indexed period);Example Usage:
// Usually called by backend cron job
const tx = await operatorWallet.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'selectDailyWinner'
});
const receipt = await publicClient.waitForTransactionReceipt({ hash: tx });
// Parse winning seed ID from events
const creationMintedLog = receipt.logs.find(log =>
log.topics[0] === '0x...' // CreationMinted event signature
);
const winningSeedId = Number(creationMintedLog.topics[2]);
console.log(`Winner: Seed ${winningSeedId}`);Distribute curator editions to top blessers (priests).
Signature:
function rewardPriests(
uint256 tokenId,
address[] calldata priests,
uint256[] calldata amounts
) externalAccess: OPERATOR_ROLE (via distributeCuratorEditions)
Parameters:
tokenId- ERC1155 token ID of the winning creationpriests- Array of curator addresses to rewardamounts- Array of edition amounts for each curator
Requirements:
- Arrays must have same length
- Total must not exceed curator allocation
- Contract must have enough editions
Events Emitted:
event PriestsRewarded(uint256 indexed tokenId, address[] priests, uint256[] amounts);
event CuratorEditionsDistributed(uint256 indexed tokenId, address[] curators, uint256[] amounts);Example Usage:
// Distribute curator editions to top 3 blessers
const tx = await operatorWallet.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'rewardPriests',
args: [
tokenId,
[curator1, curator2, curator3],
[1n, 1n, 1n]
]
});
await publicClient.waitForTransactionReceipt({ hash: tx });Purchase public editions of a creation.
Signature:
function purchaseCreation(uint256 tokenId, uint256 amount) external payableAccess: Public
Parameters:
tokenId- ERC1155 token ID to purchaseamount- Number of editions to purchase
Requirements:
- Must send correct payment (editionPrice * amount)
- Enough public editions must be available
Value Split:
- 50% to creator
- 50% to treasury
Events Emitted:
event EditionPurchased(uint256 indexed tokenId, address indexed buyer, uint256 amount, uint256 price);Example Usage:
const price = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getEditionPrice'
});
const tx = await walletClient.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'purchaseCreation',
args: [tokenId, 1n],
value: price
});
await publicClient.waitForTransactionReceipt({ hash: tx });
console.log('Edition purchased!');Get edition information for a creation.
Signature:
function getCreationEditionInfo(uint256 tokenId) external view returns (
uint256 seedId,
uint256 totalMinted,
uint256 creatorEditions,
uint256 curatorEditions,
uint256 curatorDistributed,
uint256 publicEditions,
uint256 publicSold,
uint256 availableForSale
)Example Usage:
const info = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getCreationEditionInfo',
args: [tokenId]
});
console.log({
seedId: Number(info.seedId),
totalMinted: Number(info.totalMinted),
creatorEditions: Number(info.creatorEditions),
curatorEditions: Number(info.curatorEditions),
curatorDistributed: Number(info.curatorDistributed),
publicEditions: Number(info.publicEditions),
publicSold: Number(info.publicSold),
availableForSale: Number(info.availableForSale)
});Get total number of seeds created.
Signature:
function getSeedCount() external view returns (uint256)Example:
const count = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getSeedCount'
});
console.log(`Total seeds: ${count}`);Get current round number.
Signature:
function getCurrentRound() external view returns (uint256)Example:
const round = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getCurrentRound'
});
console.log(`Round ${round}`);Get seconds remaining in current voting period.
Signature:
function getTimeUntilRoundEnd() external view returns (uint256)Returns: Seconds remaining (0 if period ended)
Example:
const remaining = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getTimeUntilRoundEnd'
});
const hours = Number(remaining) / 3600;
console.log(`${hours.toFixed(1)} hours until winner selection`);Get blessing score for a specific seed.
Signature:
function getSeedBlessingScore(uint256 seedId) external view returns (uint256)Returns: Blessing score (quadratic scaled)
Example:
const score = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getSeedBlessingScore',
args: [42n]
});
console.log(`Seed 42 score: ${score}`);Get how many times a user has blessed a specific seed.
Signature:
function getBlessingCount(address user, uint256 seedId) external view returns (uint256)Example:
const count = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getBlessingCount',
args: [userAddress, 42n]
});
console.log(`User blessed seed 42 ${count} times`);Get how many blessings a user has left today.
Signature:
function getRemainingBlessings(address user, uint256 tokenCount) external view returns (uint256)Parameters:
user- User addresstokenCount- Number of FirstWorks NFTs owned
Returns: Remaining blessings
Example:
const nftCount = 3n;
const remaining = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getRemainingBlessings',
args: [userAddress, nftCount]
});
console.log(`${remaining} of ${nftCount} blessings left today`);Check if user can bless today (has remaining blessings).
Signature:
function canBlessToday(address user, uint256 tokenCount) external view returns (bool)Example:
const canBless = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'canBlessToday',
args: [userAddress, 3n]
});
console.log(`Can bless: ${canBless}`);Get count of eligible seeds (non-winner, non-retracted).
Signature:
function getEligibleSeedsCount() external view returns (uint256)Example:
const count = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getEligibleSeedsCount'
});
console.log(`${count} eligible seeds`);Get number of commandments for a seed.
Signature:
function getCommandmentCount(uint256 seedId) external view returns (uint256)Example:
const count = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getCommandmentCount',
args: [42n]
});
console.log(`Seed 42 has ${count} commandments`);Get the winning seed ID for a specific round.
Signature:
function getRoundWinner(uint256 round) external view returns (uint256)Example:
const winnerId = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getRoundWinner',
args: [5n]
});
console.log(`Round 5 winner: Seed ${winnerId}`);Get NFT token ID for a winning seed.
Signature:
function getTokenIdBySeedId(uint256 seedId) external view returns (uint256)Returns: Token ID (0 if seed hasn't won)
Example:
const tokenId = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getTokenIdBySeedId',
args: [42n]
});
if (tokenId === 0n) {
console.log('Seed has not won yet');
} else {
console.log(`Seed 42 NFT token ID: ${tokenId}`);
}Get seed ID for an NFT token.
Signature:
function getSeedIdByTokenId(uint256 tokenId) external view returns (uint256)Example:
const seedId = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getSeedIdByTokenId',
args: [1n]
});
console.log(`Token 1 is for seed ${seedId}`);Get blessings allowed per NFT per day.
Signature:
function blessingsPerNFT() external view returns (uint256)Example:
const perNFT = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'blessingsPerNFT'
});
console.log(`${perNFT} blessing(s) per NFT per day`);Get voting period duration in seconds.
Signature:
function votingPeriod() external view returns (uint256)Example:
const period = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'votingPeriod'
});
console.log(`Voting period: ${Number(period) / 3600} hours`);Get price per public edition.
Signature:
function getEditionPrice() external view returns (uint256)Example:
const price = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getEditionPrice'
});
console.log(`Edition price: ${formatEther(price)} ETH`);Get edition allocation configuration.
Signature:
function getEditionAllocation() external view returns (
uint256 creator,
uint256 curator,
uint256 public_
)Example:
const [creator, curator, public_] = await publicClient.readContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: abrahamSeedsABI,
functionName: 'getEditionAllocation'
});
console.log(`Creator: ${creator}, Curator: ${curator}, Public: ${public_}`);Update contract configuration.
Signature:
function setConfig(Config calldata newConfig) external onlyRole(DEFAULT_ADMIN_ROLE)Access: DEFAULT_ADMIN_ROLE only
Parameters:
newConfig- New Config struct
Example:
const tx = await adminWallet.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: edenAgentABI,
functionName: 'setConfig',
args: [{
periodDuration: 86400n, // 1 day
reactionsPerToken: 1n, // 1 blessing per NFT
messagesPerToken: 1n, // 1 message per NFT
editionPrice: 0n // Free editions
}]
});
await publicClient.waitForTransactionReceipt({ hash: tx });Update edition allocation.
Signature:
function setEditionAlloc(EditionAlloc calldata newAlloc) external onlyRole(DEFAULT_ADMIN_ROLE)Example:
const tx = await adminWallet.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: edenAgentABI,
functionName: 'setEditionAlloc',
args: [{
creatorAmount: 1n,
curatorAmount: 5n,
publicAmount: 10n
}]
});Update the gating module.
Signature:
function setGatingModule(address module) external onlyRole(DEFAULT_ADMIN_ROLE)Example:
const tx = await adminWallet.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: edenAgentABI,
functionName: 'setGatingModule',
args: [newGatingModuleAddress]
});Pause or unpause the contract.
Signature:
function pause() external onlyRole(DEFAULT_ADMIN_ROLE);
function unpause() external onlyRole(DEFAULT_ADMIN_ROLE);Example:
// Pause contract (emergency stop)
const tx = await adminWallet.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: edenAgentABI,
functionName: 'pause'
});
// Unpause
const tx2 = await adminWallet.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: edenAgentABI,
functionName: 'unpause'
});Grant CREATOR_ROLE to an address.
Signature:
function addCreator(address creator) external onlyRole(DEFAULT_ADMIN_ROLE)Example:
const tx = await adminWallet.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: edenAgentABI,
functionName: 'addCreator',
args: [newCreatorAddress]
});Grant OPERATOR_ROLE to an address.
Signature:
function addOperator(address operator) external onlyRole(DEFAULT_ADMIN_ROLE)Example:
const tx = await adminWallet.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: edenAgentABI,
functionName: 'addOperator',
args: [newOperatorAddress]
});Approve or revoke a delegate to bless on your behalf.
Signature:
function approveDelegate(address delegate, bool approved) externalAccess: Public
Parameters:
delegate- Address to approve/revokeapproved- true to approve, false to revoke
Events Emitted:
event DelegateApproval(address indexed user, address indexed delegate, bool approved);Example Usage:
// Approve backend to bless on your behalf (for gasless blessings)
const BACKEND_RELAYER = '0x...';
const tx = await walletClient.writeContract({
address: SEEDS_CONTRACT_ADDRESS,
abi: edenAgentABI,
functionName: 'approveDelegate',
args: [BACKEND_RELAYER, true]
});
await publicClient.waitForTransactionReceipt({ hash: tx });
console.log('Backend approved for gasless blessings!');event SeedSubmitted(uint256 indexed seedId, address indexed creator, string ipfsHash, uint256 round);
event BlessingSubmitted(uint256 indexed seedId, address indexed blesser, uint256 score);
event CommandmentSubmitted(uint256 indexed id, uint256 indexed seedId, address indexed author, string ipfsHash);
event CreationMinted(uint256 indexed round, uint256 indexed seedId, uint256 tokenId);
event RoundStarted(uint256 indexed round);
event PriestsRewarded(uint256 indexed tokenId, address[] priests, uint256[] amounts);event SessionSubmitted(uint256 indexed sessionId, address indexed creator, string contentHash, uint256 period);
event SessionRetracted(uint256 indexed sessionId, address indexed creator);
event ReactionSubmitted(uint256 indexed sessionId, address indexed reactor, uint256 newScore);
event MessageSubmitted(uint256 indexed messageId, uint256 indexed sessionId, address indexed sender, string contentHash);
event SessionSelected(uint256 indexed period, uint256 indexed sessionId, uint256 score);
event EditionMinted(uint256 indexed sessionId, uint256 indexed tokenId, uint256 supply);
event EditionPurchased(uint256 indexed tokenId, address indexed buyer, uint256 amount, uint256 price);
event CuratorEditionsDistributed(uint256 indexed tokenId, address[] curators, uint256[] amounts);
event PeriodStarted(uint256 indexed period);
event DelegateApproval(address indexed user, address indexed delegate, bool approved);import { createPublicClient, createWalletClient, http } from 'viem';
import { baseSepolia } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
import abrahamSeedsABI from './abi/AbrahamSeeds.json';
const SEEDS_CONTRACT = '0x0b95d25463b7a937b3df28368456f2c40e95c730';
// Setup clients
const publicClient = createPublicClient({
chain: baseSepolia,
transport: http(process.env.RPC_URL)
});
const account = privateKeyToAccount('0x...');
const walletClient = createWalletClient({
account,
chain: baseSepolia,
transport: http(process.env.RPC_URL)
});
// 1. Prepare IPFS metadata
const metadata = {
name: "My Amazing Seed",
description: "A revolutionary art concept",
image: "ipfs://QmImage..."
};
// Upload to IPFS (using Pinata or similar)
const ipfsHash = await uploadToIPFS(metadata);
// 2. Submit seed
const hash = await walletClient.writeContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'submitSeed',
args: [`ipfs://${ipfsHash}`]
});
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log('Seed submitted!');
// 3. Get seed info
const seedCount = await publicClient.readContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'getSeedCount'
});
const seedId = Number(seedCount) - 1;
const seed = await publicClient.readContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'getSeed',
args: [BigInt(seedId)]
});
console.log(`Seed ${seedId} created:`, seed);// 1. Get user's NFT data and merkle proof
const response = await fetch(`${API_URL}/blessings/eligibility`, {
headers: { Authorization: `Bearer ${token}` }
});
const { data } = await response.json();
if (!data.eligible) {
throw new Error(`Not eligible: ${data.remainingBlessings} blessings remaining`);
}
// 2. Get merkle proof for user
const proofResponse = await fetch(`${API_URL}/merkle/proof/${userAddress}`);
const { proof, tokenIds } = await proofResponse.json();
// 3. Encode proof for contract
const encodedProof = encodeAbiParameters(
[{ type: 'bytes32[]' }],
[proof]
);
// 4. Bless the seed
const hash = await walletClient.writeContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'blessSeed',
args: [seedId, tokenIds.map(BigInt), encodedProof]
});
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log('Blessed!');
// 5. Check updated seed score
const seed = await publicClient.readContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'getSeed',
args: [seedId]
});
console.log(`Seed now has ${seed.blessings} blessings, score: ${seed.score}`);async function getRoundInfo() {
const [
currentRound,
timeRemaining,
seedCount,
eligibleCount
] = await Promise.all([
publicClient.readContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'getCurrentRound'
}),
publicClient.readContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'getTimeUntilRoundEnd'
}),
publicClient.readContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'getSeedCount'
}),
publicClient.readContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'getEligibleSeedsCount'
})
]);
const hoursRemaining = Number(timeRemaining) / 3600;
return {
round: Number(currentRound),
timeRemaining: `${hoursRemaining.toFixed(1)} hours`,
totalSeeds: Number(seedCount),
eligibleSeeds: Number(eligibleCount),
votingEnded: timeRemaining === 0n
};
}
const info = await getRoundInfo();
console.log(`
Round ${info.round}
Total Seeds: ${info.totalSeeds}
Eligible: ${info.eligibleSeeds}
Time: ${info.timeRemaining}
`);// Watch for new seed submissions
publicClient.watchContractEvent({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
eventName: 'SeedSubmitted',
onLogs: logs => {
for (const log of logs) {
const { seedId, creator, ipfsHash, round } = log.args;
console.log(`New seed ${seedId} by ${creator} in round ${round}`);
}
}
});
// Watch for blessings
publicClient.watchContractEvent({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
eventName: 'BlessingSubmitted',
onLogs: logs => {
for (const log of logs) {
const { seedId, blesser, score } = log.args;
console.log(`${blesser} blessed seed ${seedId}, new score: ${score}`);
}
}
});
// Watch for creations (winners)
publicClient.watchContractEvent({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
eventName: 'CreationMinted',
onLogs: logs => {
for (const log of logs) {
const { round, seedId, tokenId } = log.args;
console.log(`Round ${round} winner: Seed ${seedId}, Token ${tokenId}`);
}
}
});The contract uses custom errors for gas-efficient error handling:
error Paused(); // Contract is paused
error InvalidContentHash(); // Invalid IPFS hash format
error SessionNotFound(); // Seed doesn't exist
error SessionAlreadySelected(); // Seed already won
error SessionIsRetracted(); // Seed was retracted
error NotSessionCreator(); // Caller is not seed creator
error AlreadyRetracted(); // Seed already retracted
error InvalidGatingProof(); // Merkle proof invalid
error NoTokens(); // No NFTs provided
error DailyLimitReached(); // Daily blessing limit exceeded
error PeriodNotEnded(); // Voting period still active
error NoValidSession(); // No valid winner found
error NotAuthorized(); // Not operator or delegate
error InvalidPayment(); // Wrong payment amount
error EditionNotAvailable(); // Edition sold out
error CuratorLimitExceeded(); // Exceeded curator allocation
error ArrayLengthMismatch(); // Arrays have different lengths- Main README - Project overview and architecture
- API Reference - REST API endpoints
- Deployment Guide - Contract deployment instructions
- Smart Contract Summary - High-level contract overview
For issues or questions:
- GitHub Issues: https://github.com/your-org/abraham-api/issues
- Contract on Base Sepolia: View on BaseScan