Skip to content

Commit 195bb2a

Browse files
authored
Merge pull request #43 from 1inch/feature/dynamic-fees
added dynamic fees
2 parents fb183be + addc6b1 commit 195bb2a

67 files changed

Lines changed: 2193 additions & 1829 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gas-snapshot

Lines changed: 554 additions & 499 deletions
Large diffs are not rendered by default.

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ jobs:
3737
git config --global url."https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/".insteadOf "https://github.com/"
3838
git config --global url."https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/".insteadOf "git@github.com:"
3939
git config --global url."https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/".insteadOf "ssh://git@github.com/"
40+
41+
- name: Setup npm authentication for GitHub Packages
42+
run: |
43+
echo "@1inch:registry=https://npm.pkg.github.com" >> .npmrc
44+
echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" >> .npmrc
45+
4046
- name: Install node modules
4147
run: npm install
4248

README.md

Lines changed: 72 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![Github Release](https://img.shields.io/github/v/tag/1inch/swap-vm?sort=semver&label=github)](https://github.com/1inch/swap-vm/releases/latest)
44
[![CI](https://github.com/1inch/swap-vm/actions/workflows/ci.yml/badge.svg)](https://github.com/1inch/swap-vm/actions/workflows/ci.yml)
5-
[![Coverage](https://img.shields.io/badge/Coverage-50.46%25-yellow)](https://github.com/1inch/swap-vm)
5+
[![Coverage](https://img.shields.io/badge/Coverage-85%25-green)](https://github.com/1inch/swap-vm)
66
[![Tests](https://img.shields.io/github/actions/workflow/status/1inch/swap-vm/ci.yml?branch=main&label=tests)](https://github.com/1inch/swap-vm/actions)
77
[![npm](https://img.shields.io/npm/v/@1inch/swap-vm.svg)](https://www.npmjs.com/package/@1inch/swap-vm)
88
[![License](https://img.shields.io/badge/License-Degensoft--SwapVM--1.1-orange)](LICENSE)
@@ -123,7 +123,9 @@ The execution flow shows all available instructions and strategies for each bala
123123
│ ├─ _progressiveFeeInXD → Size-based dynamic fee (input)│
124124
│ ├─ _progressiveFeeOutXD → Size-based dynamic fee (output)│
125125
│ ├─ _protocolFeeAmountOutXD → Protocol revenue (ERC20) │
126-
│ └─ _aquaProtocolFeeAmountOutXD → Protocol revenue (Aqua)│
126+
│ ├─ _aquaProtocolFeeAmountOutXD → Protocol revenue (Aqua)│
127+
│ ├─ _dynamicProtocolFeeAmountInXD → Dynamic fee via provider│
128+
│ └─ _aquaDynamicProtocolFeeAmountInXD → Dynamic Aqua fee│
127129
│ │
128130
│ 6. Advanced Strategies (Optional) │
129131
│ ├─ _requireMinRate1D → Enforce minimum exchange rate │
@@ -174,6 +176,7 @@ The execution flow shows all available instructions and strategies for each bala
174176
│ │
175177
│ 2. AMM Logic (Choose Primary Strategy) │
176178
│ ├─ _xycSwapXD → Classic x*y=k constant product │
179+
│ ├─ _peggedSwapGrowPriceRange2D → Curve for pegged assets│
177180
│ └─ _xycConcentrateGrowLiquidityXD/2D → CLMM ranges │
178181
│ │
179182
│ 3. Fee Mechanisms (Optional, Combinable) │
@@ -182,7 +185,9 @@ The execution flow shows all available instructions and strategies for each bala
182185
│ ├─ _progressiveFeeInXD → Size-based dynamic fee (input) │
183186
│ ├─ _progressiveFeeOutXD → Size-based dynamic fee (output)│
184187
│ ├─ _protocolFeeAmountOutXD → Protocol revenue (ERC20) │
185-
│ └─ _aquaProtocolFeeAmountOutXD → Protocol revenue (Aqua)│
188+
│ ├─ _aquaProtocolFeeAmountOutXD → Protocol revenue (Aqua)│
189+
│ ├─ _dynamicProtocolFeeAmountInXD → Dynamic fee via provider│
190+
│ └─ _aquaDynamicProtocolFeeAmountInXD → Dynamic Aqua fee │
186191
│ │
187192
│ 4. MEV Protection (Optional) │
188193
│ └─ _decayXD → Virtual reserves (Mooniswap-style) │
@@ -410,21 +415,28 @@ bytes memory program = bytes.concat(
410415
);
411416
412417
// 2. Configure order parameters
413-
MakerTraits makerTraits = MakerTraitsLib.build(MakerTraitsLib.Args({
418+
ISwapVM.Order memory order = MakerTraitsLib.build(MakerTraitsLib.Args({
419+
maker: yourAddress, // Your address
420+
receiver: address(0), // You receive the tokens (0 = maker)
414421
shouldUnwrapWeth: false, // Keep WETH (don't unwrap to ETH)
415-
expiration: block.timestamp + 1 days, // Order expires in 24h
416-
receiver: address(0), // You receive the tokens
417-
useAquaInsteadOfSignature: false // Use standard EIP-712 signing
422+
useAquaInsteadOfSignature: false, // Use standard EIP-712 signing
423+
allowZeroAmountIn: false, // Require non-zero input
424+
hasPreTransferInHook: false,
425+
hasPostTransferInHook: false,
426+
hasPreTransferOutHook: false,
427+
hasPostTransferOutHook: false,
428+
preTransferInTarget: address(0),
429+
preTransferInData: "",
430+
postTransferInTarget: address(0),
431+
postTransferInData: "",
432+
preTransferOutTarget: address(0),
433+
preTransferOutData: "",
434+
postTransferOutTarget: address(0),
435+
postTransferOutData: "",
436+
program: program // Your swap program
418437
}));
419438
420-
// 3. Create order (completely off-chain)
421-
ISwapVM.Order memory order = ISwapVM.Order({
422-
maker: yourAddress,
423-
traits: makerTraits,
424-
program: program
425-
});
426-
427-
// 4. Sign order off-chain (gasless)
439+
// 3. Sign order off-chain (gasless)
428440
bytes32 orderHash = swapVM.hash(order);
429441
bytes memory signature = signEIP712(orderHash);
430442
```
@@ -499,7 +511,7 @@ MakerTraits makerTraits = MakerTraitsLib.build({
499511
**Use Case:** Share liquidity across multiple strategies
500512
**Key Difference:** Unlike isolated dynamic balances, Aqua enables shared liquidity
501513

502-
See [Aqua Protocol](https://github.com/1inch/aqua-protocol) for details
514+
See [Aqua Protocol](https://github.com/1inch/aqua) for details
503515

504516
### Maker Security
505517

@@ -657,45 +669,51 @@ Context
657669

658670
```
659671
MakerTraits (256-bit packed)
660-
├── Token Handling
661-
│ └── shouldUnwrapWeth ────────── Unwrap WETH to ETH on output
662-
663-
├── Order Lifecycle
664-
│ └── expiration (40 bits) ────── Unix timestamp when order expires
665-
666-
├── Balance Management
667-
│ ├── useAquaInsteadOfSignature ─ Use Aqua balance instead of signature
668-
│ └── allowZeroAmountIn ─── Skip Aqua for input transfers
672+
├── Bit Flags (bits 245-255)
673+
│ ├── shouldUnwrapWeth (255) ──── Unwrap WETH to ETH on output
674+
│ ├── useAquaInsteadOfSignature (254) ─ Use Aqua balance instead of signature
675+
│ ├── allowZeroAmountIn (253) ── Allow zero amountIn (skip validation)
676+
│ ├── hasPreTransferInHook (252) ── Call maker before input transfer
677+
│ ├── hasPostTransferInHook (251) ── Call maker after input transfer
678+
│ ├── hasPreTransferOutHook (250) ── Call maker before output transfer
679+
│ ├── hasPostTransferOutHook (249) ── Call maker after output transfer
680+
│ ├── preTransferInHookHasTarget (248) ── Hook has custom target
681+
│ ├── postTransferInHookHasTarget (247)
682+
│ ├── preTransferOutHookHasTarget (246)
683+
│ └── postTransferOutHookHasTarget (245)
669684
670-
├── Recipient Control
671-
│ └── receiver ─────────────────── Custom recipient (0 = maker)
685+
├── Data Slices Indexes (bits 160-223, 64 bits)
686+
│ └── Packed 4x uint16 offsets for hook data slices
672687
673-
└── Hooks (Callbacks)
674-
├── hasPreTransferOutHook ────── Call maker before output transfer
675-
├── hasPostTransferInHook ────── Call maker after input transfer
676-
├── preTransferOutData ────────── Data for pre-transfer hook
677-
└── postTransferInData ────────── Data for post-transfer hook
688+
└── Receiver (bits 0-159, 160 bits)
689+
└── Custom recipient address (0 = maker)
678690
```
679691

680692
```
681-
TakerTraits (Variable-length)
682-
├── Swap Direction
683-
│ └── isExactIn ────────────────── true = specify input, false = output
693+
TakerTraits (Variable-length with 176-bit header)
694+
├── Header (22 bytes packed)
695+
│ ├── Slices Indexes (160 bits) ── 10x uint16 offsets for data slices
696+
│ └── Bit Flags (16 bits)
697+
│ ├── isExactIn (0) ────────── true = specify input, false = output
698+
│ ├── shouldUnwrapWeth (1) ── Unwrap WETH to ETH on output
699+
│ ├── hasPreTransferInCallback (2) ── Call taker before input transfer
700+
│ ├── hasPreTransferOutCallback (3) ── Call taker before output transfer
701+
│ ├── isStrictThresholdAmount (4) ── true = exact, false = min/max
702+
│ ├── isFirstTransferFromTaker (5) ── Who transfers first
703+
│ └── useTransferFromAndAquaPush (6) ── SwapVM does transferFrom + Aqua push
684704
685-
├── Amount Validation
686-
│ ├── threshold ────────────────── Min output or max input
687-
│ └── isStrictThresholdAmount ─── true = exact, false = min/max
688-
689-
├── Token Handling
690-
│ ├── shouldUnwrapWeth ─────────── Unwrap WETH to ETH on output
691-
│ └── to ───────────────────────── Custom recipient (0 = taker)
692-
693-
├── Transfer Mechanics
694-
│ ├── isFirstTransferFromTaker ── Who transfers first
695-
│ └── useTransferFromAndAquaPush ─ SwapVM does transferFrom + Aqua push (vs taker pushes in callback)
696-
697-
└── Hooks (Callbacks)
698-
└── hasPreTransferInHook ─────── Call taker before input transfer
705+
└── Variable-length Data Slices
706+
├── threshold (0 or 32 bytes) ── Min output or max input
707+
├── to (0 or 20 bytes) ───────── Custom recipient
708+
├── deadline (0 or 5 bytes) ──── Unix timestamp (uint40)
709+
├── preTransferInHookData ────── Data for maker pre-in hook
710+
├── postTransferInHookData ───── Data for maker post-in hook
711+
├── preTransferOutHookData ──── Data for maker pre-out hook
712+
├── postTransferOutHookData ─── Data for maker post-out hook
713+
├── preTransferInCallbackData ─ Data for taker pre-in callback
714+
├── preTransferOutCallbackData ─ Data for taker pre-out callback
715+
├── instructionsArgs ──────────── Data consumed by VM instructions
716+
└── signature ─────────────────── EIP-712 signature for order
699717
```
700718

701719
### Instruction Capabilities
@@ -879,7 +897,7 @@ if (useAquaInsteadOfSignature) {
879897
| Feature | Description | Implementation |
880898
|---------|-------------|----------------|
881899
| **Signature Control** | Orders cannot be modified | EIP-712 signatures |
882-
| **Expiration** | Time-limited orders | `expiration` in MakerTraits |
900+
| **Expiration** | Time-limited orders | `_deadline` instruction or TakerTraits deadline |
883901
| **Balance Limits** | Cannot exceed specified amounts | Register bounds checking |
884902
| **One-time Execution** | Prevent replay | `_invalidateBit1D` instruction |
885903
| **Custom Logic** | Hooks for validation | Pre/post transfer hooks |
@@ -890,8 +908,10 @@ if (useAquaInsteadOfSignature) {
890908
// Limit order exposure
891909
p.build(Invalidators._invalidateBit1D, bitIndex);
892910
893-
// Add expiration
894-
traits.expiration = block.timestamp + 1 hours;
911+
// Add expiration via _deadline instruction in program
912+
p.build(Controls._deadline, ControlsArgsBuilder.buildDeadline(block.timestamp + 1 hours));
913+
914+
// Or via TakerTraits deadline field
895915
896916
// MEV protection
897917
p.build(Decay._decayXD, DecayArgsBuilder.build(30));

THIRD_PARTY_NOTICES

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ License: /LICENSES/MIT-OpenZeppelin.txt
2424

2525
Component: @1inch/solidity-utils
2626
Repo: https://github.com/1inch/solidity-utils
27-
Version: v6.8.0
27+
Version: v6.9.0
2828
License: MIT
2929
Copyright: © 2019, 1inch
3030
License: /LICENSES/MIT-1inch.txt
@@ -33,7 +33,7 @@ License: /LICENSES/MIT-1inch.txt
3333

3434
Component: @1inch/aqua
3535
Repo: https://github.com/1inch/aqua
36-
Version: 0.0.7
36+
Version: 0.0.8
3737
License: LicenseRef-Degensoft-Aqua-Source-1.1
3838
Copyright: © 2025 Degensoft Ltd
3939
License: /LICENSES/Aqua-Source-1.1.txt

mocks/ProtocolFeeProviderMock.sol

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// SPDX-License-Identifier: LicenseRef-Degensoft-SwapVM-1.1
2+
pragma solidity 0.8.30;
3+
4+
/// @custom:license-url https://github.com/1inch/swap-vm/blob/main/LICENSES/SwapVM-1.1.txt
5+
/// @custom:copyright © 2025 Degensoft Ltd
6+
7+
import "@openzeppelin/contracts/access/Ownable.sol";
8+
9+
import "../src/instructions/interfaces/IProtocolFeeProvider.sol";
10+
11+
/**
12+
* @title ProtocolFeeProviderMock
13+
* @notice Mock implementation of IProtocolFeeProvider for testing dynamic protocol fees
14+
* @dev This contract is used with `_dynamicProtocolFeeAmountInXD`
15+
* instructions in SwapVM programs to provide configurable protocol fee parameters.
16+
*
17+
* ## Usage in SwapVM Programs
18+
*
19+
* Protocol fee instructions must be placed BEFORE balances for correct operation:
20+
* ```solidity
21+
* bytes memory bytecode = bytes.concat(
22+
* // Dynamic protocol fee BEFORE balances
23+
* program.build(_dynamicProtocolFeeAmountInXD, abi.encodePacked(address(feeProvider))),
24+
* // Balances instruction
25+
* program.build(_dynamicBalancesXD, BalancesArgsBuilder.build(...)),
26+
* // Other fees AFTER balances (flat, progressive)
27+
* program.build(_flatFeeAmountInXD, FeeArgsBuilder.buildFlatFee(feeBps)),
28+
* // Swap instruction
29+
* program.build(_xycSwapXD)
30+
* );
31+
* ```
32+
*
33+
* ## Fee Calculation
34+
*
35+
* The fee is calculated as: `feeAmount = amount * feeBps / 1e9`
36+
* where feeBps is in basis points with 9 decimal precision (0.001e9 = 0.1%)
37+
*
38+
* ## Example
39+
*
40+
* ```solidity
41+
* // Create fee provider with 0.2% fee
42+
* ProtocolFeeProviderMock feeProvider = new ProtocolFeeProviderMock(
43+
* 0.002e9, // feeBps: 0.2% in 1e9 scale
44+
* feeRecipient, // address to receive fees
45+
* owner // owner who can update settings
46+
* );
47+
* ```
48+
*
49+
* @custom:security This is a mock contract for testing only.
50+
* Production implementations should include access control and validation.
51+
*/
52+
contract ProtocolFeeProviderMock is IProtocolFeeProvider, Ownable {
53+
/// @notice Fee rate in basis points (1e9 scale, e.g., 0.002e9 = 0.2%)
54+
uint32 private _feeBps;
55+
56+
/// @notice Address that receives protocol fees
57+
address private _to;
58+
59+
/**
60+
* @notice Creates a new ProtocolFeeProviderMock
61+
* @param feeBps Fee rate in basis points (1e9 scale)
62+
* Examples: 0.001e9 = 0.1%, 0.002e9 = 0.2%, 0.01e9 = 1%
63+
* @param to Address to receive collected protocol fees
64+
* @param owner Address with permission to update fee settings
65+
*/
66+
constructor(uint32 feeBps, address to, address owner) Ownable(owner) {
67+
_feeBps = feeBps;
68+
_to = to;
69+
}
70+
71+
/**
72+
* @notice Updates fee rate and recipient address
73+
* @dev Can be called by anyone (no access control in mock)
74+
* Production implementation should restrict to owner
75+
* @param feeBps New fee rate in basis points (1e9 scale)
76+
* @param to New address to receive protocol fees
77+
*/
78+
function setFeeBpsAndRecipient(uint32 feeBps, address to) onlyOwner external {
79+
_feeBps = feeBps;
80+
_to = to;
81+
}
82+
83+
/// @inheritdoc IProtocolFeeProvider
84+
function getFeeBpsAndRecipient(
85+
bytes32 /* orderHash */,
86+
address /* maker */,
87+
address /* taker */,
88+
address /* tokenIn */,
89+
address /* tokenOut */,
90+
bool /* isExactIn */
91+
) external view override returns (uint32 feeBps, address to) {
92+
return (_feeBps, _to);
93+
}
94+
}

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
22
"name": "@1inch/swap-vm",
3-
"version": "0.0.2",
3+
"version": "0.0.3",
44
"description": "Swap Virtual Machine protocol to build token swap strategies with programmable instruction sequences",
55
"main": "index.js",
66
"directories": {
77
"lib": "lib",
88
"test": "test"
99
},
1010
"dependencies": {
11-
"@1inch/aqua": "github:1inch/aqua#0.0.7",
12-
"@1inch/solidity-utils": "6.8.0",
11+
"@1inch/aqua": "github:1inch/aqua#0.0.8",
12+
"@1inch/solidity-utils": "6.9.0",
1313
"@openzeppelin/contracts": "5.4.0",
1414
"forge-std": "github:foundry-rs/forge-std#v1.11.0"
1515
},

snapshots/AMMGas.json

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
{
2-
"ConcentrateGrowLiquidity_XYCSwap_quote_exactIn": "45881",
3-
"ConcentrateGrowLiquidity_XYCSwap_quote_exactOut": "46586",
4-
"ConcentrateGrowLiquidity_XYCSwap_swap_exactIn": "171299",
5-
"ConcentrateGrowLiquidity_XYCSwap_swap_exactOut": "172002",
6-
"ConcentrateGrowPriceRange_XYCSwap_quote_exactIn": "42165",
7-
"ConcentrateGrowPriceRange_XYCSwap_swap_exactIn": "143515",
8-
"Concentrate_Decay_XYCSwap_quote_exactIn": "55409",
9-
"Concentrate_Decay_XYCSwap_swap_exactIn": "228143",
10-
"Decay_XYCSwap_quote_exactIn": "48159",
11-
"Decay_XYCSwap_quote_exactOut": "48864",
12-
"Decay_XYCSwap_swap_exactIn": "196825",
13-
"Decay_XYCSwap_swap_exactOut": "197529",
14-
"FullAMM_quote_exactIn": "58858",
15-
"FullAMM_swap_exactIn": "231592",
16-
"XYCSwap_FlatFeeIn_quote_exactIn": "42077",
17-
"XYCSwap_FlatFeeIn_swap_exactIn": "143426",
18-
"XYCSwap_FlatFeeOut_quote_exactIn": "42066",
19-
"XYCSwap_FlatFeeOut_swap_exactIn": "143415",
20-
"XYCSwap_quote_exactIn": "38632",
21-
"XYCSwap_quote_exactOut": "39424",
22-
"XYCSwap_swap_exactIn": "139981",
23-
"XYCSwap_swap_exactOut": "140772"
2+
"ConcentrateGrowLiquidity_XYCSwap_quote_exactIn": "46003",
3+
"ConcentrateGrowLiquidity_XYCSwap_quote_exactOut": "46708",
4+
"ConcentrateGrowLiquidity_XYCSwap_swap_exactIn": "171421",
5+
"ConcentrateGrowLiquidity_XYCSwap_swap_exactOut": "172125",
6+
"ConcentrateGrowPriceRange_XYCSwap_quote_exactIn": "42287",
7+
"ConcentrateGrowPriceRange_XYCSwap_swap_exactIn": "143637",
8+
"Concentrate_Decay_XYCSwap_quote_exactIn": "55530",
9+
"Concentrate_Decay_XYCSwap_swap_exactIn": "228265",
10+
"Decay_XYCSwap_quote_exactIn": "48280",
11+
"Decay_XYCSwap_quote_exactOut": "48986",
12+
"Decay_XYCSwap_swap_exactIn": "196947",
13+
"Decay_XYCSwap_swap_exactOut": "197652",
14+
"FullAMM_quote_exactIn": "58979",
15+
"FullAMM_swap_exactIn": "231714",
16+
"XYCSwap_FlatFeeIn_quote_exactIn": "42198",
17+
"XYCSwap_FlatFeeIn_swap_exactIn": "143548",
18+
"XYCSwap_FlatFeeOut_quote_exactIn": "42187",
19+
"XYCSwap_FlatFeeOut_swap_exactIn": "143537",
20+
"XYCSwap_quote_exactIn": "38753",
21+
"XYCSwap_quote_exactOut": "39545",
22+
"XYCSwap_swap_exactIn": "140103",
23+
"XYCSwap_swap_exactOut": "140894"
2424
}

0 commit comments

Comments
 (0)