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
Create Intent
Declare which token and how much you want to withdraw. Starts the 24-hour timer.
Wait 24 Hours
The intent is locked during this period. You can cancel it at any time but cannot fulfill it early.
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.
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.
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.
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.
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
fulfillWithdrawalIntentafter 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