The AbrahamSeeds contract includes a secure blessing system with on-chain eligibility enforcement. Users can show support for Seeds (artwork proposals) based on their FirstWorks NFT ownership, with all eligibility rules verified directly by the smart contract using Merkle proofs.
- FirstWorks NFT ownership (L1 Ethereum) verified via Merkle proofs on L2 (Base)
- Daily snapshots capture current NFT ownership
- Proofs generated from Merkle tree of ownership data
- Own N NFTs = N blessings per day (configurable)
- Default:
blessingsPerNFT = 1(one blessing per NFT owned) - Automatic period resets based on voting period configuration
- Prevents whale dominance: Score = √(user_blessings)
- 100 blessings = score 10, not 100
- Encourages broader participation over large holdings
Users can approve delegates (e.g., backend server) to bless on their behalf, enabling:
- Gasless transactions through backend relaying
- Better UX without wallet interaction
- Optional: users can still bless directly
Backend servers with OPERATOR_ROLE can submit verified blessings on behalf of users.
AbrahamSeeds (ERC1155)
└── EdenAgent (Base)
├── AccessControl (Roles)
├── Pausable
└── ReentrancyGuard
└── MerkleGating (Module)
└── Merkle Proof Verification
| Role | Description |
|---|---|
DEFAULT_ADMIN_ROLE |
Full admin control, grant/revoke roles |
OPERATOR_ROLE |
Submit blessings on behalf of users, select winners |
CREATOR_ROLE |
Submit seeds |
- Merkle Proof Verification: NFT ownership verified on-chain via MerkleGating module
- Daily Blessing Limits: Contract enforces N blessings per day (N = NFTs owned)
- Access Control: OpenZeppelin's
AccessControlfor role-based permissions - Reentrancy Protection:
ReentrancyGuardon all state-changing functions - Authorization Checks: Validates that only authorized parties can submit delegated blessings
Directly bless a seed with NFT ownership proof.
function blessSeed(
uint256 seedId,
uint256[] calldata tokenIds,
bytes calldata proof
) external payableRequirements:
- Must own FirstWorks NFTs (verified via Merkle proof)
- Must not have reached daily blessing limit
- Seed must exist and not be winner/retracted
Example:
import { encodeAbiParameters } from 'viem';
// User owns token IDs [42, 123, 456]
const tokenIds = [42n, 123n, 456n];
const merkleProof = ['0xabc...', '0xdef...']; // From API
// Encode proof for contract
const encodedProof = encodeAbiParameters(
[{ type: 'bytes32[]' }],
[merkleProof]
);
await walletClient.writeContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'blessSeed',
args: [seedId, tokenIds, encodedProof]
});Approve or revoke a delegate's permission to bless on your behalf.
function approveDelegate(address delegate, bool approved) externalExample:
// Approve backend server as delegate
await walletClient.writeContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'approveDelegate',
args: [backendServerAddress, true]
});
// Revoke delegate
await walletClient.writeContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'approveDelegate',
args: [backendServerAddress, false]
});Submit a blessing on behalf of a user with NFT proof verification.
function blessSeedFor(
uint256 seedId,
address blesser,
uint256[] calldata tokenIds,
bytes calldata proof
) external payableRequirements:
- Caller must have
OPERATOR_ROLEOR be approved delegate forblesser - User must own FirstWorks NFTs (verified via Merkle proof)
- User must not have reached daily blessing limit
Example (Backend):
// Backend relayer blessing on behalf of user
await operatorWallet.writeContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'blessSeedFor',
args: [seedId, userAddress, tokenIds, encodedProof]
});Get how many times a user has blessed a specific seed.
const count = await publicClient.readContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'getBlessingCount',
args: [userAddress, seedId]
});Get remaining blessings for a user today.
const remaining = await publicClient.readContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'getRemainingBlessings',
args: [userAddress, nftCount]
});Check if user can bless today.
const canBless = await publicClient.readContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'canBlessToday',
args: [userAddress, nftCount]
});The blessing system uses square root scoring to prevent whale dominance:
User Score = √(user_blessing_count) × SCALE
SCALE = 1e6 (for precision)
For each blessing:
- Get user's current blessing count for this seed
- Calculate score delta:
√(n+1) - √(n)(scaled) - Add delta to seed's total score
Example:
User has blessed seed 3 times, blesses again:
- Previous score contribution: √3 = 1.732
- New score contribution: √4 = 2.0
- Delta added: 2.0 - 1.732 = 0.268
Total Seed Score = Σ (√blessings_from_each_user)
| Blessings | Linear Score | Sqrt Score |
|---|---|---|
| 1 | 1 | 1.0 |
| 4 | 4 | 2.0 |
| 9 | 9 | 3.0 |
| 100 | 100 | 10.0 |
| 10000 | 10000 | 100.0 |
A whale with 10,000 NFTs blessing 10,000 times only gets score 100, not 10,000.
GET /api/blessings/eligibility
Authorization: Bearer <privy_token>Response:
{
"success": true,
"data": {
"eligible": true,
"nftCount": 5,
"maxBlessings": 5,
"usedBlessings": 2,
"remainingBlessings": 3,
"periodEnd": "2025-11-09T00:00:00.000Z"
}
}POST /api/blessings
Authorization: Bearer <privy_token>
Content-Type: application/json
{
"seedId": 0
}Response:
{
"success": true,
"data": {
"seedId": 0,
"txHash": "0x...",
"blessingCount": 42,
"message": "Blessing submitted successfully"
}
}POST /api/blessings/prepare
Authorization: Bearer <privy_token>
Content-Type: application/json
{
"seedId": 0
}Returns transaction data for client-side signing.
GET /api/blessings/delegation-status
Authorization: Bearer <privy_token>POST /api/blessings/prepare-delegate
Authorization: Bearer <privy_token>
Content-Type: application/json
{
"approved": true
}User API Backend Blockchain
│ │ │ │
│ 1. POST /blessings │ │ │
├──────────────────────>│ │ │
│ │ 2. Verify auth │ │
│ │ 3. Get Merkle proof │ │
│ │ 4. Check eligibility │ │
│ ├───────────────────────>│ │
│ │ │ 5. Sign tx │
│ │ ├──────────────────────>│
│ │ │ │
│ │ │ 6. Tx confirmed │
│ │ │<──────────────────────┤
│ 7. Return txHash │ │ │
│<──────────────────────┤ │ │
User API Blockchain
│ │ │
│ 1. POST /prepare │ │
├──────────────────────>│ │
│ │ 2. Build tx data │
│ 3. Return tx │ │
│<──────────────────────┤ │
│ │ │
│ 4. Sign & send tx │ │
├────────────────────────────────────────────────>│
│ │ │
│ │ 5. Tx confirmed │
│<────────────────────────────────────────────────┤
event BlessingSubmitted(
uint256 indexed seedId,
address indexed blesser,
uint256 score
);
event DelegateApproval(
address indexed user,
address indexed delegate,
bool approved
);Duration of each blessing/voting period.
// Default: 1 day (86400 seconds)
const period = await publicClient.readContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'votingPeriod'
});How many blessings each NFT grants per period.
// Default: 1 blessing per NFT
const perNFT = await publicClient.readContract({
address: SEEDS_CONTRACT,
abi: abrahamSeedsABI,
functionName: 'blessingsPerNFT'
});// Update via setConfig function
await adminWallet.writeContract({
address: SEEDS_CONTRACT,
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
}]
});- API Reference - Full API documentation
- Seeds Contract Reference - Contract function details
- Deployment Guide - Contract deployment