Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"transactions": [
{
"hash": "0xddb4fee168bb547dbb51471592fdf67a0c11698b3ea8b02aa506b527e0c340ae",
"transactionType": "CREATE2",
"contractName": "SmardexSwapHelpers",
"contractAddress": "0x701aafa9a3928518aff3c8a041fff5a790669e01",
"function": null,
"arguments": null,
"transaction": {
"from": "0x826e0bb2276271efdf2a500597f37b94f6c153ba",
"to": "0x4e59b44847b379578588920ca78fbf26c0b4956c",
"gas": "0x46082",
"value": "0x0",
"input": "0x536d61726465785377617048656c70657273000000000000000000000000000060808060405234601557610291908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c63481bd04614610024575f80fd5b346102195760a0366003190112610219576004356001600160a01b03811690819003610219576024356001600160a01b0381169190829003610219576084356001600160a01b0381169290839003610219576060916040519161008860608461021d565b600283526020830191604036843761009f84610253565b528251600110156102055760408395949501526040928351946100c2858761021d565b60018652601f198501905f5b8281106101f5575050506100e3604435610260565b6100ee606435610260565b936001600160801b038651958160c0880194166020880152168686015260a060608601525180915260e0840192905f5b8181106101d6575050506080830152600260a0830152819003601f1981018252610148908261021d565b61015183610253565b5261015b82610253565b508051918291602083016020845282518091526020828501928260051b8601019301915f905b82821061019057505050500390f35b91936001919395506020808092603f198a82030186528189518051918291828552018484015e5f838284010152601f801991011601019601920192018594939192610181565b82516001600160a01b031685526020948501949092019160010161011e565b60208189018101839052016100ce565b634e487b7160e01b5f52603260045260245ffd5b5f80fd5b90601f8019910116810190811067ffffffffffffffff82111761023f57604052565b634e487b7160e01b5f52604160045260245ffd5b8051156102055760200190565b6001600160801b03811161027a576001600160801b031690565b6306dfcc6560e41b5f52608060045260245260445ffd",
"nonce": "0x15a",
"chainId": "0x1"
},
"additionalContracts": [],
"isFixedGasLimit": false
}
],
"receipts": [
{
"status": "0x1",
"cumulativeGasUsed": "0x19e3778",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"type": "0x2",
"transactionHash": "0xddb4fee168bb547dbb51471592fdf67a0c11698b3ea8b02aa506b527e0c340ae",
"transactionIndex": "0xcf",
"blockHash": "0xcf0d0b0677f03ae6f50fbfe1312d4d00d394ed6c44f01d3ae023d71a9546f5b4",
"blockNumber": "0x1838406",
"gasUsed": "0x2fe29",
"effectiveGasPrice": "0x2826109b",
"from": "0x826e0bb2276271efdf2a500597f37b94f6c153ba",
"to": "0x4e59b44847b379578588920ca78fbf26c0b4956c",
"contractAddress": "0x701aafa9a3928518aff3c8a041fff5a790669e01"
}
],
"libraries": [],
"pending": [],
"returns": {
"smardexSwapHelpers": {
"internal_type": "contract SmardexSwapHelpers",
"value": "0x701AaFA9A3928518aff3C8a041FfF5A790669e01"
}
},
"timestamp": 1782409696286,
"chain": 1,
"commit": "64db0dc"
}
52 changes: 52 additions & 0 deletions broadcast/SmardexSwapHelpersDeployer.s.sol/1/run-latest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"transactions": [
{
"hash": "0xddb4fee168bb547dbb51471592fdf67a0c11698b3ea8b02aa506b527e0c340ae",
"transactionType": "CREATE2",
"contractName": "SmardexSwapHelpers",
"contractAddress": "0x701aafa9a3928518aff3c8a041fff5a790669e01",
"function": null,
"arguments": null,
"transaction": {
"from": "0x826e0bb2276271efdf2a500597f37b94f6c153ba",
"to": "0x4e59b44847b379578588920ca78fbf26c0b4956c",
"gas": "0x46082",
"value": "0x0",
"input": "0x536d61726465785377617048656c70657273000000000000000000000000000060808060405234601557610291908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c63481bd04614610024575f80fd5b346102195760a0366003190112610219576004356001600160a01b03811690819003610219576024356001600160a01b0381169190829003610219576084356001600160a01b0381169290839003610219576060916040519161008860608461021d565b600283526020830191604036843761009f84610253565b528251600110156102055760408395949501526040928351946100c2858761021d565b60018652601f198501905f5b8281106101f5575050506100e3604435610260565b6100ee606435610260565b936001600160801b038651958160c0880194166020880152168686015260a060608601525180915260e0840192905f5b8181106101d6575050506080830152600260a0830152819003601f1981018252610148908261021d565b61015183610253565b5261015b82610253565b508051918291602083016020845282518091526020828501928260051b8601019301915f905b82821061019057505050500390f35b91936001919395506020808092603f198a82030186528189518051918291828552018484015e5f838284010152601f801991011601019601920192018594939192610181565b82516001600160a01b031685526020948501949092019160010161011e565b60208189018101839052016100ce565b634e487b7160e01b5f52603260045260245ffd5b5f80fd5b90601f8019910116810190811067ffffffffffffffff82111761023f57604052565b634e487b7160e01b5f52604160045260245ffd5b8051156102055760200190565b6001600160801b03811161027a576001600160801b031690565b6306dfcc6560e41b5f52608060045260245260445ffd",
"nonce": "0x15a",
"chainId": "0x1"
},
"additionalContracts": [],
"isFixedGasLimit": false
}
],
"receipts": [
{
"status": "0x1",
"cumulativeGasUsed": "0x19e3778",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"type": "0x2",
"transactionHash": "0xddb4fee168bb547dbb51471592fdf67a0c11698b3ea8b02aa506b527e0c340ae",
"transactionIndex": "0xcf",
"blockHash": "0xcf0d0b0677f03ae6f50fbfe1312d4d00d394ed6c44f01d3ae023d71a9546f5b4",
"blockNumber": "0x1838406",
"gasUsed": "0x2fe29",
"effectiveGasPrice": "0x2826109b",
"from": "0x826e0bb2276271efdf2a500597f37b94f6c153ba",
"to": "0x4e59b44847b379578588920ca78fbf26c0b4956c",
"contractAddress": "0x701aafa9a3928518aff3c8a041fff5a790669e01"
}
],
"libraries": [],
"pending": [],
"returns": {
"smardexSwapHelpers": {
"internal_type": "contract SmardexSwapHelpers",
"value": "0x701AaFA9A3928518aff3C8a041FfF5A790669e01"
}
},
"timestamp": 1782409696286,
"chain": 1,
"commit": "64db0dc"
}
18 changes: 18 additions & 0 deletions script/SmardexSwapHelpersDeployer.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;

