"Combo Meal" - 2+ Recipes

Combo Meals are the final frontier – every zkApp Chef's dream. They combine Recipes into very complex interactions, made 100% safe for execution against a private balance using the Cookbook.
For example, there is a Combo Meal that combines an "Add Liquidity" Recipe for Uniswap, with a "Deposit Vault" Recipe for Beefy. This gives a user the ability to add liquidity for a token pair on Uniswap, gain the LP token for that pair, and then deposit the LP token into a Beefy Vault to earn yield.
This all occurs in a single validated transaction call, saving network fees and making the user experience simple and delightful.
Annotated Example: Uniswap V2-Like (Add Liquidity) and Beefy Vault (Deposit LP Token)
export class UniV2LikeAddLiquidity_BeefyDeposit_ComboMeal extends ComboMeal {
// Simple name and description. Note that the name of the Uniswap clone is set
// in the constructor.
readonly config = {
name: '[NAME] Add Liquidity + Beefy Vault Deposit Combo Meal',
'Adds liquidity to a [NAME] Pool and deposits the LP tokens into a Beefy Vault.',
// Private variables set during the constructor.
// In this case, it's a store for the two Recipes that we will combine together.
private readonly uniV2LikeAddLiquidityRecipe: UniV2LikeAddLiquidityRecipe;
private readonly beefyDepositRecipe: BeefyDepositRecipe;
uniswapV2Fork: UniswapV2Fork,
erc20InfoA: RecipeERC20Info,
erc20InfoB: RecipeERC20Info,
slippagePercentage: number,
vaultID: string,
provider: BaseProvider,
) {
// The Recipes are fully initialized here. Note that
// input amounts are never passed into Recipe/Combo constructors.
this.uniV2LikeAddLiquidityRecipe = new UniV2LikeAddLiquidityRecipe(
this.beefyDepositRecipe = new BeefyDepositRecipe(vaultID);
const forkName = UniV2LikeSDK.getForkName(uniswapV2Fork);
this.config.name = `${forkName} Add Liquidity + Beefy Vault Deposit Combo Meal`;
this.config.description = `Adds liquidity to a ${forkName} Pool and deposits the LP tokens into a Beefy Vault.`;
// This is a helper function that helps a user know how much of "ERC20 B" to unshield
// for a given amount of "ERC20 A" (A and B make up the liquidity pair).
// Accuracy is important, as the unshielding operation incurs a small % fee.
networkName: NetworkName,
targetUnshieldERC20AmountA: RecipeERC20Amount,
) {
return this.uniV2LikeAddLiquidityRecipe.getAddLiquidityAmountBForUnshield(
// Returns the two Recipes that create this "Combo Meal." The steps of each
// Recipe will be strung together in series, and sandwiched altogether
// by an unshield and shield call. The outputs from the 1st Recipe will not
// get automatically re-shielded - they will get passed into the 2nd Recipe
// as inputs for its first step.
// In this way, we can create in a single transaction call, minimizing fees and
// building a complex DeFi behavior as a sum of simple parts.
protected async getRecipes(): Promise<Recipe[]> {
return [this.uniV2LikeAddLiquidityRecipe, this.beefyDepositRecipe];