I have created a swap contract for superfluid(https://superfluid.finance) super tokens, which swaps anySuperToken <-> anySuperToken
, Essentially its just an ERC20 token with additional capabilities.
My swap contract works the following way -
- To swap SuperTokenA -> SuperTokenB.
- It unwraps the SuperTokenA -> TokenA, using the function downgrade.
- Swaps for the desired token using uniswapV3.
- Upgrades to superToken of superfluid and send the tokens back to the user.
This is my complete contract for the super token swap, the contract is also deployed on -
Rinkeby - https://rinkeby.etherscan.io/address/0xa48fe52fe42e7107c717d82dfa61cf5ff6919347 Polygon - https://polygonscan.com/address/0x0983c50085C81A7F41598Eb46a29001989102119
The same contract is deployed in both the places, I also tested the contract on Rinkeby - https://rinkeby.etherscan.io/tx/0xa896eadf0825365e2a7d9bd6c660c6f1d594935e79859657da040b7aeeefdebb where the swap is successful.
But I am getting STF error on polygon - which generally means that I have not given approval to spend the token, but I have given the approval.
These are the parameters I am using if someone wants to try it out -
_from = 0xCAa7349CEA390F89641fe306D93591f87595dc1F
_to = 0x27e1e4E6BC79D93032abef01025811B7E4727e85
amountIn = 10000000000000
amountOutMin = 0
path = [0x2791bca1f2de4661ed88a30c99a7a9449aa84174, 0x7ceb23fd6bc0add59e62ac25578270cff1b9f619]
poolFees = [500] // There is a uniswap USDC/WETH pool with 0.05% fees
The complete contract can also be found here - https://github.com/Ricochet-Exchange/ricochet-super-swap/blob/main/contracts/RexSuperSwap.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
pragma abicoder v2;
import {ISuperToken} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
import "@uniswap/swap-router-contracts/contracts/interfaces/ISwapRouter02.sol";
contract RexSuperSwap {
ISwapRouter02 public immutable swapRouter;
event SuperSwapComplete(uint256 amountOut);
constructor(ISwapRouter02 _swapRouter) {
swapRouter = _swapRouter;
}
/**
* @dev Swaps `amountIn` of `_from` SuperToken for at least `amountOutMin`
* of `_to` SuperToken through `path` with `poolFees` fees for each pair.
*
* Returns the amount of `_to` SuperToken received.
*/
function swap(
ISuperToken _from,
ISuperToken _to,
uint256 amountIn,
uint256 amountOutMin,
address[] memory path,
uint24[] memory poolFees // Example: 0.3% * 10000 = 3000
) external returns (uint256 amountOut) {
require(amountIn > 0, "Amount cannot be 0");
require(path.length > 1, "Incorrect path");
require(
poolFees.length == path.length - 1,
"Incorrect poolFees length"
);
// Step 1: Get underlying tokens and verify path
address fromBase = _from.getUnderlyingToken();
address toBase = _to.getUnderlyingToken();
require(path[0] == fromBase, "Invalid 'from' base token");
require(path[path.length - 1] == toBase, "Invalid 'to' base token");
// Step 2: Transfer SuperTokens from sender
TransferHelper.safeTransferFrom(
address(_from),
msg.sender,
address(this),
amountIn
);
// Step 3: Downgrade
_from.downgrade(amountIn);
// Step 4: Approve and Swap
// Encode the path for swap
bytes memory encodedPath;
for (uint256 i = 0; i < path.length; i++) {
if (i == path.length - 1) {
encodedPath = abi.encodePacked(encodedPath, path[i]);
} else {
encodedPath = abi.encodePacked(
encodedPath,
path[i],
poolFees[i]
);
}
}
// Approve the router to spend token supplied (fromBase).
TransferHelper.safeApprove(
fromBase,
address(swapRouter),
amountIn
);
IV3SwapRouter.ExactInputParams memory params = IV3SwapRouter
.ExactInputParams({
path: encodedPath,
recipient: address(this),
amountIn: amountIn,
amountOutMinimum: amountOutMin
});
// Execute the swap
amountOut = swapRouter.exactInput(params);
// Step 5: Upgrade and send tokens back
TransferHelper.safeApprove(address(toBase), address(_to), amountOut);
_to.upgrade(amountOut);
TransferHelper.safeApprove(address(_to), msg.sender, amountOut);
TransferHelper.safeTransferFrom(
address(_to),
address(this),
msg.sender,
amountOut
);
emit SuperSwapComplete(amountOut);
}
}