import { SmardexSwapHelpers } from "../src/helpers/SmardexSwapHelpers.sol";
import { Script } from "forge-std/Script.sol";

contract SmardexSwapHelpersDeployer is Script {
function run() public returns (SmardexSwapHelpers smardexSwapHelpers) {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

vm.startBroadcast(deployerPrivateKey);

// Stateless pure encoder: router-agnostic, same address (CREATE2) on every chain.
smardexSwapHelpers = new SmardexSwapHelpers{ salt: "SmardexSwapHelpers" }();

vm.stopBroadcast();
}
}
29 changes: 29 additions & 0 deletions src/helpers/SmardexSwapHelpers.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";

contract SmardexSwapHelpers {
using SafeCast for uint256;

uint8 private constant PAYMENT_TRANSFER_FROM = 2;

function encodeSwapInputs(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 minAmountOut,
address to
)
external
pure
returns (bytes[] memory inputs)
{
address[] memory path = new address[](2);
path[0] = tokenIn;
path[1] = tokenOut;

inputs = new bytes[](1);
inputs[0] = abi.encode(amountIn.toUint128(), minAmountOut.toUint128(), path, to, PAYMENT_TRANSFER_FROM);
}
}
64 changes: 64 additions & 0 deletions test/unit/concrete/helpers/SmardexSwapHelpers.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;

import { SmardexSwapHelpers } from "../../../../src/helpers/SmardexSwapHelpers.sol";
import { Test } from "forge-std/Test.sol";

contract SmardexSwapHelpersTest is Test {
SmardexSwapHelpers public helpers;

uint8 internal constant PAYMENT_TRANSFER_FROM = 2;

address internal constant TOKEN_IN = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; // wstETH
address internal constant TOKEN_OUT = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // WETH
address internal constant TO = 0x00000000000000000000000000000000DeaDBeef;

function setUp() public {
helpers = new SmardexSwapHelpers();
}

function test_encodeSwapInputs_matchesRouterEncoding() public view {
uint256 amountIn = 1 ether;
uint256 minAmountOut = 0.9 ether;

bytes[] memory inputs = helpers.encodeSwapInputs(TOKEN_IN, TOKEN_OUT, amountIn, minAmountOut, TO);

assertEq(inputs.length, 1, "single input");

address[] memory path = new address[](2);
path[0] = TOKEN_IN;
path[1] = TOKEN_OUT;
bytes memory expected = abi.encode(uint128(amountIn), uint128(minAmountOut), path, TO, PAYMENT_TRANSFER_FROM);
assertEq(inputs[0], expected, "encoding must match the SmarDex swapInput layout");
}

function test_encodeSwapInputs_decodesRoundTrip() public view {
uint256 amountIn = 12_345;
uint256 minAmountOut = 678;

bytes[] memory inputs = helpers.encodeSwapInputs(TOKEN_IN, TOKEN_OUT, amountIn, minAmountOut, TO);

(uint128 dAmountIn, uint128 dMinAmountOut, address[] memory dPath, address dTo, uint8 dPayment) =
abi.decode(inputs[0], (uint128, uint128, address[], address, uint8));

assertEq(dAmountIn, uint128(amountIn), "amountIn");
assertEq(dMinAmountOut, uint128(minAmountOut), "minAmountOut");
assertEq(dPath.length, 2, "path length");
assertEq(dPath[0], TOKEN_IN, "path[0]");
assertEq(dPath[1], TOKEN_OUT, "path[1]");
assertEq(dTo, TO, "to");
assertEq(dPayment, PAYMENT_TRANSFER_FROM, "payment");
}

function test_encodeSwapInputs_revertsOnAmountInOverflow() public {
uint256 tooBig = uint256(type(uint128).max) + 1;
vm.expectRevert();
helpers.encodeSwapInputs(TOKEN_IN, TOKEN_OUT, tooBig, 0, TO);
}

function test_encodeSwapInputs_revertsOnMinAmountOutOverflow() public {
uint256 tooBig = uint256(type(uint128).max) + 1;
vm.expectRevert();
helpers.encodeSwapInputs(TOKEN_IN, TOKEN_OUT, 1 ether, tooBig, TO);
}
}
Loading