Withdrawal System

DegenPrime uses a 24-hour withdrawal intent system to protect against flash-loan attacks and ensure protocol stability.

Why a Waiting Period?

The 24-hour withdrawal intent system prevents a class of attacks where an attacker could use flash loans to temporarily inflate collateral value, borrow, and withdraw in a single transaction. By requiring withdrawals to be declared 24 hours in advance, these atomic attacks become impossible.

Withdrawal Lifecycle

1

Create Intent

Declare which token and how much you want to withdraw. Starts the 24-hour timer.

2

Wait 24 Hours

The intent is locked during this period. You can cancel it at any time but cannot fulfill it early.

3

Fulfill Withdrawal

After 24 hours, execute the withdrawal. Tokens transfer to your wallet. Solvency is checked.

Step 1: Create Withdrawal Intent

Call createWithdrawalIntent(bytes32 tokenId, uint256 amount) to declare your withdrawal. This does not require RedStone wrapping.

typescript
import { encodePacked, keccak256 } from "viem";

const tokenId = keccak256(encodePacked(["string"], ["USDC"]));
const withdrawAmount = 500n * 10n ** 6n; // 500 USDC

const hash = await walletClient.writeContract({
  address: loanAddress,
  abi: withdrawalIntentFacetAbi,
  functionName: "createWithdrawalIntent",
  args: [tokenId, withdrawAmount],
});

console.log("Intent created. Tx:", hash);
console.log("You can fulfill after:", new Date(Date.now() + 86400000));

Step 2: Check Intent Status

While waiting, you can check the status of your withdrawal intent to see when it becomes fulfillable.

typescript
const intentInfo = await client.readContract({
  address: loanAddress,
  abi: withdrawalIntentFacetAbi,
  functionName: "getWithdrawalIntentInfo",
  args: [tokenId],
});

const { amount, unlockTimestamp } = intentInfo;
const now = Math.floor(Date.now() / 1000);

if (now >= unlockTimestamp) {
  console.log("Ready to fulfill!");
} else {
  const remaining = unlockTimestamp - now;
  const hours = Math.floor(remaining / 3600);
  const minutes = Math.floor((remaining % 3600) / 60);
  console.log(`${hours}h ${minutes}m remaining`);
}

Step 3: Fulfill Withdrawal

Important: fulfillWithdrawalIntent requires RedStone wrapping because it triggers a solvency check to ensure the loan remains healthy after the withdrawal.

typescript
import { WrapperBuilder } from "@redstone-finance/evm-connector";

// Wrap with RedStone (solvency check during withdrawal)
const wrappedContract = WrapperBuilder
  .wrap(contract)
  .usingDataService({
    dataServiceId: "redstone-arbitrum-prod",
    uniqueSignersCount: 3,
  });

const tx = await wrappedContract.fulfillWithdrawalIntent(tokenId);
await tx.wait();

console.log("Withdrawal complete! Tokens transferred to your wallet.");

Cancelling an Intent

If you change your mind, you can cancel a pending withdrawal intent at any time (before or after the 24-hour period). This does not require RedStone wrapping.

typescript
await walletClient.writeContract({
  address: loanAddress,
  abi: withdrawalIntentFacetAbi,
  functionName: "cancelWithdrawalIntent",
  args: [tokenId],
});

console.log("Withdrawal intent cancelled.");

Bot Integration Tips

  • Schedule a cron job or timer to automatically call fulfillWithdrawalIntent after 24 hours
  • Always check solvency before creating an intent -- if the withdrawal would make the loan insolvent, the fulfillment will revert
  • Only one intent per token can be active at a time. Creating a new intent for the same token replaces the old one