Skip to content

Commit a70104a

Browse files
committed
feat: add Treble dapp allocateValue logic
1 parent f51f0b1 commit a70104a

File tree

2 files changed

+55
-100
lines changed

2 files changed

+55
-100
lines changed

src/contracts/examples/trebleswap/TrebleSwapDAppControl.sol

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ import { CallConfig } from "src/contracts/types/ConfigTypes.sol";
99
import "src/contracts/types/UserOperation.sol";
1010
import "src/contracts/types/SolverOperation.sol";
1111

12-
// NOTES:
13-
// Support for native in/out?
14-
// Support for bid token (TREB) in/out?
15-
1612
// ODOS v2 Router on Base: https://basescan.org/address/0x19ceead7105607cd444f5ad10dd51356436095a1
1713
// Main function: swapCompact()
1814

@@ -26,13 +22,16 @@ struct SwapTokenInfo {
2622
contract TrebleSwapDAppControl is DAppControl {
2723
address public constant ODOS_ROUTER = 0x19cEeAd7105607Cd444F5ad10dd51356436095a1;
2824

29-
// TODO WETH on Base for now, change to TREB when token deployed
30-
address public constant TREB = address(0x4200000000000000000000000000000000000006);
25+
// TODO TREB token not available yet - replace when it is. DEGEN address for now.
26+
address public constant TREB = 0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed;
3127
address internal constant _ETH = address(0);
28+
address internal constant _BURN = address(0xdead);
3229

3330
error InvalidUserOpData();
3431
error UserOpDappNotOdosRouter();
3532
error InsufficientUserOpValue();
33+
error InsufficientTrebBalance();
34+
error InsufficientOutputBalance();
3635

3736
constructor(
3837
address atlas
@@ -73,30 +72,56 @@ contract TrebleSwapDAppControl is DAppControl {
7372
function _preOpsCall(UserOperation calldata userOp) internal virtual override returns (bytes memory) {
7473
if (userOp.dapp != ODOS_ROUTER) revert UserOpDappNotOdosRouter();
7574

76-
(bool success, bytes memory data) =
75+
(bool success, bytes memory swapData) =
7776
CONTROL.staticcall(abi.encodePacked(this.decodeUserOpData.selector, userOp.data));
7877

7978
if (!success) revert InvalidUserOpData();
8079

81-
SwapTokenInfo memory swapTokenInfo = abi.decode(data, (SwapTokenInfo));
80+
SwapTokenInfo memory _swapInfo = abi.decode(swapData, (SwapTokenInfo));
8281

8382
// If inputToken is ERC20, transfer tokens from user to EE, and approve Odos router for swap
84-
if (swapTokenInfo.inputToken != _ETH) {
85-
_transferUserERC20(swapTokenInfo.inputToken, address(this), swapTokenInfo.inputAmount);
86-
SafeTransferLib.safeApprove(swapTokenInfo.inputToken, ODOS_ROUTER, swapTokenInfo.inputAmount);
83+
if (_swapInfo.inputToken != _ETH) {
84+
_transferUserERC20(_swapInfo.inputToken, address(this), _swapInfo.inputAmount);
85+
SafeTransferLib.safeApprove(_swapInfo.inputToken, ODOS_ROUTER, _swapInfo.inputAmount);
8786
} else {
88-
if (userOp.value < swapTokenInfo.inputAmount) revert InsufficientUserOpValue();
87+
if (userOp.value < _swapInfo.inputAmount) revert InsufficientUserOpValue();
8988
}
9089

91-
return data; // return SwapTokenInfo in bytes format, to be used in allocateValue.
90+
return swapData; // return SwapTokenInfo in bytes format, to be used in allocateValue.
9291
}
9392

94-
// UserOperation happens here. EE calls router (userOp.dapp) with userOp.data as calldata.
93+
function _allocateValueCall(address, uint256 bidAmount, bytes calldata data) internal virtual override {
94+
SwapTokenInfo memory _swapInfo = abi.decode(data, (SwapTokenInfo));
95+
uint256 _outputTokenBalance = _balanceOf(_swapInfo.outputToken);
96+
97+
// Check enough output token was received after swap
98+
if (_swapInfo.outputToken == TREB) {
99+
// If outputToken is TREB, check there is enough to burn the solver bid and fulfill user swap
100+
if (_outputTokenBalance < bidAmount + _swapInfo.outputMin) revert InsufficientTrebBalance();
101+
_outputTokenBalance -= bidAmount; // deduct solver bid to get final user output amount to send
102+
} else {
103+
if (_outputTokenBalance < _swapInfo.outputMin) revert InsufficientOutputBalance();
104+
}
105+
106+
// Transfer output token to user
107+
if (_swapInfo.outputToken == _ETH) {
108+
SafeTransferLib.safeTransferETH(_user(), _outputTokenBalance);
109+
} else {
110+
SafeTransferLib.safeTransfer(_swapInfo.outputToken, _user(), _outputTokenBalance);
111+
}
112+
113+
// If solver won, burn TREB bid
114+
if (bidAmount > 0) SafeTransferLib.safeTransfer(TREB, _BURN, bidAmount);
95115

96-
function _allocateValueCall(address bidToken, uint256 bidAmount, bytes calldata data) internal virtual override {
97-
// Solver bid (in TREB) gets burnt here
98-
// Send user back their tokenOut from the swap, and any leftover tokenIn
99-
// Revert here if swap fails
116+
// If any leftover input token, transfer back to user
117+
uint256 _inputTokenBalance = _balanceOf(_swapInfo.inputToken);
118+
if (_inputTokenBalance > 0) {
119+
if (_swapInfo.inputToken == _ETH) {
120+
SafeTransferLib.safeTransferETH(_user(), _inputTokenBalance);
121+
} else {
122+
SafeTransferLib.safeTransfer(_swapInfo.inputToken, _user(), _inputTokenBalance);
123+
}
124+
}
100125
}
101126

102127
// ---------------------------------------------------- //
@@ -184,4 +209,12 @@ contract TrebleSwapDAppControl is DAppControl {
184209
}
185210
}
186211
}
212+
213+
function _balanceOf(address token) internal view returns (uint256) {
214+
if (token == _ETH) {
215+
return address(this).balance;
216+
} else {
217+
return SafeTransferLib.balanceOf(token, address(this));
218+
}
219+
}
187220
}

test/TrebleSwap.t.sol

Lines changed: 4 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ contract TrebleSwapTest is BaseTest {
4848
IERC20 bWETH = IERC20(0x4200000000000000000000000000000000000006);
4949
IERC20 USDC = IERC20(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913);
5050
IERC20 WUF = IERC20(0x4da78059D97f155E18B37765e2e042270f4E0fC4);
51+
IERC20 TREB = IERC20(0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed);
5152

5253
address executionEnvironment;
5354

@@ -90,8 +91,9 @@ contract TrebleSwapTest is BaseTest {
9091
bytes memory swapCompactCalldata =
9192
hex"83bd37f9000400014da78059d97f155e18b37765e2e042270f4e0fc4040bc108800601d1d9f50a5a028f5c0001f73f77f9466da712590ae432a80f07fd50a7de600001616535324976f8dbcef19df0705b95ace86ebb480001736F6980876FDa51A610AB79E2856528a62Bf80e0000000006020207003401000001020180000005020a0004040500000301010003060119ff0000000000000000000000000000000000000000000000000000000000000000616535324976f8dbcef19df0705b95ace86ebb48833589fcd6edb6e08f4c7c32d4f71b54bda02913569d81c17b5b4ac08929dc1769b8e39668d3ae29f6c0a374a483101e04ef5f7ac9bd15d9142bac95d9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca42000000000000000000000000000000000000060000000000000000";
9293

93-
bytes memory encodedCall = abi.encodePacked(this.decodeSwapCompactCalldata.selector, swapCompactCalldata);
94-
(bool res, bytes memory returnData) = address(this).staticcall(encodedCall);
94+
bytes memory encodedCall =
95+
abi.encodePacked(TrebleSwapDAppControl.decodeUserOpData.selector, swapCompactCalldata);
96+
(bool res, bytes memory returnData) = address(trebleSwapControl).staticcall(encodedCall);
9597

9698
console.log("res", res);
9799

@@ -121,86 +123,6 @@ contract TrebleSwapTest is BaseTest {
121123
signature: new bytes(0)
122124
});
123125
}
124-
125-
// struct swapTokenInfo {
126-
// address inputToken;
127-
// uint256 inputAmount;
128-
// address outputToken;
129-
// uint256 outputMin;
130-
// }
131-
132-
// TODO once this works, move to TrebleSwapDAppControl
133-
function decodeSwapCompactCalldata() public view returns (SwapTokenInfo memory swapTokenInfo) {
134-
assembly {
135-
// helper function to get address either from storage or calldata
136-
function getAddress(currPos) -> result, newPos {
137-
let inputPos := shr(240, calldataload(currPos))
138-
139-
switch inputPos
140-
// Reserve the null address as a special case that can be specified with 2 null bytes
141-
case 0x0000 { newPos := add(currPos, 2) }
142-
// This case means that the address is encoded in the calldata directly following the code
143-
case 0x0001 {
144-
result := and(shr(80, calldataload(currPos)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
145-
newPos := add(currPos, 22)
146-
}
147-
default {
148-
// 0000 and 0001 are reserved for cases above, so offset by 2 for addressList index
149-
let arg := sub(inputPos, 2)
150-
let selector := 0xb810fb43 // function selector for "addressList(uint256)"
151-
let ptr := mload(0x40) // get the free memory pointer
152-
mstore(ptr, shl(224, selector)) // shift selector to left of slot and store
153-
mstore(add(ptr, 4), arg) // store the uint256 argument after the selector
154-
155-
// Perform the external call
156-
let success :=
157-
staticcall(
158-
gas(), // gas remaining
159-
ODOS_ROUTER,
160-
ptr, // input location
161-
0x24, // input size (4 byte selector + uint256 arg)
162-
ptr, // output location
163-
0x20 // output size (32 bytes for the address)
164-
)
165-
166-
if eq(success, 0) { revert(0, 0) }
167-
168-
result := mload(ptr)
169-
newPos := add(currPos, 2)
170-
}
171-
}
172-
173-
let result := 0
174-
let pos := 8 // starts at 4 to skip the selector
175-
176-
// swapTokenInfo.inputToken (slot 0)
177-
result, pos := getAddress(pos)
178-
mstore(swapTokenInfo, result)
179-
180-
// swapTokenInfo.outputToken (slot 2)
181-
result, pos := getAddress(pos)
182-
mstore(add(swapTokenInfo, 0x40), result)
183-
184-
// swapTokenInfo.inputAmount (slot 1)
185-
let inputAmountLength := shr(248, calldataload(pos))
186-
pos := add(pos, 1)
187-
if inputAmountLength {
188-
mstore(add(swapTokenInfo, 0x20), shr(mul(sub(32, inputAmountLength), 8), calldataload(pos)))
189-
pos := add(pos, inputAmountLength)
190-
}
191-
192-
// swapTokenInfo.outputMin (slot 3)
193-
// get outputQuote and slippageTolerance from calldata, then calculate outputMin
194-
let quoteAmountLength := shr(248, calldataload(pos))
195-
pos := add(pos, 1)
196-
let outputQuote := shr(mul(sub(32, quoteAmountLength), 8), calldataload(pos))
197-
pos := add(pos, quoteAmountLength)
198-
{
199-
let slippageTolerance := shr(232, calldataload(pos))
200-
mstore(add(swapTokenInfo, 0x60), div(mul(outputQuote, sub(0xFFFFFF, slippageTolerance)), 0xFFFFFF))
201-
}
202-
}
203-
}
204126
}
205127

206128
interface IOdosRouterV2 {

0 commit comments

Comments
 (0)