Fabricator Contract
The Vidya Fabricator contract enables complex crafting mechanics by consuming multiple input tokens (ERC1155, ERC20, and ETH) to produce output items.
Contract Interface
Core Structures
struct Recipe {
MintItem mintItem; // Output item to mint
address creator; // Recipe creator (receives transfers)
Item1155[] items1155; // ERC1155 input requirements
Item20[] items20; // ERC20/ETH input requirements
}
struct MintItem {
address contractAddress; // Output token contract
uint256 id; // Output token ID
uint256 amount; // Output amount
}
struct Item1155 {
address contractAddress; // Input token contract
uint256 id; // Input token ID
uint256 amount; // Required amount
bool burn; // Burn (true) or transfer (false)
}
struct Item20 {
address contractAddress; // Token contract (if not native ETH)
uint256 amount; // Required amount
bool native; // True for ETH, false for ERC20
}
Functions
State-Changing Functions
addRecipe(Recipe memory _recipe)
function addRecipe(Recipe memory _recipe) external
Creates a new crafting recipe. Requires ADMIN_ROLE on the output token contract.
Parameters:
_recipe: Recipe struct containing all crafting requirements and outputs
Requirements:
- Caller must have ADMIN_ROLE on
_recipe.mintItem.contractAddress - Fabricator must have MINTER_ROLE on output contract
- Maximum 20 ERC1155 inputs
- Maximum 20 ERC20/ETH inputs
- Creator address must be set
- At least one input required
removeRecipe(uint256 _recipeId)
function removeRecipe(uint256 _recipeId) external
Removes a recipe by swapping with the last recipe. Requires ADMIN_ROLE.
Parameters:
_recipeId: ID of recipe to remove
adjustRecipe(uint256 _recipeId, Recipe memory _recipe)
function adjustRecipe(uint256 _recipeId, Recipe memory _recipe) external
Updates an existing recipe. Requires ADMIN_ROLE.
Parameters:
_recipeId: ID of recipe to update_recipe: New recipe data
fabricate(uint256 _recipeId)
function fabricate(uint256 _recipeId) external payable
Executes a recipe to craft items.
Parameters:
_recipeId: Recipe to execute
Process:
- Validates all input balances
- Burns or transfers ERC1155 inputs based on recipe
- Transfers ERC20 inputs to creator
- Transfers ETH to creator (if required)
- Mints output items to caller
- Refunds excess ETH
batchFabricate(uint256[] calldata _recipeIds)
function batchFabricate(uint256[] calldata _recipeIds) external payable
Executes multiple recipes in one transaction.
Parameters:
_recipeIds: Array of recipe IDs to execute
Requirements:
- No duplicate recipe IDs
- Sufficient inputs for all recipes
- Exact or excess ETH for total requirements
View Functions
getRecipeDetails(uint256 _recipeId)
function getRecipeDetails(uint256 _recipeId) external view returns (
MintItem memory mintItem,
address creator,
Item1155[] memory items1155,
Item20[] memory items20
)
Returns complete recipe information.
getRecipeRequirements(uint256 _recipeId)
function getRecipeRequirements(uint256 _recipeId) external view returns (
uint256 totalEthRequired,
Item1155[] memory items1155,
Item20[] memory items20
)
Returns input requirements for a recipe.
willFabricate(uint256 _recipeId, address _user)
function willFabricate(uint256 _recipeId, address _user) external view returns (
bool canFabricate,
string memory reason
)
Checks if a user can execute a recipe.
Returns:
canFabricate: Whether user has sufficient inputsreason: Human-readable explanation
recipeCount()
function recipeCount() external view returns (uint256)
Returns the total number of recipes.
isMinter(address _contractAddress)
function isMinter(address _contractAddress) public view returns (bool)
Checks if Fabricator has MINTER_ROLE on a contract.
Events
event RecipeAdded(uint256 indexed recipeId, address indexed creator, MintItem mintItem);
event RecipeRemoved(uint256 indexed recipeId);
event RecipeAdjusted(uint256 indexed recipeId, address indexed creator, MintItem mintItem);
event ItemFabricated(uint256 indexed recipeId, address indexed user, MintItem mintItem);
event ItemBurned(
address indexed user,
address indexed contractAddress,
uint256 id,
uint256 amount
);
event ItemTransferred(
address indexed from,
address indexed to,
address indexed contractAddress,
uint256 id,
uint256 amount
);
event NativeTokenTransferred(address indexed from, address indexed to, uint256 amount);
event ERC20Transferred(
address indexed from,
address indexed to,
address indexed token,
uint256 amount
);
event BatchFabricationCompleted(uint256[] recipeIds, address indexed user);
Errors
error NotAuthorized(address caller, bytes32 role);
error NotMinter(address contractAddress);
error RecipeDoesNotExist(uint256 recipeId);
error TooManyItems(uint256 count, uint256 max);
error CreatorNotSet();
error InsufficientBalance(address token, uint256 required, uint256 available);
error InsufficientEth(uint256 required, uint256 sent);
error TransferFailed(address token, uint256 amount);
error InvalidBatchLength();
error DuplicateRecipeInBatch();
error RecipeIdOutOfBounds(uint256 recipeId, uint256 maxRecipeId);
error NoItemsListed();
Usage Examples
Creating a Sword Upgrade Recipe
Recipe memory swordUpgrade = Recipe({
mintItem: MintItem({
contractAddress: inventoryAddress,
id: LEGENDARY_SWORD_ID,
amount: 1
}),
creator: treasuryAddress,
items1155: [
Item1155({
contractAddress: inventoryAddress,
id: BASIC_SWORD_ID,
amount: 1,
burn: true // Destroy the basic sword
}),
Item1155({
contractAddress: inventoryAddress,
id: UPGRADE_CRYSTAL_ID,
amount: 5,
burn: true // Consume crystals
})
],
items20: [
Item20({
contractAddress: vidyaTokenAddress,
amount: 100 * 10**18, // 100 VIDYA tokens
native: false
}),
Item20({
contractAddress: address(0),
amount: 0.1 ether, // 0.1 ETH fee
native: true
})
]
});
fabricator.addRecipe(swordUpgrade);
Executing a Recipe
// Check if player can craft
(bool canCraft, string memory reason) = fabricator.willFabricate(recipeId, playerAddress);
require(canCraft, reason);
// Approve token transfers
inventory.setApprovalForAll(fabricatorAddress, true);
vidyaToken.approve(fabricatorAddress, requiredTokens);
// Execute crafting
fabricator.fabricate{value: 0.1 ether}(recipeId);
Batch Crafting
uint256[] memory recipeIds = [SWORD_RECIPE, SHIELD_RECIPE, POTION_RECIPE];
uint256 totalEth = 0.3 ether; // Sum of all ETH requirements
fabricator.batchFabricate{value: totalEth}(recipeIds);
Security Considerations
- Access Control: Only ADMIN_ROLE holders can manage recipes
- Reentrancy Protection: Contract uses ReentrancyGuard
- Balance Validation: All input balances checked before transfers
- Minter Verification: Fabricator must have MINTER_ROLE on output contracts
- Overflow Protection: Uses unchecked blocks only for safe operations
Recipe Design Best Practices
- Set appropriate burn flags based on game economics
- Use the creator address as a treasury or sink
- Validate recipe balance to prevent exploits
- Consider ETH fees for anti-spam measures
- Test recipes thoroughly before deployment