Non-Locking FlashLoan Protocol
The Non-Locking FlashLoan Protocol revolutionizes flash loans by allowing users to retain full control of their tokens while providing liquidity, powered by Reactive Network’s event-driven architecture.
Introduction
Flash loans are a cornerstone of DeFi, enabling users to borrow large amounts of capital without collateral, provided the loan is repaid within the same transaction. However, traditional protocols like Aave require liquidity providers to lock assets in pools, limiting the tokens’ usability in other DeFi activities.
The Non-Locking FlashLoan Protocol, powered by the Reactive Network, offers an alternative. This system allows users to retain full control of their tokens while participating as liquidity providers. By enabling simultaneous liquidity provision and asset utilization, the protocol redefines flash loan dynamics.
This implementation is based on the project of Hackathon winner Maniveer, whose solution highlights the potential of decentralized finance to evolve toward greater flexibility and accessibility.
Redefining Flash Loan Dynamics
At the heart of the protocol is the Reactive Network, a framework enabling automatic, event-driven on-chain actions. Unlike conventional systems that require static pools, this architecture leverages real-time triggers, such as token approvals and transfers, to manage liquidity dynamically.
The protocol offers significant advantages, including real-time liquidity management, where updates are triggered automatically by user actions such as token approvals, ensuring the system remains responsive and up-to-date. Additionally, liquidity providers benefit from unrestricted access to their tokens, allowing them to participate in flash loans without locking assets, thus maintaining flexibility to leverage their funds across other DeFi protocols.
Protocol Features
The Non-Locking FlashLoan Protocol presents a set of unique features:
Token Ownership Retained: Users retain full control of their tokens without transferring them to a central pool.
Instant Liquidity Management: Token approvals automatically update the protocol’s liquidity details in real time.
Event-Triggered Transactions: Loans are dynamically executed based on token events, avoiding static pools.
Transparent Fee Structure: A 0.3% fee incentivizes liquidity providers while maintaining the non-locking design.
Unmatched Accessibility: Users can freely move or utilize their tokens while participating as liquidity providers.
Implementation
The protocol’s implementation relies on several smart contracts for event-driven liquidity management and flash loan execution:
Token.sol: A basic ERC-20 token with minting capabilities for testing.
MockReceiver.sol: Simulates operations on the receiver side of a flash loan.
FlashLoan.sol: Manages liquidity updates and facilitates flash loan execution.
ReactiveFlashLoan.sol: Subscribes to token events, dynamically updating liquidity details via the Reactive Network.
How It Works
Liquidity Management
Users dynamically include their tokens in the liquidity pool by approving the flash loan contract. The system monitors token balances and allowances to calculate available liquidity. Liquidity updates are handled via the `updateUser` function, which ensures accurate tracking in real-time.
function updateUser(address /**/, address user) public {
uint256 balance = token.balanceOf(user);
uint256 allowance = token.allowance(user, address(this));
uint256 newLiquidity = _getLiquidity(balance, allowance);
if (userLPindex[user] == 0 && newLiquidity > 0) {
userLPindex[user] = liquidityProviders.length;
liquidityProviders.push(LP(user, balance, allowance));
reserves += newLiquidity;
} else if (userLPindex[user] != 0) {
uint256 index = userLPindex[user];
uint256 userBalanceBefore = liquidityProviders[index].balance;
uint256 userAllowanceBefore = liquidityProviders[index].allowance;
liquidityProviders[index].balance = balance;
liquidityProviders[index].allowance = allowance;
uint256 previousLiquidity = _getLiquidity(userBalanceBefore, userAllowanceBefore);
if (previousLiquidity > newLiquidity) {
reserves -= previousLiquidity - newLiquidity;
} else {
reserves += newLiquidity - previousLiquidity;
}
if (newLiquidity == 0) {
_removeUser(index);
}
}
emit UserUpdated(user, balance, allowance);
}
Flash Loan Execution
When a flash loan is requested, liquidity is aggregated from providers, ensuring tokens are not locked. The protocol transfers the requested amount to the receiver, who must repay the loan along with a fee (0.3%). After repayment, tokens are redistributed to liquidity providers, and their states are dynamically updated.
function requestFlashLoan(address receiver, uint256 amount) public {
require(reserves >= amount, "Reserves not sufficient");
uint256 pendingAmount = amount;
uint256 length = 0;
LP[] memory queue = liquidityProviders;
for (uint256 i = 0; i < queue.length; i++) {
if (pendingAmount <= _getLiquidity(queue[i].balance, queue[i].allowance)) {
length++;
break;
}
pendingAmount -= _getLiquidity(queue[i].balance, queue[i].allowance);
length++;
}
IReactiveFlashLoan.withdrawLP[] memory ret = new IReactiveFlashLoan.withdrawLP[](length);
uint256 pendingAmount2 = amount;
for (uint256 i = 0; i < length; i++) {
uint256 availableLiquidity = _getLiquidity(queue[i].balance, queue[i].allowance);
if (pendingAmount2 <= availableLiquidity) {
ret[i] = IReactiveFlashLoan.withdrawLP(queue[i].user, pendingAmount2);
break;
}
ret[i] = IReactiveFlashLoan.withdrawLP(queue[i].user, availableLiquidity);
pendingAmount2 -= availableLiquidity;
}
sendFlashLoan(ret, receiver, amount);
}
Event-Driven Updates
Token approvals or transfers trigger dynamic updates to the liquidity ledger. The `updateUser` function is invoked during such events to reflect accurate token balances and allowances for liquidity providers.
function react(
uint256 chain_id,
address _contract,
uint256 topic_0,
uint256 topic_1,
uint256 topic_2,
uint256 topic_3,
bytes calldata data,
uint256 /* block_number */,
uint256 /* op_code */
) external vmOnly {
if (topic_0 == APPROVE_TOPIC_0 && _contract == detail.flashLoanToken && detail.flashLoanContract == address(uint160(topic_2))) {
bytes memory payload = abi.encodeWithSignature("updateUser(address,address)", address(0), address(uint160(topic_1)));
emit Callback(chain_id, detail.flashLoanContract, GAS_LIMIT, payload);
} else if (topic_0 == TRANSFER_TOPIC_0 && _contract == detail.flashLoanToken) {
if (!(detail.flashLoanContract == address(uint160(topic_1)) || detail.flashLoanContract == address(uint160(topic_2)))) {
bytes memory payload1 = abi.encodeWithSignature("updateUser(address,address)", address(0), address(uint160(topic_1)));
bytes memory payload2 = abi.encodeWithSignature("updateUser(address,address)", address(0), address(uint160(topic_2)));
emit Callback(chain_id, detail.flashLoanContract, GAS_LIMIT, payload1);
emit Callback(chain_id, detail.flashLoanContract, GAS_LIMIT, payload2);
}
} else {
revert();
}
}
Conclusion
The Non-Locking FlashLoan Protocol demonstrates how the Reactive Network can redefine DeFi, eliminating traditional locking mechanisms while empowering users with control over their assets. By integrating event-driven architecture and dynamic liquidity, it offers a scalable and user-centric alternative for the flash loan ecosystem.
About Reactive Network
The Reactive Network, pioneered by PARSIQ, ushers in a new wave of blockchain innovation through its Reactive Smart Contracts (RSCs). These advanced contracts can autonomously execute based on specific on-chain events, eliminating the need for off-chain computation and heralding a seamless cross-chain ecosystem vital for Web3’s growth.
Central to this breakthrough is the Inversion of Control (IoC) framework, which redefines smart contracts and decentralized applications (DApps) by imbuing them with unparalleled autonomy, efficiency, and interactivity. By marrying RSCs with IoC, Reactive Network is setting the stage for a transformative blockchain era, characterized by enhanced interoperability and the robust, user-friendly foundation Web3 demands.