Contracts Reference
Complete technical reference for all core Opals Protocol contracts, including function signatures, events, and usage examples.
Core Contracts
OpalsFactory
Central registry and deployment system using EIP-1167 minimal proxies.
Address: Varies by network (see deployment documentation)
Key Functions:
deployContract
Deploy a new contract from a template.
function deployContract(
bytes32 templateId,
bytes memory initData
) external payable returns (address clone)Parameters:
templateId: Template identifier (e.g.,keccak256("PROJECT"))initData: Initialization data (ABI-encoded constructor arguments)
Returns: Address of the deployed clone
Example:
bytes memory initData = abi.encode("ProjectName", adminAddress);
address project = factory.deployContract(keccak256("PROJECT"), initData);getContractsByTemplateId
Get all contracts deployed from a specific template.
function getContractsByTemplateId(bytes32 templateId)
external view returns (address[] memory)Parameters:
templateId: Template identifier
Returns: Array of deployed contract addresses
addContractTemplate
Add a new template to the factory (operator only).
function addContractTemplate(
bytes32 templateId,
address contractAddr,
uint128 minimumFee,
address feeAddr,
uint32 integratorFeePct
) externalEvents:
event ContractCreated(
address indexed owner,
address indexed addr,
address contractTemplate
);
event ContractTemplateAdded(
address newContract,
bytes32 templateId
);Project
Central coordination hub managing relationships between all project components.
Template ID: keccak256("PROJECT")
Key Functions:
addMarket
Add a market to the project and optionally link to a card.
function addMarket(address _market, address _card) publicAccess: Operator or Admin only
Parameters:
_market: Market contract address_card: Optional card to link (useaddress(0)to skip)
Example:
IProject(project).addMarket(marketAddress, cardAddress);addClaim
Add a claim contract to the project.
function addClaim(address _claim, address _card) publicAccess: Operator or Admin only
Parameters:
_claim: Claim contract address_card: Optional card to link
batchWireProject
Batch wiring function to reduce gas usage (saves ~500,000 gas).
function batchWireProject(
address patronClaim,
address vaultClaim,
address patronCard,
address vaultCard
) externalAccess: Operator or Admin only
Gas Savings: ~500,000 gas (17% reduction vs. individual calls)
getClaims
Get all claim addresses for this project.
function getClaims() external view returns (address[] memory)Returns: Array of claim contract addresses
getMarkets
Get all market addresses for this project.
function getMarkets() external view returns (address[] memory)Returns: Array of market contract addresses
transferTokens
Transfer tokens from the project to a specified address.
function transferTokens(
address _token,
address _to,
uint256 _amount
) externalAccess: Admin only
Events:
event MarketAdded(address indexed market);
event ClaimAdded(address indexed claim);
event CardAdded(address indexed card);
event ProjectWired(
address patronClaim,
address vaultClaim,
address patronCard,
address vaultCard,
uint256 timestamp
);
event TokenSet(address indexed token);
event LauncherSet(address indexed launcher);
event DistributorSet(address indexed distributor);Token
ERC20 token with minting capabilities and 1e27 initial supply.
Template ID: keccak256("TOKEN")
Key Functions:
mint
Mint new tokens to a specified address.
function mint(address to, uint256 amount) externalAccess: MINTER_ROLE or DEFAULT_ADMIN_ROLE
Parameters:
to: Recipient addressamount: Amount to mint (in wei, 18 decimals)
Example:
IToken(token).mint(recipientAddress, 1000 * 1e18); // Mint 1000 tokensStandard ERC20 Functions:
function transfer(address to, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
function totalSupply() external view returns (uint256);Operator
Role-based access control for project operations.
Template ID: keccak256("OPERATOR")
Roles:
Admin: Full control (equivalent to project owner)
Operator: Can manage markets, claims, and cards
Moderator: Can approve/reject membership requests
Key Functions:
isAdmin
Check if an address has admin role.
function isAdmin(address account) external view returns (bool)isOperator
Check if an address has operator role.
function isOperator(address account) external view returns (bool)addOperator
Grant operator role to an address.
function addOperator(address account) externalAccess: Project or Admin only
removeOperator
Revoke operator role from an address.
function removeOperator(address account) externalAccess: Project or Admin only
Events:
event OperatorRoleGranted(address indexed account);
event OperatorRoleRemoved(address indexed account);
event ModeratorRoleGranted(address indexed account);
event ModeratorRoleRemoved(address indexed account);
event AdminRoleGranted(address indexed account);
event AdminRoleRemoved(address indexed account);Market Contracts
SteppedMarket
Batch-based NFT sales with increasing prices for bot resistance.
Template ID: keccak256("STEPPED_MARKET")
Key Functions:
collect
Purchase an NFT with optional referrer.
function collect(address referrer) external payable nonReentrantParameters:
referrer: Optional referrer address (useaddress(0)if none)
Payment: ETH sent with transaction must equal or exceed current price
Example:
uint256 price = market.getCurrentPrice();
market.collect{value: price}(referrerAddress);getCurrentPrice
Get the current price for the next NFT.
function getCurrentPrice() public view returns (uint256)Returns: Price in wei
Formula: basePrice + (priceIncrement × currentPackageId)
getCurrentPackageId
Get the current package (batch) ID.
function getCurrentPackageId() public view returns (uint256)Returns: Package ID (0-indexed)
Formula: totalSold / itemsPerPackage
emergencyBurn
Burn an NFT and receive 70% refund before trading opens.
function emergencyBurn(uint256 tokenId) external nonReentrantAccess: NFT owner only
Conditions:
Trading not yet open (before liquidity launch)
Refund is 70% of purchase price
Events:
event Collected(
address indexed buyer,
uint256 indexed tokenId,
uint256 price,
address referrer
);
event EmergencyBurned(
address indexed owner,
uint256 indexed tokenId,
uint256 refundAmount
);
event Finalized(
uint256 totalRaised,
uint256 lpAmount,
uint256 treasuryAmount
);FixedMarket
Fixed-price NFT sales for simple membership models.
Template ID: keccak256("FIXED_MARKET")
Key Functions:
collect
Purchase a fixed-price membership NFT.
function collect(address referrer) external payable nonReentrantParameters:
referrer: Optional referrer address
Payment: ETH sent must equal fixed price
MembersMarket
Membership market with approval workflow and group management.
Template ID: keccak256("MEMBERS_MARKET")
Key Functions:
requestToJoin
Request to join the membership market with prepayment.
function requestToJoin() external payable nonReentrantPayment: Full membership cost (price + fees)
approveRequest
Approve a membership request and mint NFT.
function approveRequest(address user) external nonReentrantAccess: Operator or Moderator only
rejectRequest
Reject a membership request and issue refund.
function rejectRequest(address user) external nonReentrantAccess: Operator or Moderator only
inviteMember
Allow a member to invite a new person with potential discount.
function inviteMember(address invitee) externalAccess: Existing members only
Events:
event MembershipRequested(address indexed user, uint256 amount);
event MembershipApproved(address indexed user, uint256 tokenId);
event MembershipRejected(address indexed user, uint256 refundAmount);
event MemberInvited(address indexed inviter, address indexed invitee);Claim Contracts
PatronClaim
Permanent lock claim with 10x multiplier for early supporters.
Template ID: keccak256("PATRON_CLAIM")
Key Functions:
addCardSet
Add a card set with per-card weight and max supply.
function addCardSet(
address card,
uint256 weightPerCard,
uint256 maxSupply
) externalAccess: Project operator only
Parameters:
card: Card contract addressweightPerCard: Weight allocated per cardmaxSupply: Maximum supply for this card set
Example:
IPatronClaim(patronClaim).addCardSet(
patronCardAddress,
1000, // 1000 weight per card
500 // Max 500 cards
);claimTokensForNFTs
Claim protocol fees for specific NFT token IDs.
function claimTokensForNFTs(
uint256[] calldata tokenIds,
address token
) external nonReentrantParameters:
tokenIds: Array of NFT token IDs to claim fortoken: Token address to claim (use WETH for ETH rewards)
Returns: Tokens transferred to caller
Example:
uint256[] memory myTokenIds = new uint256[](2);
myTokenIds[0] = 1;
myTokenIds[1] = 5;
IPatronClaim(patronClaim).claimTokensForNFTs(myTokenIds, wethAddress);getClaimableAmountForNFTs
Calculate claimable amount for specific NFT token IDs.
function getClaimableAmountForNFTs(
uint256[] calldata tokenIds,
address token
) external view returns (uint256)Returns: Amount claimable in wei
getPatronPower
Get PatronPower for a specific NFT (LP amount × 10x).
function getPatronPower(uint256 tokenId)
external view returns (uint256)Returns: PatronPower value
Formula: lpAmount × 10
depositRewards
Deposit protocol fees to be distributed to patron card holders.
function depositRewards() external payable nonReentrantPayment: ETH to deposit
Access: Permissionless (anyone can deposit)
Events:
event CardSetAdded(
address indexed card,
uint256 weightPerCard,
uint256 maxSupply,
uint256 index
);
event TokensDeposited(
address indexed token,
uint256 amount
);
event TokensClaimed(
address indexed claimer,
address indexed token,
uint256 amount,
uint256[] tokenIds
);VaultClaim
Flexible staking with time-based multipliers (0-5x) and OVL early exit.
Template ID: keccak256("VAULT_CLAIM")
Key Functions:
stakeLP
Stake LP tokens with a specified lock duration.
function stakeLP(
uint256 amount,
uint256 lockDuration
) external nonReentrant returns (uint256 tokenId)Parameters:
amount: LP token amount to stakelockDuration: Lock duration in seconds (7 days to 4 years, or max uint256 for permanent)
Returns: NFT token ID representing the stake
Example:
// Approve LP tokens first
IERC20(lpToken).approve(address(vaultClaim), amount);
// Stake for 1 year (1.25x multiplier)
uint256 tokenId = IVaultClaim(vaultClaim).stakeLP(
1000 * 1e18,
365 days
);unstakeLP
Unstake LP tokens after lock expiration.
function unstakeLP(uint256 tokenId) external nonReentrantParameters:
tokenId: NFT token ID to unstake
Conditions:
Lock period must have expired
Burns NFT and returns LP tokens
Grants 20% diamond hands bonus
earlyExit
Exit early with penalty before lock expires (OVL).
function earlyExit(uint256 tokenId) external nonReentrantParameters:
tokenId: NFT token ID to exit
Penalty: 0-50% based on remaining time
Formula: penalty = lpAmount × (remainingTime / totalDuration) × 50%
Example:
// Exit early (penalty applies)
IVaultClaim(vaultClaim).earlyExit(tokenId);
// Penalty redistributed to remaining stakers
// User receives reduced LP amountgetPatronPower
Get PatronPower for a staked position.
function getPatronPower(uint256 tokenId)
external view returns (uint256)Returns: PatronPower value
Formula:
Permanent:
lpAmount × 10Time-based:
lpAmount × min(5, lockDuration / 4years × 5)
claimTokensForNFTs
Claim protocol fees for staked positions.
function claimTokensForNFTs(
uint256[] calldata tokenIds,
address token
) external nonReentrantParameters:
tokenIds: Array of NFT token IDstoken: Token to claim
Events:
event LPStaked(
address indexed staker,
uint256 indexed tokenId,
uint256 amount,
uint256 lockEndTime
);
event LPUnstaked(
address indexed staker,
uint256 indexed tokenId,
uint256 amount,
uint256 bonus
);
event EarlyExit(
address indexed user,
uint256 indexed tokenId,
uint256 lpReturned,
uint256 penalty
);
event PenaltyRedistributed(
uint256 indexed tokenId,
uint256 penaltyAmount,
uint256 totalPowerBefore
);DiamondClaim
Vesting claim with penalty redistribution for early redemption.
Template ID: keccak256("DIAMOND_CLAIM")
Key Functions:
redeem
Claim tokens for one or multiple NFT token IDs.
function redeem(uint256[] calldata tokenIds) external nonReentrantParameters:
tokenIds: Array of NFT token IDs to redeem
Behavior:
Before vesting: No tokens available
During vesting: Partial tokens with penalty for early claim
After vesting: Full tokens with no penalty
Launcher Contracts
LiquidityLauncher
Accumulates tokens and launches Uniswap V2 liquidity pool.
Template ID: keccak256("LIQUIDITY_LAUNCHER")
Key Functions:
launch
Launch the liquidity pool by creating Uniswap V2 pair.
function launch() external nonReentrantAccess: Permissionless (anyone can trigger after market success)
Actions:
Creates Uniswap V2 pair (Token/WETH)
Adds liquidity with 2% slippage protection
Transfers LP tokens to PatronClaim
Permanently locks liquidity
Example:
// After market reaches success threshold
ILauncher(launcher).launch();
// Trading is now open on Uniswap
address pair = ILauncher(launcher).pair();isLaunched
Returns whether the launcher has been launched.
function isLaunched() external view returns (bool)Returns: True if liquidity has been launched
Events:
event LiquidityLaunched(
address indexed pair,
uint256 tokenAmount,
uint256 ethAmount,
uint256 lpTokens
);
event LPTokensTransferred(
address indexed recipient,
uint256 amount
);Vault Contracts
WorkLock
ETH staking with Aave V3 yield generation converted to LP.
Template ID: keccak256("WORKLOCK")
Key Functions:
stake
Stake ETH and receive a Card NFT.
function stake() external payable nonReentrant returns (uint256 tokenId)Payment: ETH to stake
Returns: NFT token ID representing stake
Actions:
Wraps ETH to WETH
Deposits WETH to Aave V3
Receives aWETH (interest-bearing)
Mints Card NFT to user
Example:
uint256 tokenId = IWorklock(worklock).stake{value: 10 ether}();unstake
Unstake ETH by burning Card NFT.
function unstake(uint256 tokenId) external nonReentrantParameters:
tokenId: Card NFT to burn
Returns: Original ETH + any unclaimed interest
zapInterest
Zap interest to LP tokens (permissionless).
function zapInterest(uint256 tokenId) external nonReentrantParameters:
tokenId: Card NFT to zap interest for
Access: Permissionless (anyone can trigger)
Actions:
Claims Aave interest
Takes 30% treasury fee
Converts 70% to LP (50% swap to token, add liquidity)
Sends LP to VaultClaim with permanent lock (10x multiplier)
Events:
event CardMinted(
address indexed staker,
uint256 indexed tokenId,
uint256 amount
);
event CardBurned(
address indexed staker,
uint256 indexed tokenId,
uint256 ethReturned
);
event InterestZappedForNFT(
uint256 indexed tokenId,
uint256 interestAmount,
uint256 lpAmount,
uint256 treasuryFee
);Distribution Contracts
Distributor
Pro-rata protocol fee distribution based on PatronPower.
Template ID: keccak256("DISTRIBUTOR")
Key Functions:
distribute
Distribute available ETH or ERC20 tokens (permissionless).
function distribute(address token) external nonReentrantParameters:
token: Token to distribute (use WETH for ETH)
Access: Permissionless
Algorithm:
Query PatronPower from PatronClaim and VaultClaim
Calculate total PatronPower
Distribute pro-rata based on power percentages
Transfer to claim contracts
Example:
// Anyone can trigger distribution
IDistributor(distributor).distribute(wethAddress);
// Fees distributed to PatronClaim and VaultClaim
// Users claim individually from their claim contractquoteShares
Quote the shares that would be distributed.
function quoteShares(address token, uint256 amount)
external view returns (uint256 patronShare, uint256 vaultShare)Parameters:
token: Token to quoteamount: Amount to distribute
Returns:
patronShare: Amount to PatronClaimvaultShare: Amount to VaultClaim
Events:
event Distribution(
address indexed token,
uint256 patronAmount,
uint256 vaultAmount,
uint256 totalAmount
);
event TokenDistribution(
address indexed token,
uint256 patronAmount,
uint256 vaultAmount
);EthRewarder
Permissionless donation model for protocol fee distribution.
Key Functions:
deposit
Deposit ETH to a recipient's balance.
function deposit(address recipient, string calldata reason)
external payableParameters:
recipient: Address to receive ETHreason: Human-readable reason for deposit
Access: Permissionless
withdraw
Withdraw ETH balance.
function withdraw(address to) external nonReentrantParameters:
to: Address to receive withdrawn ETH
Access: Anyone can withdraw their own balance
Events:
event Deposit(
address indexed from,
address indexed to,
uint256 amount,
string reason
);
event Withdraw(
address indexed from,
address indexed to,
uint256 amount
);Usage Examples
Complete Project Deployment
// Deploy using OpalsRecipe
OpalsRecipe.ProjectCreationArgs memory args = OpalsRecipe.ProjectCreationArgs({
owner: msg.sender,
projectName: "MyProject",
tokenName: "MyToken",
tokenSymbol: "MYT",
tokenInitialSupply: 1e27,
launcherWethReserve: 0,
launcherTokenReserve: 1e26,
launcherTokenPriceInWei: 0,
launcherTrigger: address(0),
launcherTarget: 10 ether,
patronCardImageURI: "ipfs://...",
patronCardDescriptorURI: "",
patronCardBaseURI: "ipfs://...",
vaultCardImageURI: "ipfs://...",
vaultCardDescriptorURI: "",
vaultCardBaseURI: "ipfs://..."
});
OpalsRecipe.ProjectCreationResult memory result = recipe.createProject(args);User Flow: Purchase → Stake → Claim
// 1. User purchases NFT
uint256 price = market.getCurrentPrice();
market.collect{value: price}(referrer);
// 2. Market succeeds, liquidity launches
launcher.launch();
// 3. User stakes LP tokens
lpToken.approve(address(vaultClaim), amount);
uint256 tokenId = vaultClaim.stakeLP(amount, 365 days);
// 4. Protocol fees accumulate
distributor.distribute(wethAddress);
// 5. User claims rewards
uint256[] memory tokenIds = new uint256[](1);
tokenIds[0] = tokenId;
vaultClaim.claimTokensForNFTs(tokenIds, wethAddress);Monitoring Project Health
// Check market progress
uint256 sold = market.totalSold();
uint256 maxSupply = market.maxSupply();
bool successful = market.isSuccessful();
// Check liquidity status
bool launched = launcher.isLaunched();
address pair = launcher.pair();
// Check claim statistics
uint256 totalPatronPower = patronClaim.totalPatronPower();
uint256 totalVaultPower = vaultClaim.totalPatronPower();
// Check pending rewards
uint256 claimable = patronClaim.getClaimableAmountForNFT(tokenId, token);Next Steps
Review Architecture Overview for system design
Study Integration Guide for deployment patterns
Check Security for best practices
Explore test suite in
/testfor more examples
Last updated