Merge branch 'refs/heads/main' into encoding/dc/ENG-4081-split-swap-strategy

# Conflicts:
#	src/encoding/evm/router_encoder.rs
#	src/encoding/evm/strategy_encoder/encoder.rs
#	src/encoding/evm/strategy_encoder/selector.rs
#	src/encoding/strategy_encoder.rs

Took 2 minutes
This commit is contained in:
Diana Carvalho
2025-01-30 16:08:08 +00:00
15 changed files with 503 additions and 36 deletions

View File

@@ -1,3 +1,45 @@
## [0.24.0](https://github.com/propeller-heads/tycho-execution/compare/0.23.0...0.24.0) (2025-01-30)
### Features
* rename batchSetExecutor to setExecutors ([c653062](https://github.com/propeller-heads/tycho-execution/commit/c65306202783ec80e8086423a4cec4261728da03))
* replace setExecutor with batchSetExecutor ([ea504fa](https://github.com/propeller-heads/tycho-execution/commit/ea504faca12bdf19f4c98946a4173167a73fba2d))
### Bug Fixes
* rm redundant test ([24e95b1](https://github.com/propeller-heads/tycho-execution/commit/24e95b1206d403a9c7b2c82b12bd14c3fc7ee6c4))
## [0.23.0](https://github.com/propeller-heads/tycho-execution/compare/0.22.0...0.23.0) (2025-01-30)
### Features
* add executor encoder test ([ad70a0d](https://github.com/propeller-heads/tycho-execution/commit/ad70a0d5a87f2a89d78de7f4ae783f6c80097407))
* add swap test with hex for univ2 executor ([0196767](https://github.com/propeller-heads/tycho-execution/commit/0196767eff1d18481e3154defd92514bd45d74b9))
* add univ2 executor test with hex ([c482e21](https://github.com/propeller-heads/tycho-execution/commit/c482e21a5f7b254a19cca53c5a86b97830a90932))
* remove exact_out from USV2 ([8cb95f0](https://github.com/propeller-heads/tycho-execution/commit/8cb95f0950e3b57ae0c6ecc3f4c0950005ae75e7))
* resolve pr comments ([1b8bf56](https://github.com/propeller-heads/tycho-execution/commit/1b8bf56c754254dc74233e28f3ae3a3992bbf0d3))
* update ExecutorEncoder interface and relevant types ([5c39651](https://github.com/propeller-heads/tycho-execution/commit/5c396512cf695dab3b0d0fec16f71b916661d54d))
## [0.22.0](https://github.com/propeller-heads/tycho-execution/compare/0.21.0...0.22.0) (2025-01-30)
### Features
* fixed USV3 Verification ([96af542](https://github.com/propeller-heads/tycho-execution/commit/96af5429232a851a7e7144b8a30843a3e6dc980e))
* Implement generic callback ([fafeba9](https://github.com/propeller-heads/tycho-execution/commit/fafeba924848f107e1a00a00cfe94347fde3d919))
* UniswapV3Executor and integration tests ([ca32446](https://github.com/propeller-heads/tycho-execution/commit/ca32446a9ee28118d8857c02abefd24389485b7e))
* USV3 verification ([7822c4f](https://github.com/propeller-heads/tycho-execution/commit/7822c4f9132b6d64a1281f6e54a8515cb0d242d3))
### Bug Fixes
* Remove amountReceived and dataOffset from the callback verification ([63b94b5](https://github.com/propeller-heads/tycho-execution/commit/63b94b55849f2087dab78ec951c459d3811409eb))
* Remove amountReceived, dataOffset from ICallbackVerifier interface ([33ada0c](https://github.com/propeller-heads/tycho-execution/commit/33ada0cf26209cd626c75e26fc6d56943988e0b1))
* Remove exactOut from USV3 encoding ([d8b44f6](https://github.com/propeller-heads/tycho-execution/commit/d8b44f623b8175f4759f8a8cbd42c46e5abad3b4))
## [0.21.0](https://github.com/propeller-heads/tycho-execution/compare/0.20.0...0.21.0) (2025-01-28)

6
Cargo.lock generated
View File

@@ -2941,7 +2941,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3"
dependencies = [
"anyhow",
"itertools 0.10.5",
"itertools 0.13.0",
"proc-macro2",
"quote",
"syn 2.0.96",
@@ -3011,7 +3011,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -4163,7 +4163,7 @@ dependencies = [
[[package]]
name = "tycho-execution"
version = "0.21.0"
version = "0.24.0"
dependencies = [
"alloy",
"alloy-primitives",

View File

@@ -1,6 +1,6 @@
[package]
name = "tycho-execution"
version = "0.21.0"
version = "0.24.0"
edition = "2021"
[dependencies]

View File

@@ -235,13 +235,15 @@ contract TychoRouter is
/**
* @dev Entrypoint to add or replace an approved executor contract address
* @param target address of the executor contract
* @param targets address of the executor contract
*/
function setExecutor(address target)
function setExecutors(address[] memory targets)
external
onlyRole(EXECUTOR_SETTER_ROLE)
{
_setExecutor(target);
for (uint256 i = 0; i < targets.length; i++) {
_setExecutor(targets[i]);
}
}
/**
@@ -379,8 +381,8 @@ contract TychoRouter is
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) internal view returns (uint256 amountOwed, address tokenOwed) {
address tokenIn = address(bytes20(data[0:20]));
) internal view returns (uint256 amountIn, address tokenIn) {
tokenIn = address(bytes20(data[0:20]));
address tokenOut = address(bytes20(data[20:40]));
uint24 poolFee = uint24(bytes3(data[40:43]));
@@ -389,9 +391,9 @@ contract TychoRouter is
_usv3Factory, tokenIn, tokenOut, poolFee
);
amountOwed =
amountIn =
amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta);
return (amountOwed, tokenOwed);
return (amountIn, tokenIn);
}
}

View File

@@ -0,0 +1,83 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;
import "@interfaces/IExecutor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
error UniswapV3Executor__InvalidDataLength();
contract UniswapV3Executor is IExecutor {
uint160 private constant MIN_SQRT_RATIO = 4295128739;
uint160 private constant MAX_SQRT_RATIO =
1461446703485210103287273052203988822378723970342;
// slither-disable-next-line locked-ether
function swap(uint256 amountIn, bytes calldata data)
external
payable
returns (uint256 amountOut)
{
(
address tokenIn,
address tokenOut,
uint24 fee,
address receiver,
address target,
bool zeroForOne
) = _decodeData(data);
int256 amount0;
int256 amount1;
IUniswapV3Pool pool = IUniswapV3Pool(target);
bytes memory callbackData = _makeV3CallbackData(tokenIn, tokenOut, fee);
{
(amount0, amount1) = pool.swap(
receiver,
zeroForOne,
// positive means exactIn
int256(amountIn),
zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1,
callbackData
);
}
if (zeroForOne) {
amountOut = amount1 > 0 ? uint256(amount1) : uint256(-amount1);
} else {
amountOut = amount0 > 0 ? uint256(amount0) : uint256(-amount0);
}
}
function _decodeData(bytes calldata data)
internal
pure
returns (
address tokenIn,
address tokenOut,
uint24 fee,
address receiver,
address target,
bool zeroForOne
)
{
if (data.length != 84) {
revert UniswapV3Executor__InvalidDataLength();
}
tokenIn = address(bytes20(data[0:20]));
tokenOut = address(bytes20(data[20:40]));
fee = uint24(bytes3(data[40:43]));
receiver = address(bytes20(data[43:63]));
target = address(bytes20(data[63:83]));
zeroForOne = uint8(data[83]) > 0;
}
function _makeV3CallbackData(address tokenIn, address tokenOut, uint24 fee)
internal
pure
returns (bytes memory)
{
return abi.encodePacked(tokenIn, tokenOut, fee);
}
}

View File

@@ -16,6 +16,8 @@ contract Constants is Test {
// Dummy contracts
address DUMMY = makeAddr("dummy");
address DUMMY2 = makeAddr("dummy2");
address DUMMY3 = makeAddr("dummy3");
address PAUSER = makeAddr("pauser");
address UNPAUSER = makeAddr("unpauser");
@@ -32,11 +34,17 @@ contract Constants is Test {
address WETH_WBTC_POOL = 0xBb2b8038a1640196FbE3e38816F3e67Cba72D940;
address USDC_WBTC_POOL = 0x004375Dff511095CC5A197A54140a24eFEF3A416;
// uniswap v3
address DAI_WETH_USV3 = 0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8;
/**
* @dev Deploys a dummy contract with non-empty bytecode
*/
function deployDummyContract() internal {
bytes memory minimalBytecode = hex"01"; // Single-byte bytecode
vm.etch(DUMMY, minimalBytecode); // Deploy minimal bytecode
// Deploy minimal bytecode
vm.etch(DUMMY, minimalBytecode);
vm.etch(DUMMY2, minimalBytecode);
vm.etch(DUMMY3, minimalBytecode);
}
}

View File

@@ -19,16 +19,31 @@ contract TychoRouterTest is TychoRouterTestSetup {
address indexed token, uint256 amount, address indexed receiver
);
function testSetExecutorValidRole() public {
function testSetExecutorsValidRole() public {
// Set single executor
address[] memory executors = new address[](1);
executors[0] = DUMMY;
vm.startPrank(EXECUTOR_SETTER);
tychoRouter.setExecutor(DUMMY);
tychoRouter.setExecutors(executors);
vm.stopPrank();
assert(tychoRouter.executors(DUMMY) == true);
// Set multiple executors
address[] memory executors2 = new address[](2);
executors2[0] = DUMMY2;
executors2[1] = DUMMY3;
vm.startPrank(EXECUTOR_SETTER);
tychoRouter.setExecutors(executors2);
vm.stopPrank();
assert(tychoRouter.executors(DUMMY2) == true);
assert(tychoRouter.executors(DUMMY3) == true);
}
function testRemoveExecutorValidRole() public {
vm.startPrank(EXECUTOR_SETTER);
tychoRouter.setExecutor(DUMMY);
address[] memory executors = new address[](1);
executors[0] = DUMMY;
tychoRouter.setExecutors(executors);
tychoRouter.removeExecutor(DUMMY);
vm.stopPrank();
assert(tychoRouter.executors(DUMMY) == false);
@@ -39,9 +54,11 @@ contract TychoRouterTest is TychoRouterTestSetup {
tychoRouter.removeExecutor(BOB);
}
function testSetExecutorMissingSetterRole() public {
function testSetExecutorsMissingSetterRole() public {
vm.expectRevert();
tychoRouter.setExecutor(DUMMY);
address[] memory executors = new address[](1);
executors[0] = DUMMY;
tychoRouter.setExecutors(executors);
}
function testSetVerifierValidRole() public {
@@ -215,7 +232,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
function testSwapSimple() public {
// Trade 1 WETH for DAI with 1 swap on Uniswap V2
// 1 WETH -> DAI
// (univ2)
// (USV2)
uint256 amountIn = 1 ether;
deal(WETH_ADDR, tychoRouterAddr, amountIn);
@@ -606,4 +623,52 @@ contract TychoRouterTest is TychoRouterTestSetup {
vm.stopPrank();
}
function testUSV3Callback() public {
uint24 poolFee = 3000;
uint256 amountOwed = 1000000000000000000;
deal(WETH_ADDR, tychoRouterAddr, amountOwed);
uint256 initialPoolReserve = IERC20(WETH_ADDR).balanceOf(DAI_WETH_USV3);
vm.startPrank(DAI_WETH_USV3);
tychoRouter.uniswapV3SwapCallback(
-2631245338449998525223,
int256(amountOwed),
abi.encodePacked(WETH_ADDR, DAI_ADDR, poolFee)
);
vm.stopPrank();
uint256 finalPoolReserve = IERC20(WETH_ADDR).balanceOf(DAI_WETH_USV3);
assertEq(finalPoolReserve - initialPoolReserve, amountOwed);
}
function testSwapSingleUSV3() public {
// Trade 1 WETH for DAI with 1 swap on Uniswap V3
// 1 WETH -> DAI
// (USV3)
uint256 amountIn = 10 ** 18;
deal(WETH_ADDR, tychoRouterAddr, amountIn);
uint256 expAmountOut = 1205_128428842122129186; //Swap 1 WETH for 1205.12 DAI
bool zeroForOne = false;
bytes memory protocolData = encodeUniswapV3Swap(
WETH_ADDR, DAI_ADDR, tychoRouterAddr, DAI_WETH_USV3, zeroForOne
);
bytes memory swap = encodeSwap(
uint8(0),
uint8(1),
uint24(0),
address(usv3Executor),
bytes4(0),
protocolData
);
bytes[] memory swaps = new bytes[](1);
swaps[0] = swap;
tychoRouter.exposedSwap(amountIn, 2, pleEncode(swaps));
uint256 finalBalance = IERC20(DAI_ADDR).balanceOf(tychoRouterAddr);
assertGe(finalBalance, expAmountOut);
}
}

View File

@@ -6,6 +6,7 @@ import "./Constants.sol";
import "./mock/MockERC20.sol";
import "@src/TychoRouter.sol";
import {WETH} from "../lib/permit2/lib/solmate/src/tokens/WETH.sol";
import "../src/executors/UniswapV3Executor.sol";
contract TychoRouterExposed is TychoRouter {
constructor(address _permit2, address weth, address usv3Factory)
@@ -34,6 +35,7 @@ contract TychoRouterTestSetup is Test, Constants {
address tychoRouterAddr;
address permit2Address = address(0x000000000022D473030F116dDEE9F6B43aC78BA3);
UniswapV2Executor public usv2Executor;
UniswapV3Executor public usv3Executor;
MockERC20[] tokens;
function setUp() public {
@@ -41,8 +43,9 @@ contract TychoRouterTestSetup is Test, Constants {
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
vm.startPrank(ADMIN);
address factoryV3 = address(0x1F98431c8aD98523631AE4a59f267346ea31F984);
tychoRouter =
new TychoRouterExposed(permit2Address, WETH_ADDR, address(1));
new TychoRouterExposed(permit2Address, WETH_ADDR, factoryV3);
tychoRouterAddr = address(tychoRouter);
tychoRouter.grantRole(keccak256("FUND_RESCUER_ROLE"), FUND_RESCUER);
tychoRouter.grantRole(keccak256("FEE_SETTER_ROLE"), FEE_SETTER);
@@ -55,8 +58,12 @@ contract TychoRouterTestSetup is Test, Constants {
vm.stopPrank();
usv2Executor = new UniswapV2Executor();
usv3Executor = new UniswapV3Executor();
vm.startPrank(EXECUTOR_SETTER);
tychoRouter.setExecutor(address(usv2Executor));
address[] memory executors = new address[](2);
executors[0] = address(usv2Executor);
executors[1] = address(usv3Executor);
tychoRouter.setExecutors(executors);
vm.stopPrank();
vm.startPrank(BOB);
@@ -190,4 +197,17 @@ contract TychoRouterTestSetup is Test, Constants {
) internal pure returns (bytes memory) {
return abi.encodePacked(tokenIn, target, receiver, zero2one);
}
function encodeUniswapV3Swap(
address tokenIn,
address tokenOut,
address receiver,
address target,
bool zero2one
) internal view returns (bytes memory) {
IUniswapV3Pool pool = IUniswapV3Pool(target);
return abi.encodePacked(
tokenIn, tokenOut, pool.fee(), receiver, target, zero2one
);
}
}

View File

@@ -80,7 +80,7 @@ contract UniswapV2ExecutorTest is UniswapV2ExecutorExposed, Test, Constants {
assertGe(amountOut, 0);
}
function testSwap() public {
function testSwapUniswapV2() public {
uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891;
bool zeroForOne = false;
@@ -93,4 +93,31 @@ contract UniswapV2ExecutorTest is UniswapV2ExecutorExposed, Test, Constants {
uint256 finalBalance = DAI.balanceOf(BOB);
assertGe(finalBalance, amountOut);
}
function testSwapExecutorEncoderData() public {
// Generated by the ExecutorStrategyEncoder - test_executor_strategy_encode
bytes memory protocolData =
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc288e6a0c2ddd26feeb64f039a2c41296fcb3f5640000000000000000000000000000000000000000100";
(IERC20 tokenIn, address target, address receiver, bool zeroForOne) =
uniswapV2Exposed.decodeParams(protocolData);
assertEq(address(tokenIn), WETH_ADDR);
assertEq(target, 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640);
assertEq(receiver, 0x0000000000000000000000000000000000000001);
assertEq(zeroForOne, false);
}
function testSwapExecutorSwap() public {
// Generated by the ExecutorStrategyEncoder - test_executor_strategy_encode
bytes memory protocolData =
hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb111d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e00";
uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891;
deal(WETH_ADDR, address(uniswapV2Exposed), amountIn);
uniswapV2Exposed.swap(amountIn, protocolData);
uint256 finalBalance = DAI.balanceOf(BOB);
assertGe(finalBalance, amountOut);
}
}

View File

@@ -0,0 +1,68 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;
import "@src/executors/UniswapV3Executor.sol";
import {Test} from "../../lib/forge-std/src/Test.sol";
import {Constants} from "../Constants.sol";
contract UniswapV3ExecutorExposed is UniswapV3Executor {
function decodeData(bytes calldata data)
external
pure
returns (
address inToken,
address outToken,
uint24 fee,
address receiver,
address target,
bool zeroForOne
)
{
return _decodeData(data);
}
}
contract UniswapV3ExecutorTest is UniswapV3ExecutorExposed, Test, Constants {
using SafeERC20 for IERC20;
UniswapV3ExecutorExposed uniswapV3Exposed;
IERC20 WETH = IERC20(WETH_ADDR);
IERC20 DAI = IERC20(DAI_ADDR);
function setUp() public {
uint256 forkBlock = 17323404;
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
uniswapV3Exposed = new UniswapV3ExecutorExposed();
}
function testDecodeParams() public view {
uint24 expectedPoolFee = 500;
bytes memory data = abi.encodePacked(
WETH_ADDR, DAI_ADDR, expectedPoolFee, address(2), address(3), false
);
(
address tokenIn,
address tokenOut,
uint24 fee,
address receiver,
address target,
bool zeroForOne
) = uniswapV3Exposed.decodeData(data);
assertEq(tokenIn, WETH_ADDR);
assertEq(tokenOut, DAI_ADDR);
assertEq(fee, expectedPoolFee);
assertEq(receiver, address(2));
assertEq(target, address(3));
assertEq(zeroForOne, false);
}
function testDecodeParamsInvalidDataLength() public {
bytes memory invalidParams =
abi.encodePacked(WETH_ADDR, address(2), address(3));
vm.expectRevert(UniswapV3Executor__InvalidDataLength.selector);
uniswapV3Exposed.decodeData(invalidParams);
}
}

View File

@@ -54,7 +54,7 @@ impl<S: StrategySelector> RouterEncoder<S> for EVMRouterEncoder<S> {
self.chain,
)?;
let contract_interaction =
let (contract_interaction,target_address) =
strategy.encode_strategy(solution.clone(), router_address)?;
let value = if solution.native_action.clone().unwrap() == NativeAction::Wrap {
@@ -62,7 +62,11 @@ impl<S: StrategySelector> RouterEncoder<S> for EVMRouterEncoder<S> {
} else {
BigUint::ZERO
};
transactions.push(Transaction { value, data: contract_interaction });
transactions.push(Transaction {
value,
data: contract_interaction,
to: target_address,
});
}
Ok(transactions)
}

View File

@@ -0,0 +1,155 @@
use std::str::FromStr;
use alloy_primitives::Address;
use alloy_sol_types::SolValue;
use tycho_core::Bytes;
use crate::encoding::{
errors::EncodingError,
evm::swap_encoder::SWAP_ENCODER_REGISTRY,
models::{EncodingContext, Solution},
strategy_encoder::StrategyEncoder,
};
#[allow(dead_code)]
pub trait EVMStrategyEncoder: StrategyEncoder {
fn encode_protocol_header(
&self,
protocol_data: Vec<u8>,
executor_address: Address,
// Token indices, split, and token inclusion are only used for split swaps
token_in: u16,
token_out: u16,
split: u16, // not sure what should be the type of this :/
) -> Vec<u8> {
let args = (executor_address, token_in, token_out, split, protocol_data);
args.abi_encode()
}
}
pub struct SplitSwapStrategyEncoder {}
impl EVMStrategyEncoder for SplitSwapStrategyEncoder {}
impl StrategyEncoder for SplitSwapStrategyEncoder {
fn encode_strategy(&self, _solution: Solution) -> Result<(Vec<u8>, Bytes), EncodingError> {
todo!()
}
fn selector(&self, _exact_out: bool) -> &str {
"swap(uint256, address, uint256, bytes[])"
}
}
/// This strategy encoder is used for solutions that are sent directly to the pool.
/// Only 1 solution with 1 swap is supported.
pub struct ExecutorStrategyEncoder {}
impl EVMStrategyEncoder for ExecutorStrategyEncoder {}
impl StrategyEncoder for ExecutorStrategyEncoder {
fn encode_strategy(&self, solution: Solution) -> Result<(Vec<u8>, Bytes), EncodingError> {
if solution.router_address.is_none() {
return Err(EncodingError::InvalidInput(
"Router address is required for straight to pool solutions".to_string(),
));
}
let swap = solution.swaps.first().unwrap();
let registry = SWAP_ENCODER_REGISTRY
.read()
.map_err(|_| {
EncodingError::FatalError("Failed to read the swap encoder registry".to_string())
})?;
let swap_encoder = registry
.get_encoder(&swap.component.protocol_system)
.ok_or_else(|| {
EncodingError::InvalidInput(format!(
"Swap encoder not found for protocol: {}",
swap.component.protocol_system
))
})?;
let router_address = solution.router_address.unwrap();
let encoding_context = EncodingContext {
receiver: solution.receiver,
exact_out: solution.exact_out,
router_address,
};
let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context)?;
let executor_address = Bytes::from_str(swap_encoder.executor_address())
.map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?;
Ok((protocol_data, executor_address))
}
fn selector(&self, _exact_out: bool) -> &str {
"swap(uint256, bytes)"
}
}
#[cfg(test)]
mod tests {
use alloy::hex::encode;
use num_bigint::BigUint;
use tycho_core::{dto::ProtocolComponent, Bytes};
use super::*;
use crate::encoding::models::Swap;
#[test]
fn test_executor_strategy_encode() {
let encoder = ExecutorStrategyEncoder {};
let token_in = Bytes::from("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2");
let token_out = Bytes::from("0x6b175474e89094c44da98b954eedeac495271d0f");
let swap = Swap {
component: ProtocolComponent {
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
protocol_system: "uniswap_v2".to_string(),
..Default::default()
},
token_in: token_in.clone(),
token_out: token_out.clone(),
split: 0f64,
};
let solution = Solution {
exact_out: false,
given_token: token_in,
given_amount: BigUint::from(1000000000000000000u64),
expected_amount: BigUint::from(1000000000000000000u64),
checked_token: token_out,
check_amount: None,
sender: Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap(),
// The receiver was generated with `makeAddr("bob") using forge`
receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(),
swaps: vec![swap],
direct_execution: true,
router_address: Some(Bytes::zero(20)),
slippage: None,
native_action: None,
};
let (protocol_data, executor_address) = encoder
.encode_strategy(solution)
.unwrap();
let hex_protocol_data = encode(&protocol_data);
assert_eq!(
executor_address,
Bytes::from_str("0x5c2f5a71f67c01775180adc06909288b4c329308").unwrap()
);
assert_eq!(
hex_protocol_data,
String::from(concat!(
// in token
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
// component id
"a478c2975ab1ea89e8196811f51a7b7ade33eb11",
// receiver
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
// zero for one
"00",
))
);
}
#[test]
fn test_selector() {
let encoder = ExecutorStrategyEncoder {};
assert_eq!(encoder.selector(false), "swap(uint256, bytes)");
}
}

View File

@@ -58,7 +58,6 @@ impl SwapEncoder for UniswapV2SwapEncoder {
component_id,
bytes_to_address(&encoding_context.receiver)?,
zero_to_one,
encoding_context.exact_out,
);
Ok(args.abi_encode_packed())
@@ -129,7 +128,6 @@ impl SwapEncoder for UniswapV3SwapEncoder {
bytes_to_address(&encoding_context.receiver)?,
component_id,
zero_to_one,
encoding_context.exact_out,
);
Ok(args.abi_encode_packed())
@@ -238,8 +236,6 @@ mod tests {
"0000000000000000000000000000000000000001",
// zero for one
"00",
// exact out
"00",
))
);
}
@@ -290,8 +286,6 @@ mod tests {
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
// zero for one
"00",
// exact out
"00",
))
);
}

View File

@@ -23,10 +23,10 @@ pub struct Solution {
pub receiver: Bytes,
/// List of swaps to fulfill the solution.
pub swaps: Vec<Swap>,
/// If set to true, the solution will be encoded to be sent directly to the SwapExecutor and
/// If set to true, the solution will be encoded to be sent directly to the Executor and
/// skip the router. The user is responsible for managing necessary approvals and token
/// transfers.
pub straight_to_pool: bool,
pub direct_execution: bool,
// if not set, then the Propeller Router will be used
pub router_address: Option<Bytes>,
// if set, it will be applied to check_amount
@@ -60,6 +60,8 @@ pub struct Transaction {
pub data: Vec<u8>,
// ETH value to be sent with the transaction.
pub value: BigUint,
// Address of the contract to call with the calldata
pub to: Bytes,
}
#[allow(dead_code)]

View File

@@ -4,11 +4,8 @@ use crate::encoding::{errors::EncodingError, models::Solution};
#[allow(dead_code)]
pub trait StrategyEncoder {
fn encode_strategy(
&self,
to_encode: Solution,
router_address: Bytes,
) -> Result<Vec<u8>, EncodingError>;
fn encode_strategy(&self, to_encode: Solution,router_address: Bytes,) -> Result<(Vec<u8>, Bytes), EncodingError>;
fn selector(&self, exact_out: bool) -> &str;
}
pub trait StrategySelector {