Pool
The base Pool contract provides core functionality for grant pools and can be extended to create specific allocation strategies. Key features include:
- Pool Configuration: Manages pool settings like owner, admins, tokens, and metadata
- Project Registration: Handles project applications with pending/approved/rejected status
- Token Operations: Supports both allocation (from sender) and distribution (from contract)
- Event Emission: Emits standardized events for indexing
PoolConfig Structure
struct PoolConfig {
address owner;
address[] admins;
address allocationToken; // Token used for donations/allocations
address distributionToken; // Token used for matching funds/distributions
uint256 maxAmount; // Maximum pool funding (0 = no limit)
uint64[] timestamps; // Important dates (registration, allocation, distribution)
string metadataURI; // Pool metadata (title, description, etc.)
}
Registration Status
enum Status {
pending, // Initial state when project registers
approved, // Project approved by admin
rejected // Project rejected by admin
}
struct Registration {
Status status;
address owner; // Who registered the project
string metadataURI; // Project metadata
bytes data; // Custom data for strategy-specific info
}
Core Interface
interface IPool {
// Events
event Deployed(string name, address indexed owner, string schema, string metadataURI);
event Allocate(address indexed from, address indexed to, uint256 amount, address token, bytes data);
event Register(address indexed project, address indexed owner, string metadataURI, bytes data);
event Review(address indexed project, uint8 status, address indexed approver, string metadataURI, bytes data);
event Update(address indexed project, address indexed updater, string metadataURI, bytes data);
event Configure(address indexed updater, PoolConfig config);
// Core functions
function initialize(PoolConfig memory config, bytes memory data) external;
function configure(PoolConfig memory config) external;
function register(address project, string memory metadataURI, bytes memory data) external;
function update(address project, string memory metadataURI, bytes memory data) external;
function review(address project, uint8 status, string memory metadataURI, bytes memory data) external;
function allocate(address[] memory recipients, uint256[] memory amounts, address token, bytes[] memory data) external;
function distribute(address[] memory recipients, uint256[] memory amounts, address token, bytes[] memory data) external;
}
Key Functions
Allocate vs Distribute
allocate()
: Transfers tokens from the caller to recipients (used for donations, funding pools)distribute()
: Transfers tokens from the contract to recipients (used for distributing matching funds)
Hooks for Custom Logic
Strategies can override these internal functions to add custom validation:
_beforeAllocate()
: Called before each allocation_beforeDistribute()
: Called before each distribution
Registration System
The Pool contract includes a built-in registration system for managing project eligibility. This replaces the need for separate Registry extensions.
Registration Process
- Registration: Projects call
register()
with metadata - Review: Admins call
review()
to approve/reject applications - Status Tracking: Registrations have pending/approved/rejected status
Registration Events
event Register(address indexed project, address indexed owner, string metadataURI, bytes data);
event Review(address indexed project, uint8 status, address indexed approver, string metadataURI, bytes data);
event Update(address indexed project, address indexed updater, string metadataURI, bytes data);
Registration Structure
The Pool tracks registrations per project address:
mapping(address => Registration) public registrations;
struct Registration {
Status status; // pending, approved, or rejected
address owner; // Who registered the project
string metadataURI; // Project metadata (title, description, etc.)
bytes data; // Custom data for strategy-specific info
}
Token Operations (Allocation & Distribution)
The Pool contract provides two types of token operations:
Allocate vs Distribute
allocate()
: Transfers tokens from the caller to recipients (donations, funding pools)distribute()
: Transfers tokens from the contract to recipients (distributing matching funds)
Token Transfer Events
event Allocate(address indexed from, address indexed to, uint256 amount, address token, bytes data);
Both allocation and distribution operations emit the same Allocate
event, with from
indicating the source:
- Allocation:
from
= caller address - Distribution:
from
= contract address
Batch Operations
Both functions support batch operations to efficiently transfer to multiple recipients:
function allocate(address[] memory recipients, uint256[] memory amounts, address token, bytes[] memory data) external;
function distribute(address[] memory recipients, uint256[] memory amounts, address token, bytes[] memory data) external;
Custom Validation
Strategies can override validation hooks to enforce business logic:
function _beforeAllocate(address recipient, uint256 amount, address token, bytes memory data) internal virtual {
// Example: Only approved projects can receive allocations
require(registrations[recipient].status == Status.approved, "Recipient must be approved");
}
function _beforeDistribute(address recipient, uint256 amount, address token, bytes memory data) internal virtual {
// Example: Check contract has sufficient balance
uint256 balance = IERC20(token).balanceOf(address(this));
require(amount <= balance, "Amount exceeds balance");
}
Frontend Integration
GraphQL Queries
The indexer provides GraphQL endpoints for both registrations and allocations:
Registrations Query
{
registrations(where: { strategy_in: ["0xYourPoolAddress"] }) {
items {
id
address # Project address
owner # Who registered
metadata # Fetched metadataURI content
review # Review metadata (if approved/rejected)
isApproved # Boolean status
status # "pending", "approved", "rejected"
createdAt
updatedAt
# Token transfers to this project
allocations {
items {
from
to
amount
amountInUSD
token
}
}
}
}
}
Allocations Query
{
allocations(where: { strategy_in: ["0xYourPoolAddress"] }) {
items {
from
to
amount
amountInUSD # Indexer fetches token price
token # { address, symbol, decimals }
data
createdAt
registration {
address
metadata
isApproved
}
}
}
}
React Hooks
Registration Hooks
import { useRegistrations, useRegister, useReview } from "~/hooks/use-indexer";
export function RegistrationExample() {
const { YourPool } = useContracts();
// List all projects (initial registrations)
const projects = useRegistrations({
where: {
strategy_in: [YourPool?.address],
// Add any additional filters
},
});
// Register new project
const register = useRegister({ strategyAddress: YourPool?.address });
// Review applications (admin only)
const review = useReview({ strategyAddress: YourPool?.address });
return (
<div>
{projects.data?.items.map(project => (
<div key={project.address}>
<h3>{project.metadata?.title}</h3>
<p>Status: {project.status}</p>
{project.status === 'pending' && (
<button onClick={() => review.mutate({
project: project.address,
status: 1, // approved
metadataURI: '',
data: '0x'
})}>
Approve
</button>
)}
</div>
))}
</div>
);
}
Allocation Hooks
import { useAllocations, useAllocate } from "~/hooks/use-indexer";
export function AllocationExample() {
const { YourPool } = useContracts();
// Get donations (from users to projects)
const donations = useAllocations({
where: {
strategy_in: [YourPool?.address],
from_not_in: [YourPool?.address], // Exclude distributions from pool
},
});
// Get distributions (from pool to projects)
const distributions = useAllocations({
where: {
strategy_in: [YourPool?.address],
from_in: [YourPool?.address], // Only distributions from pool
},
});
// Allocate tokens
const allocate = useAllocate({ strategyAddress: YourPool?.address });
return (
<div>
<h3>Recent Donations</h3>
{donations.data?.items.map(allocation => (
<div key={allocation.id}>
{allocation.amount} {allocation.token.symbol} to {allocation.to}
</div>
))}
</div>
);
}
Components
RegistrationForm
Register projects or submit applications:
import { RegistrationForm } from "~/components/registration/registration-form";
export default function RegisterPage() {
const router = useRouter();
const { YourPool } = useContracts();
return (
<RegistrationForm
strategyAddress={YourPool?.address}
onSuccess={({ project }) => router.push(`/project/${project}`)}
/>
);
}
ApplicationsList
Show pending applications with approve/reject buttons:
import { ApplicationsList } from "~/components/registration/applications-list";
export default function AdminPage() {
const { YourPool } = useContracts();
return (
<ApplicationsList
query={{
where: {
strategy_in: [YourPool?.address],
isApproved: false,
},
}}
/>
);
}
ProjectsList
Display approved projects in a grid:
import { ProjectsList } from "~/components/registration/projects-list";
export default function ProjectsPage() {
const { YourPool } = useContracts();
return (
<ProjectsList
query={{
where: {
strategy_in: [YourPool?.address],
isApproved: true,
},
}}
/>
);
}
AllocationForm
Allocate tokens to projects in cart:
import { AllocationForm } from "~/components/allocation/allocation-form";
export default function CheckoutPage() {
const { YourPool, ERC20Mock } = useContracts();
return (
<AllocationForm
strategyAddress={YourPool?.address}
tokenAddress={ERC20Mock?.address}
/>
);
}
AllocationsTable
Display token transfers in a table:
import { AllocationsTable } from "~/components/allocation/allocations-table";
export default function ProjectDetailsPage() {
const { project } = useParams();
const { YourPool } = useContracts();
return (
<AllocationsTable
query={{
where: {
strategy_in: [YourPool?.address],
to: project,
},
}}
/>
);
}
Legacy GraphQL Query (from original Pool docs)
{
# Note the spelling of "strategys" here (Ponder pluralizes by just adding an "s")
strategys(where: {}) {
items {
address
name
createdAt
# Projects and Applications
registrations(where: {}) {
items {
address
index
metadata
review
isApproved
createdAt
updatedAt
# Token transfers to this project
allocations {
items {
# See Allocations query
}
}
}
}
# Token transfers in Strategy
allocations(where: {}) {
items {
from
to
token
amount
amountInUSD
registration {
# See Registrations query
}
}
}
}
}
}