Overview
These recipes show specific allocation management patterns you can implement. Each pattern includes the allocation setup, frontend handling, and contract enforcement code.Reserved + Overflow Pool
Use case: Give strategic investors guaranteed allocations, then open remaining tokens to public.Setup
Copy
// Phase 1: Strategic investors with reserved allocations
const strategicAllocations = [
{ wallet: "0xabc...", reserved: "100000000000", max: "100000000000" }, // $100k reserved (full)
{ wallet: "0xdef...", reserved: "50000000000", max: "75000000000" }, // $50k reserved, $75k max
{ wallet: "0x123...", reserved: "25000000000", max: "50000000000" } // $25k reserved, $50k max
];
// Total reserved: $175k
// If only $150k is purchased by strategic investors, $25k flows to public pool
// Phase 2: Public gets remaining + overflow
const publicPool = {
totalSaleCap: "1000000000000", // $1M total sale
reservedForStrategic: "175000000000", // $175k reserved
availableForPublic: "825000000000" + overflow // $825k + any strategic undersubscription
};
Frontend Implementation
Copy
function ReservedOverflowSale() {
const [phase, setPhase] = useState<"strategic" | "public">("strategic");
const [overflowAmount, setOverflowAmount] = useState(0n);
useEffect(() => {
// Check current phase and calculate overflow
const checkPhaseAndOverflow = async () => {
const currentPhase = await getCurrentSalePhase();
setPhase(currentPhase);
if (currentPhase === "public") {
// Calculate how much strategic investors didn't use
const totalReserved = 175000000000n; // $175k
const strategicPurchased = await getStrategicPurchaseTotal();
const overflow = totalReserved - strategicPurchased;
setOverflowAmount(overflow);
}
};
checkPhaseAndOverflow();
}, []);
if (phase === "strategic") {
return <StrategicPhase />;
}
return <PublicPhaseWithOverflow overflowAmount={overflowAmount} />;
}
function PublicPhaseWithOverflow({ overflowAmount }) {
const basePublicPool = 825000000000n; // $825k base
const totalPublicPool = basePublicPool + overflowAmount;
return (
<div>
<div className="mb-4 p-4 bg-blue-50 rounded-lg">
<h3 className="text-blue-800 font-medium">Public Sale Open</h3>
<div className="mt-2 space-y-1 text-sm">
<div className="flex justify-between">
<span>Base Public Pool:</span>
<span>${formatUSDC(basePublicPool)}</span>
</div>
{overflowAmount > 0n && (
<div className="flex justify-between text-green-600">
<span>Overflow from Strategic:</span>
<span>+${formatUSDC(overflowAmount)}</span>
</div>
)}
<div className="flex justify-between font-medium border-t pt-1">
<span>Total Available:</span>
<span>${formatUSDC(totalPublicPool)}</span>
</div>
</div>
</div>
<FCFSPurchaseFlow totalPool={totalPublicPool} />
</div>
);
}
Contract Pattern
Copy
contract ReservedOverflowSale is ExampleSale {
uint256 public constant TOTAL_SALE_CAP = 1_000_000_000_000; // $1M
uint256 public constant STRATEGIC_RESERVED = 175_000_000_000; // $175k
uint256 public strategicPurchased = 0;
uint256 public publicPurchased = 0;
enum Phase { Strategic, Public }
Phase public currentPhase = Phase.Strategic;
error StrategicPhaseEnded();
error ExceedsPublicPool(uint256 attempted, uint256 available);
error AlreadyInPublicPhase();
constructor(bytes16 _saleUUID, address _signer)
ExampleSale(_saleUUID, _signer) {}
function purchase(
uint256 amount,
PurchasePermitWithAllocation calldata purchasePermit,
bytes calldata purchasePermitSignature
) external override {
_validatePurchasePermit(purchasePermit, purchasePermitSignature);
if (currentPhase == Phase.Strategic) {
_handleStrategicPurchase(amount, purchasePermit);
} else {
_handlePublicPurchase(amount, purchasePermit);
}
}
function _handleStrategicPurchase(uint256 amount, PurchasePermitWithAllocation calldata purchasePermit) internal {
uint256 newTotalAmount = amountByAddress[msg.sender] + amount;
// Standard permit validation
if (newTotalAmount < purchasePermit.minAmount) {
revert AmountBelowMinimum(newTotalAmount, purchasePermit.minAmount);
}
if (newTotalAmount > purchasePermit.maxAmount) {
revert AmountExceedsMaximum(newTotalAmount, purchasePermit.maxAmount);
}
_trackEntity(purchasePermit.permit.entityID, msg.sender);
amountByAddress[msg.sender] = newTotalAmount;
strategicPurchased += amount;
emit Purchased(msg.sender, purchasePermit.permit.entityID, amount, newTotalAmount);
}
function _handlePublicPurchase(uint256 amount, PurchasePermitWithAllocation calldata purchasePermit) internal {
// Calculate available public pool (base + strategic overflow)
uint256 strategicOverflow = STRATEGIC_RESERVED - strategicPurchased;
uint256 basePublicPool = TOTAL_SALE_CAP - STRATEGIC_RESERVED;
uint256 totalPublicAvailable = basePublicPool + strategicOverflow;
if (publicPurchased + amount > totalPublicAvailable) {
revert ExceedsPublicPool(publicPurchased + amount, totalPublicAvailable - publicPurchased);
}
uint256 newTotalAmount = amountByAddress[msg.sender] + amount;
// Standard permit validation
if (newTotalAmount < purchasePermit.minAmount) {
revert AmountBelowMinimum(newTotalAmount, purchasePermit.minAmount);
}
if (newTotalAmount > purchasePermit.maxAmount) {
revert AmountExceedsMaximum(newTotalAmount, purchasePermit.maxAmount);
}
_trackEntity(purchasePermit.permit.entityID, msg.sender);
amountByAddress[msg.sender] = newTotalAmount;
publicPurchased += amount;
emit Purchased(msg.sender, purchasePermit.permit.entityID, amount, newTotalAmount);
}
function switchToPublicPhase() external {
if (currentPhase != Phase.Strategic) {
revert AlreadyInPublicPhase();
}
currentPhase = Phase.Public;
}
function getAvailablePublicPool() external view returns (uint256) {
if (currentPhase == Phase.Strategic) return 0;
uint256 strategicOverflow = STRATEGIC_RESERVED - strategicPurchased;
uint256 basePublicPool = TOTAL_SALE_CAP - STRATEGIC_RESERVED;
return basePublicPool + strategicOverflow - publicPurchased;
}
}
Dynamic Allocation Increases
Use case: Reward active participants by increasing their allocations during the sale.Setup
Copy
// Initial conservative allocations
const initialAllocations = [
{ wallet: "0x111...", max: "5000000000" }, // $5k initial
{ wallet: "0x222...", max: "10000000000" }, // $10k initial
{ wallet: "0x333...", max: "2500000000" } // $2.5k initial
];
// Triggers for allocation increases
const increaseTriggers = {
earlyParticipant: {
condition: "purchase_within_first_hour",
increase: "5000000000" // +$5k bonus
},
largeVolume: {
condition: "purchase_over_threshold",
threshold: "2500000000", // $2.5k+
increase: "7500000000" // +$7.5k bonus
},
referral: {
condition: "successful_referral",
increase: "2500000000" // +$2.5k per referral
}
};
Frontend Implementation
Copy
function DynamicAllocationSale() {
const [currentAllocation, setCurrentAllocation] = useState(null);
const [allocationHistory, setAllocationHistory] = useState([]);
const [eligibleBonuses, setEligibleBonuses] = useState([]);
useEffect(() => {
// Fetch current allocation and check for bonuses
const checkAllocationStatus = async () => {
const allocation = await sonarClient.fetchAllocation(saleUUID, walletAddress);
setCurrentAllocation(allocation);
// Check if user is eligible for bonuses
const bonuses = await checkEligibleBonuses(walletAddress);
setEligibleBonuses(bonuses);
// Fetch allocation history
const history = await getAllocationHistory(walletAddress);
setAllocationHistory(history);
};
checkAllocationStatus();
}, []);
return (
<div className="space-y-4">
{/* Current allocation display */}
<div className="p-4 bg-green-50 rounded-lg">
<h3 className="text-green-800 font-medium">Your Current Allocation</h3>
<p className="text-2xl font-bold text-green-700">
${formatUSDC(currentAllocation?.MaxAmountUSD)}
</p>
</div>
{/* Allocation increase opportunities */}
{eligibleBonuses.length > 0 && (
<AllocationBonusPanel bonuses={eligibleBonuses} />
)}
{/* Allocation history */}
<AllocationHistoryPanel history={allocationHistory} />
<PurchaseForm maxAmount={currentAllocation?.MaxAmountUSD} />
</div>
);
}
function AllocationBonusPanel({ bonuses }) {
return (
<div className="p-4 bg-purple-50 rounded-lg">
<h3 className="text-purple-800 font-medium">🎉 Allocation Bonuses Available!</h3>
<div className="mt-2 space-y-2">
{bonuses.map((bonus, index) => (
<div key={index} className="flex justify-between items-center">
<span className="text-purple-700">{bonus.description}</span>
<span className="font-medium text-purple-800">
+${formatUSDC(bonus.amount)}
</span>
</div>
))}
</div>
<button
className="mt-3 px-4 py-2 bg-purple-600 text-white rounded"
onClick={claimAllocationBonuses}
>
Claim Bonuses
</button>
</div>
);
}
function AllocationHistoryPanel({ history }) {
if (history.length <= 1) return null;
return (
<div className="p-4 bg-gray-50 rounded-lg">
<h3 className="text-gray-800 font-medium">Allocation History</h3>
<div className="mt-2 space-y-1">
{history.map((entry, index) => (
<div key={index} className="flex justify-between text-sm">
<span className="text-gray-600">{entry.timestamp}</span>
<span className="text-gray-700">{entry.reason}</span>
<span className="font-medium text-green-600">
${formatUSDC(entry.newMax)}
</span>
</div>
))}
</div>
</div>
);
}
Backend Logic (Conceptual)
Copy
// API endpoint to handle allocation increases
async function updateAllocation(walletAddress: string, reason: string, increase: string) {
const currentAllocation = await getAllocation(walletAddress);
const newMaxAmount = (BigInt(currentAllocation.MaxAmountUSD) + BigInt(increase)).toString();
// Update allocation in Sonar dashboard
await sonarAdmin.updateAllocation({
walletAddress,
maxAmount: newMaxAmount,
reason: reason // For audit trail
});
// Log the change
await logAllocationChange({
walletAddress,
oldMax: currentAllocation.MaxAmountUSD,
newMax: newMaxAmount,
reason,
timestamp: new Date().toISOString()
});
return { success: true, newMaxAmount };
}
// Automatic triggers
async function checkAndApplyBonuses(walletAddress: string, purchaseAmount: string) {
const purchaseTime = Date.now();
const saleStartTime = await getSaleStartTime();
// Early participant bonus
if (purchaseTime - saleStartTime < 3600000) { // Within 1 hour
await updateAllocation(walletAddress, "Early participant bonus", "5000000000");
}
// Large volume bonus
if (BigInt(purchaseAmount) >= BigInt("2500000000")) { // $2.5k+
await updateAllocation(walletAddress, "Large volume bonus", "7500000000");
}
// Check referral bonuses
const referrals = await getSuccessfulReferrals(walletAddress);
if (referrals.length > 0) {
const referralBonus = (BigInt("2500000000") * BigInt(referrals.length)).toString();
await updateAllocation(walletAddress, `Referral bonus (${referrals.length} referrals)`, referralBonus);
}
}
Graduated Allocation Tiers
Use case: Different allocation sizes based on investor categories or participation levels.Setup
Copy
// Tier definitions
const allocationTiers = {
whale: {
minPurchase: "100000000000", // $100k minimum
maxAllocation: "1000000000000", // $1M maximum
reserved: "100000000000", // $100k reserved
description: "Institutional/Whale Tier"
},
strategic: {
minPurchase: "25000000000", // $25k minimum
maxAllocation: "100000000000", // $100k maximum
reserved: "25000000000", // $25k reserved
description: "Strategic Investor Tier"
},
premium: {
minPurchase: "5000000000", // $5k minimum
maxAllocation: "25000000000", // $25k maximum
reserved: "0", // No guaranteed allocation
description: "Premium Tier"
},
standard: {
minPurchase: "1000000000", // $1k minimum
maxAllocation: "5000000000", // $5k maximum
reserved: "0", // No guaranteed allocation
description: "Standard Tier"
}
};
// Assign tiers to wallets
const tierAssignments = [
{ wallet: "0xaaa...", tier: "whale" },
{ wallet: "0xbbb...", tier: "strategic" },
{ wallet: "0xccc...", tier: "premium" },
{ wallet: "0xddd...", tier: "standard" }
];
Frontend Implementation
Copy
function TieredAllocationSale() {
const [userTier, setUserTier] = useState(null);
const [allocation, setAllocation] = useState(null);
const [tierInfo, setTierInfo] = useState(null);
useEffect(() => {
const loadTierInfo = async () => {
// Fetch user's tier and allocation
const [tier, alloc] = await Promise.all([
getUserTier(walletAddress),
sonarClient.fetchAllocation(saleUUID, walletAddress)
]);
setUserTier(tier);
setAllocation(alloc);
setTierInfo(allocationTiers[tier]);
};
loadTierInfo();
}, []);
if (!userTier || !tierInfo) {
return <div>Loading tier information...</div>;
}
return (
<div className="space-y-4">
{/* Tier badge */}
<TierBadge tier={userTier} tierInfo={tierInfo} />
{/* Allocation details */}
<TierAllocationDisplay allocation={allocation} tierInfo={tierInfo} />
{/* Purchase form with tier-specific constraints */}
<TierPurchaseForm
allocation={allocation}
tierInfo={tierInfo}
tier={userTier}
/>
{/* Tier comparison (help users understand their tier) */}
<TierComparisonPanel currentTier={userTier} />
</div>
);
}
function TierBadge({ tier, tierInfo }) {
const tierColors = {
whale: "bg-purple-100 text-purple-800 border-purple-200",
strategic: "bg-blue-100 text-blue-800 border-blue-200",
premium: "bg-green-100 text-green-800 border-green-200",
standard: "bg-gray-100 text-gray-800 border-gray-200"
};
const tierEmojis = {
whale: "🐋",
strategic: "⭐",
premium: "💎",
standard: "🎯"
};
return (
<div className={`inline-flex items-center px-3 py-1 rounded-full border ${tierColors[tier]}`}>
<span className="mr-2">{tierEmojis[tier]}</span>
<span className="font-medium">{tierInfo.description}</span>
</div>
);
}
function TierAllocationDisplay({ allocation, tierInfo }) {
return (
<div className="p-4 bg-gradient-to-r from-blue-50 to-purple-50 rounded-lg">
<h3 className="font-medium text-gray-800 mb-3">Your Tier Allocation</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-600">Maximum Allocation</p>
<p className="text-xl font-bold text-gray-800">
${formatUSDC(allocation.MaxAmountUSD)}
</p>
</div>
<div>
<p className="text-sm text-gray-600">Minimum Purchase</p>
<p className="text-xl font-bold text-gray-800">
${formatUSDC(tierInfo.minPurchase)}
</p>
</div>
{allocation.ReservedAmountUSD !== "0" && (
<div className="col-span-2">
<p className="text-sm text-gray-600">Reserved (Guaranteed)</p>
<p className="text-lg font-semibold text-green-700">
${formatUSDC(allocation.ReservedAmountUSD)}
</p>
</div>
)}
</div>
</div>
);
}
function TierPurchaseForm({ allocation, tierInfo, tier }) {
const [purchaseAmount, setPurchaseAmount] = useState("");
// Suggest tier-appropriate amounts
const suggestedAmounts = {
whale: ["100000", "250000", "500000", "1000000"],
strategic: ["25000", "50000", "75000", "100000"],
premium: ["5000", "10000", "15000", "25000"],
standard: ["1000", "2500", "3500", "5000"]
};
return (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium mb-2">
Purchase Amount (USD)
</label>
{/* Tier-specific quick amounts */}
<div className="flex flex-wrap gap-2 mb-3">
{suggestedAmounts[tier].map(amount => (
<button
key={amount}
onClick={() => setPurchaseAmount(amount)}
className="px-3 py-1 text-sm bg-gray-100 rounded hover:bg-gray-200"
>
${formatUSDC(amount + "000000")}
</button>
))}
</div>
<input
type="number"
value={purchaseAmount}
onChange={(e) => setPurchaseAmount(e.target.value)}
min={formatUSDC(tierInfo.minPurchase)}
max={formatUSDC(allocation.MaxAmountUSD)}
className="w-full px-3 py-2 border rounded"
/>
</div>
<button className="w-full py-3 bg-blue-600 text-white rounded font-medium">
Purchase Tokens
</button>
</div>
);
}
function TierComparisonPanel({ currentTier }) {
return (
<div className="p-4 bg-gray-50 rounded-lg">
<h4 className="font-medium mb-3">Tier Comparison</h4>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b">
<th className="text-left py-2">Tier</th>
<th className="text-right py-2">Min Purchase</th>
<th className="text-right py-2">Max Allocation</th>
<th className="text-right py-2">Reserved</th>
</tr>
</thead>
<tbody>
{Object.entries(allocationTiers).map(([tierName, info]) => (
<tr
key={tierName}
className={`border-b ${tierName === currentTier ? 'bg-blue-50 font-medium' : ''}`}
>
<td className="py-2">{info.description}</td>
<td className="text-right py-2">${formatUSDC(info.minPurchase)}</td>
<td className="text-right py-2">${formatUSDC(info.maxAllocation)}</td>
<td className="text-right py-2">
{info.reserved === "0" ? "None" : `$${formatUSDC(info.reserved)}`}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
Allocation Transfer/Marketplace
Use case: Allow allocation holders to transfer unused allocations to other eligible participants.Setup
Copy
// Enable allocation transfers in sale configuration
const transferConfig = {
allowAllocationTransfers: true,
transferFeePercentage: 250, // 2.5% transfer fee
minTransferAmount: "1000000000", // $1k minimum
transferDeadline: "2024-12-31T23:59:59Z", // Transfers must complete before sale ends
requiresApproval: false // Auto-approve transfers vs manual review
};
Frontend Implementation
Copy
function AllocationMarketplace() {
const [userAllocation, setUserAllocation] = useState(null);
const [availableTransfers, setAvailableTransfers] = useState([]);
const [showTransferModal, setShowTransferModal] = useState(false);
useEffect(() => {
const loadMarketplace = async () => {
const [allocation, transfers] = await Promise.all([
sonarClient.fetchAllocation(saleUUID, walletAddress),
fetchAvailableAllocationTransfers(saleUUID)
]);
setUserAllocation(allocation);
setAvailableTransfers(transfers);
};
loadMarketplace();
}, []);
return (
<div className="space-y-6">
{/* User's allocation status */}
<UserAllocationPanel
allocation={userAllocation}
onCreateTransfer={() => setShowTransferModal(true)}
/>
{/* Available allocations to purchase */}
<AvailableTransfersPanel transfers={availableTransfers} />
{/* Transfer creation modal */}
{showTransferModal && (
<CreateTransferModal
allocation={userAllocation}
onClose={() => setShowTransferModal(false)}
/>
)}
</div>
);
}
function UserAllocationPanel({ allocation, onCreateTransfer }) {
const remainingAllocation = BigInt(allocation.MaxAmountUSD) - getCurrentPurchased();
return (
<div className="p-4 bg-blue-50 rounded-lg">
<h3 className="text-blue-800 font-medium mb-3">Your Allocation</h3>
<div className="grid grid-cols-2 gap-4 mb-4">
<div>
<p className="text-sm text-blue-600">Total Allocation</p>
<p className="text-lg font-semibold">${formatUSDC(allocation.MaxAmountUSD)}</p>
</div>
<div>
<p className="text-sm text-blue-600">Remaining</p>
<p className="text-lg font-semibold">${formatUSDC(remainingAllocation)}</p>
</div>
</div>
{remainingAllocation > 0n && (
<div className="flex gap-2">
<button className="px-4 py-2 bg-blue-600 text-white rounded">
Purchase Tokens
</button>
<button
onClick={onCreateTransfer}
className="px-4 py-2 bg-gray-600 text-white rounded"
>
Transfer Allocation
</button>
</div>
)}
</div>
);
}
function AvailableTransfersPanel({ transfers }) {
return (
<div className="space-y-4">
<h3 className="font-medium">Available Allocation Transfers</h3>
{transfers.length === 0 ? (
<div className="p-8 text-center text-gray-500">
No allocation transfers available at this time.
</div>
) : (
<div className="grid gap-4">
{transfers.map(transfer => (
<TransferCard key={transfer.id} transfer={transfer} />
))}
</div>
)}
</div>
);
}
function TransferCard({ transfer }) {
const handlePurchaseTransfer = async () => {
try {
await purchaseAllocationTransfer(transfer.id);
// Refresh page or update state
} catch (error) {
console.error("Transfer purchase failed:", error);
}
};
return (
<div className="p-4 border rounded-lg">
<div className="flex justify-between items-start mb-2">
<div>
<p className="font-medium">${formatUSDC(transfer.amount)} Allocation</p>
<p className="text-sm text-gray-600">
Price: ${formatUSDC(transfer.price)}
<span className="text-green-600 ml-1">
({((Number(transfer.price) / Number(transfer.amount) - 1) * 100).toFixed(1)}% premium)
</span>
</p>
</div>
<div className="text-right">
<p className="text-sm text-gray-500">Expires in</p>
<p className="font-medium">{formatTimeRemaining(transfer.expiresAt)}</p>
</div>
</div>
<div className="flex justify-between items-center">
<div className="text-xs text-gray-500">
Seller: {shortenAddress(transfer.sellerAddress)}
</div>
<button
onClick={handlePurchaseTransfer}
className="px-4 py-2 bg-green-600 text-white rounded text-sm"
>
Purchase Transfer
</button>
</div>
</div>
);
}
function CreateTransferModal({ allocation, onClose }) {
const [transferAmount, setTransferAmount] = useState("");
const [transferPrice, setTransferPrice] = useState("");
const remainingAllocation = BigInt(allocation.MaxAmountUSD) - getCurrentPurchased();
const transferFee = BigInt(transferPrice || "0") * BigInt(250) / BigInt(10000); // 2.5%
const handleCreateTransfer = async () => {
try {
await createAllocationTransfer({
saleUUID,
amount: transferAmount,
price: transferPrice,
sellerAddress: walletAddress
});
onClose();
} catch (error) {
console.error("Transfer creation failed:", error);
}
};
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center">
<div className="bg-white p-6 rounded-lg max-w-md w-full">
<h3 className="font-medium mb-4">Create Allocation Transfer</h3>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium mb-1">
Transfer Amount (max: ${formatUSDC(remainingAllocation)})
</label>
<input
type="number"
value={transferAmount}
onChange={(e) => setTransferAmount(e.target.value)}
max={Number(formatUSDC(remainingAllocation))}
className="w-full px-3 py-2 border rounded"
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">
Sale Price (what buyer pays)
</label>
<input
type="number"
value={transferPrice}
onChange={(e) => setTransferPrice(e.target.value)}
className="w-full px-3 py-2 border rounded"
/>
</div>
{transferPrice && (
<div className="p-3 bg-gray-50 rounded text-sm">
<div className="flex justify-between">
<span>Buyer pays:</span>
<span>${formatUSDC(transferPrice + "000000")}</span>
</div>
<div className="flex justify-between">
<span>Transfer fee (2.5%):</span>
<span>-${formatUSDC(transferFee)}</span>
</div>
<div className="flex justify-between font-medium border-t pt-1">
<span>You receive:</span>
<span>${formatUSDC(BigInt(transferPrice + "000000") - transferFee)}</span>
</div>
</div>
)}
</div>
<div className="flex gap-3 mt-6">
<button
onClick={onClose}
className="flex-1 px-4 py-2 border rounded"
>
Cancel
</button>
<button
onClick={handleCreateTransfer}
className="flex-1 px-4 py-2 bg-blue-600 text-white rounded"
>
Create Transfer
</button>
</div>
</div>
</div>
);
}
See Also
Sale Patterns
More complete sale implementation patterns
Allocation Management
Core concepts for allocation tracking
Smart Contract Integration
Implement allocation enforcement in your contract
Allocation Management
Core concepts and API reference for allocations
Smart Contract Integration
Contract enforcement of allocation limits