Skip to main content

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:

  1. Validates all input balances
  2. Burns or transfers ERC1155 inputs based on recipe
  3. Transfers ERC20 inputs to creator
  4. Transfers ETH to creator (if required)
  5. Mints output items to caller
  6. 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 inputs
  • reason: 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

  1. Access Control: Only ADMIN_ROLE holders can manage recipes
  2. Reentrancy Protection: Contract uses ReentrancyGuard
  3. Balance Validation: All input balances checked before transfers
  4. Minter Verification: Fabricator must have MINTER_ROLE on output contracts
  5. Overflow Protection: Uses unchecked blocks only for safe operations

Recipe Design Best Practices

  1. Set appropriate burn flags based on game economics
  2. Use the creator address as a treasury or sink
  3. Validate recipe balance to prevent exploits
  4. Consider ETH fees for anti-spam measures
  5. Test recipes thoroughly before deployment