Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 32 additions & 14 deletions examples/apps/XYCSwap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,24 @@ contract XYCSwap is AquaApp {
uint256 amountOutMin,
address to,
bytes calldata takerData
) external returns (uint256 amountOut) {
bytes32 strategyHash = keccak256(abi.encode(strategy));
return _swapExactIn(strategy, strategyHash, zeroForOne, amountIn, amountOutMin, to, takerData);
}

function _swapExactIn(
Strategy calldata strategy,
bytes32 strategyHash,
bool zeroForOne,
uint256 amountIn,
uint256 amountOutMin,
address to,
bytes calldata takerData
)
external
nonReentrantStrategy(keccak256(abi.encode(strategy)))
private
nonReentrantStrategy(strategyHash)
returns (uint256 amountOut)
{
bytes32 strategyHash = keccak256(abi.encode(strategy));

(address tokenIn, address tokenOut, uint256 balanceIn, uint256 balanceOut) = _getInAndOut(strategy, strategyHash, zeroForOne);
amountOut = _quoteExactIn(strategy, balanceIn, balanceOut, amountIn);
require(amountOut >= amountOutMin, InsufficientOutputAmount(amountOut, amountOutMin));
Expand All @@ -80,13 +91,24 @@ contract XYCSwap is AquaApp {
uint256 amountInMax,
address to,
bytes calldata takerData
) external returns (uint256 amountIn) {
bytes32 strategyHash = keccak256(abi.encode(strategy));
return _swapExactOut(strategy, strategyHash, zeroForOne, amountOut, amountInMax, to, takerData);
}

function _swapExactOut(
Strategy calldata strategy,
bytes32 strategyHash,
bool zeroForOne,
uint256 amountOut,
uint256 amountInMax,
address to,
bytes calldata takerData
)
external
nonReentrantStrategy(keccak256(abi.encode(strategy)))
private
nonReentrantStrategy(strategyHash)
returns (uint256 amountIn)
{
bytes32 strategyHash = keccak256(abi.encode(strategy));

(address tokenIn, address tokenOut, uint256 balanceIn, uint256 balanceOut) = _getInAndOut(strategy, strategyHash, zeroForOne);
amountIn = _quoteExactOut(strategy, balanceIn, balanceOut, amountOut);
require(amountIn <= amountInMax, ExcessiveInputAmount(amountIn, amountInMax));
Expand All @@ -102,10 +124,8 @@ contract XYCSwap is AquaApp {
uint256 balanceOut,
uint256 amountIn
) internal view virtual returns (uint256 amountOut) {
// Use constant product formula (x*y=const) after fee deduction:
// balanceIn * balanceOut == (balanceIn + amountIn) * (balanceOut - amountOut)
uint256 amountInWithFee = amountIn * (BPS_BASE - strategy.feeBps) / BPS_BASE;
amountOut = (amountInWithFee * balanceOut) / (balanceIn + amountInWithFee);
amountOut = Math.mulDiv(amountInWithFee, balanceOut, balanceIn + amountInWithFee);
}

function _quoteExactOut(
Expand All @@ -114,10 +134,8 @@ contract XYCSwap is AquaApp {
uint256 balanceOut,
uint256 amountOut
) internal view virtual returns (uint256 amountIn) {
// Use constant product formula (x*y=const) after fee deduction:
// balanceIn * balanceOut == (balanceIn + amountIn) * (balanceOut - amountOut)
uint256 amountOutWithFee = amountOut * BPS_BASE / (BPS_BASE - strategy.feeBps);
amountIn = (balanceIn * amountOutWithFee).ceilDiv(balanceOut - amountOutWithFee);
amountIn = Math.mulDiv(balanceIn, amountOutWithFee, balanceOut - amountOutWithFee, Math.Rounding.Ceil);
}

function _getInAndOut(Strategy calldata strategy, bytes32 strategyHash, bool zeroForOne) private view returns (address tokenIn, address tokenOut, uint256 balanceIn, uint256 balanceOut) {
Expand Down
39 changes: 34 additions & 5 deletions src/Aqua.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ contract Aqua is IAqua {
error DockingShouldCloseAllTokens(address app, bytes32 strategyHash);
error PushToNonActiveStrategyPrevented(address maker, address app, bytes32 strategyHash, address token);
error SafeBalancesForTokenNotInActiveStrategy(address maker, address app, bytes32 strategyHash, address token);
error ArrayLengthMismatch();
error InvalidAddress();
error DuplicateToken();
error InsufficientBalance();
error InactiveStrategy();

uint8 private constant _DOCKED = 0xff;

Expand All @@ -44,38 +49,62 @@ contract Aqua is IAqua {
}

function ship(address app, bytes calldata strategy, address[] calldata tokens, uint256[] calldata amounts) external returns(bytes32 strategyHash) {
require(app != address(0), InvalidAddress());
require(tokens.length == amounts.length, ArrayLengthMismatch());

strategyHash = keccak256(strategy);
uint8 tokensCount = tokens.length.toUint8();
require(tokensCount != _DOCKED, MaxNumberOfTokensExceeded(tokensCount, _DOCKED));

emit Shipped(msg.sender, app, strategyHash, strategy);
for (uint256 i = 0; i < tokens.length; i++) {
Balance storage balance = _balances[msg.sender][app][strategyHash][tokens[i]];
uint256 length = tokens.length;
for (uint256 i = 0; i < length;) {
address token = tokens[i];
require(token != address(0), InvalidAddress());

for (uint256 j = 0; j < i;) {
require(tokens[j] != token, DuplicateToken());
unchecked { ++j; }
}

Balance storage balance = _balances[msg.sender][app][strategyHash][token];
require(balance.tokensCount == 0, StrategiesMustBeImmutable(app, strategyHash));
balance.store(amounts[i].toUint248(), tokensCount);
emit Pushed(msg.sender, app, strategyHash, tokens[i], amounts[i]);
emit Pushed(msg.sender, app, strategyHash, token, amounts[i]);

unchecked { ++i; }
}
}

function dock(address app, bytes32 strategyHash, address[] calldata tokens) external {
for (uint256 i = 0; i < tokens.length; i++) {
uint256 length = tokens.length;
for (uint256 i = 0; i < length;) {
Balance storage balance = _balances[msg.sender][app][strategyHash][tokens[i]];
require(balance.tokensCount == tokens.length, DockingShouldCloseAllTokens(app, strategyHash));
require(balance.tokensCount == length, DockingShouldCloseAllTokens(app, strategyHash));
balance.store(0, _DOCKED);

unchecked { ++i; }
}
emit Docked(msg.sender, app, strategyHash);
}

function pull(address maker, bytes32 strategyHash, address token, uint256 amount, address to) external {
require(maker != address(0) && to != address(0), InvalidAddress());

Balance storage balance = _balances[maker][msg.sender][strategyHash][token];
(uint248 prevBalance, uint8 tokensCount) = balance.load();
require(tokensCount > 0 && tokensCount != _DOCKED, InactiveStrategy());
require(amount <= prevBalance, InsufficientBalance());

balance.store(prevBalance - amount.toUint248(), tokensCount);

IERC20(token).safeTransferFrom(maker, to, amount);
emit Pulled(maker, msg.sender, strategyHash, token, amount);
}

function push(address maker, address app, bytes32 strategyHash, address token, uint256 amount) external {
require(maker != address(0) && app != address(0), InvalidAddress());

Balance storage balance = _balances[maker][app][strategyHash][token];
(uint248 prevBalance, uint8 tokensCount) = balance.load();
require(tokensCount > 0 && tokensCount != _DOCKED, PushToNonActiveStrategyPrevented(maker, app, strategyHash, token));
Expand Down
11 changes: 10 additions & 1 deletion src/libs/Multicall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@ pragma solidity 0.8.30;
/// @custom:copyright © 2025 Degensoft Ltd

contract Multicall {
error MsgValueNotAllowedForMulticall();

function multicall(bytes[] calldata data) external {
for (uint256 i = 0; i < data.length; i++) {
if (msg.value != 0 && data.length > 1) {
revert MsgValueNotAllowedForMulticall();
}

uint256 length = data.length;
for (uint256 i = 0; i < length;) {
(bool success,) = address(this).delegatecall(data[i]);
if (!success) {
assembly ("memory-safe") {
Expand All @@ -15,6 +22,8 @@ contract Multicall {
revert(ptr, returndatasize())
}
}

unchecked { ++i; }
}
}
}
Loading