Building Your First dApp on Raenest: A Developer's Guide to Nested Finance
So you've heard about Raenest and want to actually build something instead of just reading whitepapers. Smart move. Nested finance is one of those concepts that sounds complicated until you start playing with it, then it clicks. Let me walk you through setting up your first dApp on Raenest so you can see what all the fuss is about.
What Even Is Raenest?
Before we jump into code, let's be real about what we're dealing with here. Raenest is essentially a protocol that lets you compose DeFi strategies into reusable, tradeable positions. Instead of manually managing a basket of assets across different protocols, you can package that entire strategy as a single token and trade it. It's like combining your smart contract logic with an AMM, but way more composable.
The key insight? You can nest positions within positions. That's where "nested" comes from. You could have a strategy that's literally made up of other strategies. It's recursive DeFi, which is either genius or chaos—probably both.
Prerequisites
You're going to need a few things before we start:
- Node.js (v16 or higher, seriously get a recent version)
- Hardhat or Foundry (I'll use Hardhat, but Foundry nerds will figure it out)
- A testnet wallet with some testnet tokens
- Basic Solidity knowledge (if you're reading this, you probably have it)
- MetaMask or another Web3 wallet
Install Hardhat if you don't have it:
npm install -g hardhat
Setting Up Your Project
Create a new directory and initialize a Hardhat project:
mkdir my-raenest-app
cd my-raenest-app
npx hardhat
Choose the TypeScript option when prompted—your future self will thank you. Install the Raenest SDK:
npm install @raenest/sdk ethers
You'll also want to grab the core dependencies:
npm install dotenv
Create a .env file:
PRIVATE_KEY=your_private_key_here
RPC_URL=https://your-rpc-endpoint.com
Never, ever commit this file. Add it to .gitignore immediately.
Understanding Raenest Architecture
Here's the mental model you need: Raenest has three main components:
Positions - These are your actual holdings, represented as ERC721 tokens. Each position is unique and can contain different assets.
Operations - The primitive actions you can perform. Think "swap," "deposit," "withdraw." They're composable building blocks.
Strategies - Pre-built sequences of operations. These are what you'll likely use or build on top of.
Creating Your First Position
Let's write some actual code. Here's a basic contract that creates a position on Raenest:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@raenest/core/PositionManager.sol";
import "@raenest/core/interfaces/IOperator.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract BasicRaenestStrategy {
PositionManager public positionManager;
address public operator;
constructor(address _positionManager, address _operator) {
positionManager = PositionManager(_positionManager);
operator = _operator;
}
function createSimplePosition(
address token0,
address token1,
uint256 amount0,
uint256 amount1
) external returns (uint256 positionId) {
// First, approve the position manager
IERC20(token0).approve(address(positionManager), amount0);
IERC20(token1).approve(address(positionManager), amount1);
// Create the position
bytes memory positionData = abi.encode(token0, token1, amount0, amount1);
positionId = positionManager.createPosition(
msg.sender,
positionData
);
return positionId;
}
function executeOperation(
uint256 positionId,
bytes calldata operationData
) external {
positionManager.executeOperation(positionId, operationData);
}
}
This is the bare minimum. You're creating a position with two tokens and the ability to execute operations on it.
Interacting with Your Position
Now let's look at how you'd actually use this from off-chain. The SDK makes this pretty straightforward:
import { RaenestClient } from "@raenest/sdk";
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const raenest = new RaenestClient(signer);
// Create a position
async function createPosition() {
const tx = await raenest.positions.create({
assets: [
{ token: "0x...USDC", amount: "1000000000" },
{ token: "0x...DAI", amount: "1000000000" }
],
strategy: "BALANCED"
});
const receipt = await tx.wait();
console.log("Position created:", receipt.transactionHash);
return receipt;
}
// Execute a swap operation
async function executeSwap(positionId: number) {
const swap = await raenest.operations.swap({
positionId,
tokenIn: "0x...USDC",
tokenOut: "0x...WETH",
amount: "500000000"
});
const receipt = await swap.wait();
console.log("Swap executed:", receipt.transactionHash);
}
// Get position details
async function getPositionDetails(positionId: number) {
const position = await raenest.positions.get(positionId);
console.log("Position holdings:", position.assets);
console.log("Position value:", position.totalValue);
}
Testing Your Strategy
Here's a test file to make sure your contract doesn't completely break:
import { expect } from "chai";
import { ethers } from "hardhat";
describe("BasicRaenestStrategy", function () {
it("Should create a position", async function () {
const [signer] = await ethers.getSigners();
// Deploy mock tokens (you'd use real testnet tokens)
const TokenA = await ethers.getContractFactory("MockERC20");
const token0 = await TokenA.deploy("Token A", "TKNA");
const token1 = await TokenA.deploy("Token B", "TKNB");
// Deploy your strategy
const Strategy = await ethers.getContractFactory("BasicRaenestStrategy");
const strategy = await Strategy.deploy(
"0x...", // positionManager address
"0x..." // operator address
);
// Mint some tokens
await token0.mint(signer.address, ethers.parseEther("100"));
await token1.mint(signer.address, ethers.parseEther("100"));
// Approve strategy
await token0.approve(await strategy.getAddress(), ethers.parseEther("100"));
await token1.approve(await strategy.getAddress(), ethers.parseEther("100"));
// Create position
const tx = await strategy.createSimplePosition(
await token0.getAddress(),
await token1.getAddress(),
ethers.parseEther("10"),
ethers.parseEther("10")
);
expect(tx).to.emit(strategy, "PositionCreated");
});
});
Run your tests with:
npx hardhat test
Real-World Considerations
A few things I've learned the hard way:
- Gas optimization matters. Nested operations can get expensive. Profile everything.
- Composability has limits. Not every combination of strategies makes sense. Test your assumptions.
- Rebalancing is tricky. If you're auto-compounding or rebalancing, you need a solid oracle setup.
- Audits are non-negotiable. If you're building this for real money, get professional eyes on it.
Next Steps
Once you have this working:
- Build a more sophisticated strategy (maybe a yield farming composite)
- Write a UI to interact with your positions
- Add governance if you're sharing this with others
- Get it audited before mainnet
- Deploy and watch it go sideways immediately (jk... mostly)
The Raenest ecosystem is still relatively young, so if you're building here, you're in the trenches where the real innovation happens. That's exciting and terrifying in equal measure.
Go build something cool.
Top comments (0)