Deposit & Borrow (T2 Vault)
T2 vaults use LP-style collateral (e.g. weETH/ETH) and support both operate() (token-amount based) and operatePerfect() (share-based).
Helper: calculateColSharesMinMax
For deposit: pass both token amounts you wish to deposit (supply0AmountInWei, supply1AmountInWei). The function returns min/max shares for operate().
Helper: calculatePerfectColAmounts
For operatePerfect(): you only need to pass supply0AmountWei (or supply1AmountWei). The other amount is automatically computed from the pool proportion.
Function: operate()
Deposits collateral and borrows debt using explicit token amounts. Use calculateColSharesMinMax to compute colSharesMinMax for the desired token amounts.
Collateral:
- ERC20: Approve vault, then call
operatewithvalue: 0. - Native ETH: Send the collateral leg as
value; no approval needed for that leg.
- ERC20: Approve vault, then call
Transaction:
typescript{ to: vaultAddress, data: encodeFunctionData({ abi: vaultT2Abi, functionName: 'operate', args: [ nftId, // 0 for new position colToken0, // Positive: deposit token0 (e.g. weETH) colToken1, // Positive: deposit token1 (e.g. ETH) colSharesMinMax, // Max shares to mint (from calculateColSharesMinMax) borrowAmount, // Positive: borrow debt accountAddress ] }), value: isToken0Native ? colToken0 : isToken1Native ? colToken1 : 0n }
Function: operatePerfect()
Deposits collateral and borrows debt using share-based amounts. Use calculatePerfectColAmounts for perfectColShares and token min/max; it auto-computes the second token from pool proportion.
- Transaction:typescript
{ to: vaultAddress, data: encodeFunctionData({ abi: vaultT2Abi, functionName: 'operatePerfect', args: [ nftId, perfectColShares, colToken0MinMax, // Token amounts with buffer (e.g. * 101/100) colToken1MinMax, borrowAmount, accountAddress ] }), value: isToken0Native ? colToken0 : isToken1Native ? colToken1 : 0n }
Operate ABI
export const vaultT2Abi = [
{
name: "operate",
type: "function",
stateMutability: "payable",
inputs: [
{ name: "nftId_", type: "uint256" },
{ name: "newColToken0_", type: "int256" },
{ name: "newColToken1_", type: "int256" },
{ name: "colSharesMinMax_", type: "int256" },
{ name: "newDebt_", type: "int256" },
{ name: "to_", type: "address" },
],
outputs: [
{ name: "nftId", type: "uint256" },
{ name: "supplyAmt", type: "int256" },
{ name: "borrowAmt", type: "int256" },
],
},
{
name: "operatePerfect",
type: "function",
stateMutability: "payable",
inputs: [
{ name: "nftId_", type: "uint256" },
{ name: "perfectColShares_", type: "int256" },
{ name: "colToken0MinMax_", type: "int256" },
{ name: "colToken1MinMax_", type: "int256" },
{ name: "newDebt_", type: "int256" },
{ name: "to_", type: "address" },
],
outputs: [
{ name: "nftId", type: "uint256" },
{ name: "r_", type: "int256[]" },
],
},
];Example: operate()
/**
* Example: Deposit weETH + ETH, borrow wstETH (T2 vault) using operate().
*
* Flow:
* (1) Approve ERC20 collateral (weETH); native ETH sent as value for the ETH leg.
* (2) Call operate() to deposit collateral and borrow.
* Set PRIVATE_KEY and RPC_URL in .env.
*
* Run: pnpm run borrow:t2-deposit-borrow -- --network arbitrum
*/
import { encodeFunctionData, erc20Abi, formatUnits, parseUnits } from "viem";
import { vaultT2Abi } from "../shared/abis/vaultT2Abi.js";
import {
account,
chain,
publicClient,
walletClient,
} from "../shared/config.js";
import { fetchVaultApi } from "../shared/utils/utils.js";
import { calculateColSharesMinMax } from "../shared/utils/dex/shares.js";
const SLIPPAGE = 0.01;
const vaultAddress =
"0x3996464c0fCCa8183e13ea5E5e74375e2c8744Dd" as `0x${string}`; // weETH/ETH - wstETH T2 vault
const supply0Wei = parseUnits("0.000724696012379057102", 18); // weETH
const supply1Wei = parseUnits("0.0005928029528832", 18); // ETH (native)
const borrowAmount = parseUnits("0.0001", 18); // 0.0001 wstETH
// Flags to indicate which legs are native ETH for this example
const isToken0Native = false;
const isToken1Native = true;
async function main() {
const vault = await fetchVaultApi(chain.id, vaultAddress);
const supplyToken0 = vault.supplyToken.token0;
const supplyToken1 = vault.supplyToken.token1;
if (!supplyToken1) throw new Error("T2 vault must have supplyToken.token1");
// calculateColSharesMinMax: pass both tokens you wish to deposit; returns colSharesMinMax for operate()
const colResult = calculateColSharesMinMax({
vault,
supply0AmountInWei: supply0Wei.toString(),
supply1AmountInWei: supply1Wei.toString(),
borrow0AmountInWei: "",
borrow1AmountInWei: "",
supplySlippage: SLIPPAGE,
borrowSlippage: 0,
depositAndBorrow: true,
});
if (!colResult) throw new Error("calculateColSharesMinMax failed");
const colSharesMinMax = BigInt(colResult.sharesWithSlippage);
// Raw collateral amounts used for operate()
const colToken0 = BigInt(supply0Wei);
const colToken1 = BigInt(supply1Wei);
const token0Addr = supplyToken0.address as `0x${string}`;
const token1Addr = supplyToken1.address as `0x${string}`;
console.log("T2 operate: deposit collateral, borrow (Arbitrum)");
console.log(" Vault:", vaultAddress);
console.log(" colToken0:", colToken0.toString());
console.log(" colToken1:", colToken1.toString());
console.log(" colSharesMinMax:", colSharesMinMax.toString());
console.log(
" Borrow:",
formatUnits(borrowAmount, vault.borrowToken.token0.decimals),
vault.borrowToken.token0.symbol,
);
// Helper: send ERC20 approve transaction
const approve = async (token: `0x${string}`, amount: bigint) => {
const data = encodeFunctionData({
abi: erc20Abi,
functionName: "approve",
args: [vaultAddress, amount],
});
const hash = await walletClient.sendTransaction({
to: token,
data,
account,
});
await publicClient.waitForTransactionReceipt({ hash });
console.log(" Approve tx:", hash);
};
// Step 1 - Approve ERC20 collateral (weETH). Token1 is native ETH; send as value.
if (!isToken0Native) await approve(token0Addr, colToken0);
if (!isToken1Native) await approve(token1Addr, colToken1);
// Step 2 - Call operate() to deposit collateral and borrow
const operateData = encodeFunctionData({
abi: vaultT2Abi,
functionName: "operate",
args: [
0n,
colToken0,
colToken1,
colSharesMinMax,
borrowAmount,
account.address,
],
});
const hash = await walletClient.sendTransaction({
to: vaultAddress,
data: operateData,
// Send native ETH for the ETH collateral leg; zero otherwise.
value: isToken0Native ? colToken0 : isToken1Native ? colToken1 : 0n,
account,
});
console.log(" Operate tx:", hash);
console.log("Explorer:", `${chain.blockExplorers?.default?.url}/tx/${hash}`);
const receipt = await publicClient.waitForTransactionReceipt({ hash });
if (receipt.status !== "success") {
console.error("Operate tx reverted");
process.exit(1);
}
console.log("Position opened.");
}
main().catch((e) => {
console.error(e);
process.exit(1);
});Example: operatePerfect()
/**
* Example: Deposit weETH + ETH, borrow wstETH (T2 vault) using operatePerfect().
*
* Flow:
* (1) Approve ERC20 collateral (weETH); native ETH sent as value for the ETH leg.
* (2) Call operatePerfect() to deposit collateral and borrow.
* Set PRIVATE_KEY and RPC_URL in .env.
*
* Run: pnpm run borrow:t2-deposit-borrow-perfect -- --network arbitrum
*/
import { encodeFunctionData, erc20Abi, formatUnits, parseUnits } from "viem";
import { vaultT2Abi } from "../shared/abis/vaultT2Abi.js";
import {
account,
chain,
publicClient,
walletClient,
} from "../shared/config.js";
import { fetchVaultApi } from "../shared/utils/utils.js";
import { calculatePerfectColAmounts } from "../shared/utils/dex/perfectAmounts.js";
const SLIPPAGE = 0.01;
const vaultAddress =
"0x3996464c0fCCa8183e13ea5E5e74375e2c8744Dd" as `0x${string}`; // weETH/ETH - wstETH T2 vault
const supply0Wei = parseUnits("0.0007444939633859551022", 18); // weETH
const supply1Wei = parseUnits("0.0005713045660656", 18); // ETH (native)
const borrowAmount = parseUnits("0.00001", 18); // 0.00001 wstETH
// Flags to indicate which legs are native ETH for this example
const isToken0Native = false;
const isToken1Native = true;
async function main() {
const vault = await fetchVaultApi(chain.id, vaultAddress);
const supplyToken0 = vault.supplyToken.token0;
const supplyToken1 = vault.supplyToken.token1;
if (!supplyToken1) throw new Error("T2 vault must have supplyToken.token1");
// calculatePerfectColAmounts: pass only supply0; supply1 is auto-computed from pool proportion
const perfectCol = calculatePerfectColAmounts({
vault,
supplySlippage: SLIPPAGE,
depositAndBorrow: true,
paybackAndWithdraw: false,
proportion: "fixed",
supply0AmountWei: supply0Wei.toString(),
supply1AmountWei: "",
});
if (!perfectCol) {
throw new Error(
"calculatePerfectColAmounts failed: perfectCol is undefined",
);
}
if (!supplyToken0 || !supplyToken1) {
throw new Error("Missing supplyToken0 or supplyToken1");
}
const colToken0MinMax = BigInt(perfectCol.token0AmtWithSlippage);
const colToken1MinMax = BigInt(perfectCol.token1AmtWithSlippage);
const perfectColShares = BigInt(perfectCol.shares);
const token0Addr = supplyToken0.address as `0x${string}`;
const token1Addr = supplyToken1.address as `0x${string}`;
console.log("T2 operatePerfect: deposit collateral, borrow (Arbitrum)");
console.log(" Vault:", vaultAddress);
console.log(" perfectColShares:", perfectColShares.toString());
console.log(" colToken0MinMax:", colToken0MinMax.toString());
console.log(" colToken1MinMax:", colToken1MinMax.toString());
console.log(
" Borrow:",
formatUnits(borrowAmount, vault.borrowToken.token0.decimals),
vault.borrowToken.token0.symbol,
);
// Helper: send ERC20 approve transaction
const approve = async (token: `0x${string}`, amount: bigint) => {
const data = encodeFunctionData({
abi: erc20Abi,
functionName: "approve",
args: [vaultAddress, amount],
});
const hash = await walletClient.sendTransaction({
to: token,
data,
account,
});
await publicClient.waitForTransactionReceipt({ hash });
console.log(" Approve tx:", hash);
};
// Step 1 - Approve ERC20 collateral (weETH). Token1 is native ETH; send as value.
if (!isToken0Native) await approve(token0Addr, colToken0MinMax);
if (!isToken1Native) await approve(token1Addr, colToken1MinMax);
// Step 2 - Call operatePerfect() to deposit collateral and borrow
const operateData = encodeFunctionData({
abi: vaultT2Abi,
functionName: "operatePerfect",
args: [
0n,
perfectColShares,
colToken0MinMax,
colToken1MinMax,
borrowAmount,
account.address,
],
});
const hash = await walletClient.sendTransaction({
to: vaultAddress,
data: operateData,
// Send native ETH for the ETH collateral leg; zero otherwise.
value: isToken0Native
? colToken0MinMax
: isToken1Native
? colToken1MinMax
: 0n,
account,
});
console.log(" OperatePerfect tx:", hash);
console.log("Explorer:", `${chain.blockExplorers?.default?.url}/tx/${hash}`);
const receipt = await publicClient.waitForTransactionReceipt({ hash });
if (receipt.status !== "success") {
console.error("OperatePerfect tx reverted");
process.exit(1);
}
console.log("Position opened.");
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
