Repay & Withdraw (T1 Vault)
Function: operate()
Repays borrowed debt and withdraws collateral from a T1 Fluid vault in a single transaction.
Repay Debt:
- ERC20 Tokens: Prior to repaying, you must approve the vault to spend the debt token. Always approve slightly more than the exact debt amount — interest accrues every block, so by the time your transaction is mined the debt will be marginally higher than when you read it. A 1% buffer is the standard practice:typescriptIf you approve exactly
const approveAmount = (debtAmount * 101n) / 100n; // 1% buffer for accrued interest await walletClient.writeContract({ address: debtTokenAddress, abi: erc20Abi, functionName: 'approve', args: [vaultAddress, approveAmount], });debtAmountand interest accrues before youroperatecall lands, the vault'stransferFromwill revert because the approval is insufficient. - Native Tokens (ETH): If the debt is in native ETH, send the repay amount as the
valueof theoperatetransaction. No approval is needed.
- ERC20 Tokens: Prior to repaying, you must approve the vault to spend the debt token. Always approve slightly more than the exact debt amount — interest accrues every block, so by the time your transaction is mined the debt will be marginally higher than when you read it. A 1% buffer is the standard practice:
Withdraw Collateral:
- Collateral is withdrawn to the
to_address specified in theoperatecall. - If the collateral is a native token, it will be sent as native ETH to the recipient.
- Collateral is withdrawn to the
Repay and Withdraw Action:
- Use negative values for
newCol_to withdraw collateral and negative values fornewDebt_to repay debt. - Transaction data:typescript
{ to: vaultAddress, data: encodeFunctionData({ abi: vaultT1Abi, functionName: 'operate', args: [ nftId, // The specific NFT ID for your position -withdrawCollateralWei, // Negative value to withdraw collateral -repayAmountWei, // Negative value to repay debt accountAddress // Address to receive withdrawn collateral ] }), value: isNativeDebt ? repayAmountWei : 0n // Send value if repaying native ETH }
- Use negative values for
Repay & Withdraw Max (Close Position):
- To fully repay debt and withdraw all collateral (closing the position), use the minimum possible
int256value (-57896044618658097711785492504343953926634992332820282019728792003956564819968). - Using this value ensures all remaining amounts are processed regardless of small interest accruals.typescript
const minInt256 = -57896044618658097711785492504343953926634992332820282019728792003956564819968n; // In operate call args: [nftId, minInt256, minInt256, accountAddress]
- To fully repay debt and withdraw all collateral (closing the position), use the minimum possible
Operate ABI:
typescriptexport const vaultT1Abi = [ { name: "operate", type: "function", stateMutability: "payable", inputs: [ { name: "nftId_", type: "uint256" }, { name: "newCol_", type: "int256" }, { name: "newDebt_", type: "int256" }, { name: "to_", type: "address" }, ], outputs: [ { name: "nftId", type: "uint256" }, { name: "supplyAmt", type: "int256" }, { name: "borrowAmt", type: "int256" }, ], }, ];
Flow:
User → approve(debtToken, debt * 1.01) → operate(nftId, -col, -debt, ...) → Debt Repaid & Collateral WithdrawnFull repay & withdraw example
ts
/**
* Example: Repay borrowed USDC and withdraw WETH collateral (exact amounts).
*
* Flow: (1) Approve vault to spend USDC, (2) operate(nftId, -withdrawCol, -repayDebt, to).
* Set PRIVATE_KEY, RPC_URL in .env.
*
* Run: pnpm run borrow:t1-repay-withdraw -- --network polygon
*/
import vaultT1Abi from "../shared/abis/vaultT1Abi";
import { formatUnits, parseUnits, encodeFunctionData, erc20Abi } from "viem";
import {
account,
chain,
publicClient,
walletClient,
} from "../shared/config.js";
const VAULT_WETH_USDC = "0xeAbBfca72F8a8bf14C4ac59e69ECB2eB69F0811C";
const POLYGON_USDC = "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359";
// Set your NFT position ID here
const NFT_ID = "";
async function main() {
if (!NFT_ID.trim()) {
console.error("Set NFT_ID in this file");
process.exit(1);
}
const nftId = BigInt(NFT_ID.trim());
const repayAmountWei = parseUnits("1.000458", 6); // USDC - 6 decimals
const withdrawCollateralWei = parseUnits("0.001000028414875511", 18); // wEth 18 decimals
// Approve 1.01 times the repay amount to add a slight buffer for rounding or fees
const approveAmount = (repayAmountWei * 101n) / 100n;
console.log("Repay & withdraw (exact amounts, WETH/USDC on Polygon)");
console.log(" Vault:", VAULT_WETH_USDC);
console.log(" NFT ID:", nftId.toString());
console.log(" Repay:", formatUnits(repayAmountWei, 6), "USDC");
console.log(
" Withdraw collateral:",
formatUnits(withdrawCollateralWei, 18),
"WETH",
);
// Step 1 - Approve the vault to spend USDC
const approveData = encodeFunctionData({
abi: erc20Abi,
functionName: "approve",
args: [VAULT_WETH_USDC, approveAmount],
});
const approveHash = await walletClient.sendTransaction({
to: POLYGON_USDC,
data: approveData,
account,
});
console.log(" Approve tx:", approveHash);
console.log(
"Explorer:",
`${chain.blockExplorers?.default?.url}/tx/${approveHash}`,
);
const approveReceipt = await publicClient.waitForTransactionReceipt({
hash: approveHash,
});
if (approveReceipt.status !== "success") {
console.error("Approve tx reverted");
process.exit(1);
}
// Step 2 - Call operate() to repay debt and withdraw collateral
const operateData = encodeFunctionData({
abi: vaultT1Abi,
functionName: "operate",
args: [nftId, -withdrawCollateralWei, -repayAmountWei, account.address],
});
const hash = await walletClient.sendTransaction({
to: VAULT_WETH_USDC,
data: operateData,
value: 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 updated. USDC repaid, WETH withdrawn.");
}
main().catch((e) => {
console.error(e);
process.exit(1);
});Close position (repay & withdraw max) example
ts
/**
* Example: Repay all borrowed USDC and withdraw all WETH collateral (close position fully).
*
* Flow: (1) Approve vault a large USDC amount, (2) operate(nftId, INT256_MIN, INT256_MIN, to).
* Set PRIVATE_KEY, RPC_URL in .env.
*
* Run: pnpm run borrow:t1-repay-withdraw-max -- --network polygon
*/
import vaultT1Abi from "../shared/abis/vaultT1Abi";
import { encodeFunctionData, erc20Abi, parseUnits } from "viem";
import { minInt } from "../shared/utils/constants";
import {
account,
chain,
publicClient,
walletClient,
} from "../shared/config.js";
const VAULT_WETH_USDC = "0xeAbBfca72F8a8bf14C4ac59e69ECB2eB69F0811C";
const POLYGON_USDC = "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359";
// Set your NFT_ID here, e.g. const nftId = "123";
const NFT_ID = "";
async function main() {
if (!NFT_ID.trim()) {
console.error("Set NFT_ID in .env");
process.exit(1);
}
const nftId = BigInt(NFT_ID.trim());
const actualAmountWei = parseUnits("1.00085", 6);
const approveAmount = (actualAmountWei * 101n) / 100n;
console.log("Repay & withdraw MAX (close position, WETH/USDC on Polygon)");
console.log(" Vault:", VAULT_WETH_USDC);
console.log(" NFT ID:", nftId.toString());
const approveData = encodeFunctionData({
abi: erc20Abi,
functionName: "approve",
args: [VAULT_WETH_USDC, approveAmount],
});
// Step 1 - Approve the vault to spend USDC
const approveHash = await walletClient.sendTransaction({
to: POLYGON_USDC,
data: approveData,
account,
});
console.log(" Approve tx:", approveHash);
console.log(
"Explorer:",
`${chain.blockExplorers?.default?.url}/tx/${approveHash}`,
);
const approveReceipt = await publicClient.waitForTransactionReceipt({
hash: approveHash,
});
if (approveReceipt.status !== "success") {
console.error("Approve tx reverted");
process.exit(1);
}
// Step 2 - Call operate() to repay max debt and withdraw max collateral
const operateData = encodeFunctionData({
abi: vaultT1Abi,
functionName: "operate",
args: [nftId, BigInt(minInt), BigInt(minInt), account.address],
});
const hash = await walletClient.sendTransaction({
to: VAULT_WETH_USDC,
data: operateData,
value: 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 closed. USDC repaid, WETH withdrawn (max).");
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
