Featured for Asset Managers. Multi-strategy yield portfolio in one signed transaction — the canonical pattern for portfolio construction without per-step capital lockup.
Scenario
A user wants to diversify a USDC position across two yield venues: 60% into Aave v3 aEthUSDC, 40% into Morpho’s Steakhouse USDC. The flow uses core.split to divide the input resource by a fixed basis-point ratio, threads the two output handles into independent lifi.zap nodes, and attaches a slippage guard to each zap output.
This recipe is the canonical shape for portfolio diversification — any pattern where one input resource is partitioned and each partition is routed to a different terminal venue.
What this recipe demonstrates
core.split with a single basis-point ratio — bps: 6000 sends 60% to output a, the remaining 40% to output b.
- Consuming both split outputs in parallel via two
lifi.zap nodes that target different protocols.
- Attaching a
slippage guard to each zap’s amountOut port independently.
- Multiple terminal resources (the Aave and Morpho receipt tokens) swept to the sender in a single transaction.
Full example
Adapted from splitAndZap.ts in the composer-sdk-examples repo.
import {
createComposeSdk,
guards,
materialisers,
resources,
} from '@lifi/composer-sdk';
const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
// Aave v3 aEthUSDC receipt token on Ethereum mainnet
const A_ETH_USDC = '0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c';
// Steakhouse USDC (Morpho MetaMorpho vault) on Ethereum mainnet
const STEAKHOUSE_USDC = '0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB';
const sdk = createComposeSdk({ baseUrl: 'https://composer.li.quest' });
const builder = sdk.flow(1, {
name: 'split-usdc-to-two-vaults',
inputs: {
amountIn: resources.erc20(USDC, 1),
},
});
// Split USDC 60/40 — 6000 bps to `a`, remainder to `b`.
const { a, b } = builder.core.split('split', {
bind: { source: builder.inputs.amountIn },
config: { bps: 6000 },
});
// Zap 60% into Aave v3 aEthUSDC.
builder.lifi.zap('zap-aave', {
bind: { amountIn: a },
config: {
resourceOut: resources.erc20(A_ETH_USDC, 1),
},
guards: [guards.slippage({ port: 'amountOut', bps: 100 })],
});
// Zap 40% into Steakhouse USDC (Morpho).
builder.lifi.zap('zap-morpho', {
bind: { amountIn: b },
config: {
resourceOut: resources.erc20(STEAKHOUSE_USDC, 1),
},
guards: [guards.slippage({ port: 'amountOut', bps: 100 })],
});
const result = await builder.compile({
signer: '0xYourSignerAddress',
inputs: {
amountIn: materialisers.directDeposit({ amount: '10000000000' }), // 10,000 USDC
},
sweepTo: builder.context.sender,
});
What to observe
- Inputs. One resource input (
amountIn: USDC). The example uses 10,000 USDC.
- Nodes. Three ops: one
core.split, two lifi.zap.
- Split wire format.
flow.nodes[0].config === { bps: 6000 } with bind.source pointing at input.amountIn. Outputs are named a and b on the destructured return value — these become the refs split.a and split.b.
- Guards. Each zap carries an independent
slippage guard at 100 bps (1%) on amountOut.
- Terminal resources. Two:
aEthUSDC from the Aave zap, and the Steakhouse vault share from the Morpho zap. Both are swept to the sender.
producedResources[<name>].simulated?.amountOut. producedResources contains one entry per zap output, each with its own simulated?.amountOut / simulated?.amountOutMin.