```json
{
  "sym_variables": [
    ("x1", "dollars invested in NFTs"),
    ("x2", "dollars invested in crypto-currency")
  ],
  "objective_function": "0.3 * x1 + 0.4 * x2",
  "constraints": [
    "x1 + x2 <= 5000",
    "x1 >= 0.25 * (x1 + x2)",
    "x2 >= 2300",
    "x1 >= 0",
    "x2 >= 0"
  ]
}
```

```python
import gurobipy as gp

# Create a new model
m = gp.Model("Lisa's Investment")

# Create variables
nfts = m.addVar(name="NFTs")  # x1: dollars invested in NFTs
crypto = m.addVar(name="Crypto") # x2: dollars invested in crypto-currency

# Set objective function
m.setObjective(0.3 * nfts + 0.4 * crypto, gp.GRB.MAXIMIZE)

# Add constraints
m.addConstr(nfts + crypto <= 5000, "Total investment")
m.addConstr(nfts >= 0.25 * (nfts + crypto), "NFT proportion")
m.addConstr(crypto >= 2300, "Minimum crypto")
m.addConstr(nfts >= 0, "Non-negative NFTs")  # Although implied, good practice to include
m.addConstr(crypto >= 0, "Non-negative Crypto") # Although implied, good practice to include


# Optimize model
m.optimize()

# Print results
if m.status == gp.GRB.OPTIMAL:
    print(f"Optimal profit: ${m.objVal:.2f}")
    print(f"Invest ${nfts.x:.2f} in NFTs")
    print(f"Invest ${crypto.x:.2f} in Crypto")
elif m.status == gp.GRB.INFEASIBLE:
    print("The problem is infeasible.")
else:
    print(f"Optimization terminated with status {m.status}")

```
