Allocator
The Allocator comes with functions to allocate tokens to a collection of recipients. We use arrays for the public function to simplify token transfers to multiple recipients.
- Allocate - tokens going from sender to a recipient
- Distribute - tokens going from strategy contract to a recipient
event Allocate(address indexed from, address indexed to, uint256 amount, address token, bytes data);
function allocate(address[] calldata recipients, uint256[] calldata amounts, address token, bytes[] calldata data) public virtual {}
function _allocate(address to, uint256 amount, address token, bytes memory data) internal virtual internal virtual nonReentrant {}
function distribute(address[] calldata recipients, uint256[] calldata amounts, address token, bytes[] calldata data) public virtual {}
function _distribute(address to, uint256 amount, address token, bytes memory data) internal virtual internal virtual nonReentrant {}
The reason the allocate function contains an array of recipients and amounts is because we want to be able to send to many in one transaction without using Multicall.
Never approve Multicall3 to spend your tokens. https://github.com/mds1/multicall?tab=readme-ov-file#security
Examples
// Only Vote token is allowed and projects must be approved
contract VoteStrategy is Allocator {
function _allocate(address recipient, uint256 amount, address token, bytes memory)
internal override {
require(token == voteToken, "Must be vote token");
require(projects[recipient][0].status == IRegistry.Status.approved, "Project must be approved");
super._allocate(recipient, amount, token, "");
}
}
// Fund Strategy with tokens
contract MatchingFundsStrategy is Allocator, Ownable {
function fund(uint256 amount, address token) public {
require(token == matchingToken, "Must be vote token or matching token");
super._allocate(address(this), amount, token, "");
}
// Calculate distributions off-chain and call this to distributes
function distributeMatching(address[] calldata recipients, uint256[] calldata amounts, address token, bytes[] calldata data) public onlyOwner {
require(token == matchingToken, "Must be vote token or matching token");
super.distribute(recipients, amounts, token, data);
}
}
Indexer GraphQL Query
{
allocations(where: { strategy_in_: ["0xYourContract"] }) {
items {
to
from
amount
amountInUSD # Indexer fetches token price
token # { token: address, symbol: "ETH", decimals: 18 }
}
}
}
Hooks
const donations = useAllocations({
where: {
strategy_in: ["0xYourContract"],
from_not_in: ["0xYourContract"], // Exclude matching distributions
},
});
const matchingDistributions = useAllocations({
where: {
strategy_in: ["0xYourContract"],
from_in: ["0xYourContract"],
},
});
Components
AllocationForm
The AllocationForm component can be used to transfer tokens to projects added to a Cart
import { AllocationForm } from "~/components/allocation/allocation-form";
export default function CheckoutPage() {
const { YourStrategy } = useContracts();
const erc20address = "0xTokenAddress";
return (
<AllocationForm
defaultValues={{}}
strategyAddress={YourStrategy?.address}
tokenAddress={erc20address}
/>
);
}
AllocationsTable
The AllocationsTable component renders a table of token tranfers.
import { AllocationForm } from "~/components/allocation/allocation-form";
export default function ProjectDetailsPage() {
const { YourStrategy } = useContracts();
return (
<AllocationsTable
query={{
where: {
strategy_in: [YourContract?.address],
to: "0xProjectAddress",
},
}}
/>
);
}
Last updated on