Skip to content

Commit

Permalink
uniswapper to perform token to weth swaps through uniswaps
Browse files Browse the repository at this point in the history
  • Loading branch information
ShivaanshK committed Sep 11, 2024
1 parent 70d2ad2 commit 4e2a0e5
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 9 deletions.
2 changes: 1 addition & 1 deletion contracts/interfaces/IBiconomyTokenPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ interface IBiconomyTokenPaymaster {

function setPriceExpiryDuration(uint256 _newPriceExpiryDuration) external payable;

function setNativeOracle(IOracle _oracle) external payable;
function setNativeAssetToUsdOracle(IOracle _oracle) external payable;

function updateTokenDirectory(address _tokenAddress, IOracle _oracle) external payable;
}
24 changes: 18 additions & 6 deletions contracts/token/BiconomyTokenPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { TokenPaymasterParserLib } from "../libraries/TokenPaymasterParserLib.so
import { SignatureCheckerLib } from "@solady/src/utils/SignatureCheckerLib.sol";
import { ECDSA as ECDSA_solady } from "@solady/src/utils/ECDSA.sol";
import "@account-abstraction/contracts/core/Helpers.sol";
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";

/**
* @title BiconomyTokenPaymaster
Expand Down Expand Up @@ -62,8 +64,8 @@ contract BiconomyTokenPaymaster is
IEntryPoint _entryPoint,
uint256 _unaccountedGas,
uint256 _priceMarkup,
IOracle _nativeAssetToUsdOracle,
uint256 _priceExpiryDuration,
IOracle _nativeAssetToUsdOracle,
address[] memory _tokens, // Array of token addresses
IOracle[] memory _oracles // Array of corresponding oracle addresses
)
Expand Down Expand Up @@ -266,18 +268,18 @@ contract BiconomyTokenPaymaster is
* @param _oracle The new native asset oracle
* @notice only to be called by the owner of the contract.
*/
function setNativeOracle(IOracle _oracle) external payable override onlyOwner {
function setNativeAssetToUsdOracle(IOracle _oracle) external payable override onlyOwner {
if (_oracle.decimals() != 8) {
// Native -> USD will always have 8 decimals
revert InvalidOracleDecimals();
}

IOracle oldNativeOracle = nativeAssetToUsdOracle;
IOracle oldNativeAssetToUsdOracle = nativeAssetToUsdOracle;
assembly ("memory-safe") {
sstore(nativeAssetToUsdOracle.slot, _oracle)
}

emit UpdatedNativeAssetOracle(oldNativeOracle, _oracle);
emit UpdatedNativeAssetOracle(oldNativeAssetToUsdOracle, _oracle);
}

/**
Expand All @@ -298,6 +300,17 @@ contract BiconomyTokenPaymaster is
emit UpdatedTokenDirectory(_tokenAddress, _oracle, decimals);
}

/**
* @dev Swap a token in the paymaster for ETH to increase its entry point deposit
* @param _swapRouter The address of the swap router to use to facilitate the swap
* @param _tokenAddress The token address of the token to swap for ETH
* @param _tokenAmount The amount of the token to swap
* @notice only to be called by the owner of the contract.
*/
function swapTokenAndDeposit(ISwapRouter _swapRouter, address _tokenAddress, uint256 _tokenAmount) external payable onlyOwner {

}

/**
* return the hash we're going to sign off-chain (and validate on-chain)
* this method is called by the off-chain service, to sign the request.
Expand Down Expand Up @@ -405,8 +418,7 @@ contract BiconomyTokenPaymaster is
// Transfer full amount to this address. Unused amount will be refunded in postOP
SafeTransferLib.safeTransferFrom(tokenAddress, userOp.sender, address(this), tokenAmount);

context =
abi.encode(userOp.sender, tokenAddress, tokenAmount, tokenPrice, externalPriceMarkup, userOpHash);
context = abi.encode(userOp.sender, tokenAddress, tokenAmount, tokenPrice, externalPriceMarkup, userOpHash);
validationData = _packValidationData(false, validUntil, validAfter);
} else if (mode == PaymasterMode.INDEPENDENT) {
// Use only oracles for the token specified in modeSpecificData
Expand Down
87 changes: 87 additions & 0 deletions contracts/token/swaps/Uniswapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;

/* solhint-disable not-rely-on-time */

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "@uniswap/v3-periphery/contracts/interfaces/IPeripheryPayments.sol";

abstract contract Uniswapper {
uint256 private constant SWAP_PRICE_DENOMINATOR = 1e26;

/// @notice The Uniswap V3 SwapRouter contract
ISwapRouter public immutable uniswapRouter;

/// @notice The ERC-20 token that wraps the native asset for current chain
address public immutable wrappedNative;

// Token address -> Fee tier of the pool to swap through
mapping(address => uint24) public tokenToPools;

event UniswapReverted(address indexed tokenIn, address indexed tokenOut, uint256 amountIn);

error TokensAndAmountsLengthMismatch();

constructor(
ISwapRouter _uniswapRouter,
address _wrappedNative,
address[] memory _tokens,
uint24[] memory _tokenPools
) {
if (_tokens.length != _tokenPools.length) {
revert TokensAndAmountsLengthMismatch();
}

// Set router and native wrapped asset addresses
uniswapRouter = _uniswapRouter;
wrappedNative = _wrappedNative;

for (uint256 i = 0; i < _tokens.length; ++i) {
IERC20(_tokens[i]).approve(address(_uniswapRouter), type(uint256).max); // one time max approval
tokenToPools[_tokens[i]] = _tokenPools[i]; // set mapping of token to uniswap pool to use for swap
}
}

function _setTokenPool(address _token, uint24 _feeTier) internal {
tokenToPools[_token] = _feeTier; // set mapping of token to uniswap pool to use for swap
}

function _swapTokenToWeth(address _tokenIn, uint256 _amountIn) internal returns (uint256 amountOut) {
uint24 poolFee = tokenToPools[_tokenIn];

ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
tokenIn: _tokenIn,
tokenOut: wrappedNative,
fee: poolFee,
recipient: address(this),
deadline: block.timestamp,
amountIn: _amountIn,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
});
try uniswapRouter.exactInputSingle(params) returns (uint256 _amountOut) {
amountOut = _amountOut;
} catch {
emit UniswapReverted(_tokenIn, wrappedNative, _amountIn);
amountOut = 0;
}
}

function addSlippage(uint256 amount, uint8 slippage) private pure returns (uint256) {
return amount * (1000 - slippage) / 1000;
}

function tokenToWei(uint256 amount, uint256 price) public pure returns (uint256) {
return amount * price / SWAP_PRICE_DENOMINATOR;
}

function weiToToken(uint256 amount, uint256 price) public pure returns (uint256) {
return amount * SWAP_PRICE_DENOMINATOR / price;
}

function unwrapWeth(uint256 _amount) internal {
IPeripheryPayments(address(uniswapRouter)).unwrapWETH9(_amount, address(this));
}
}
4 changes: 2 additions & 2 deletions test/unit/concrete/TestTokenPaymaster.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,12 @@ contract TestTokenPaymaster is TestBase {
);
}

function test_SetNativeOracle() external prankModifier(PAYMASTER_OWNER.addr) {
function test_SetNativeAssetToUsdOracle() external prankModifier(PAYMASTER_OWNER.addr) {
MockOracle newOracle = new MockOracle(100_000_000, 8);

vm.expectEmit(true, true, false, true, address(tokenPaymaster));
emit IBiconomyTokenPaymaster.UpdatedNativeAssetOracle(nativeAssetToUsdOracle, newOracle);
tokenPaymaster.setNativeOracle(newOracle);
tokenPaymaster.setNativeAssetToUsdOracle(newOracle);

assertEq(address(tokenPaymaster.nativeAssetToUsdOracle()), address(newOracle));
}
Expand Down

0 comments on commit 4e2a0e5

Please sign in to comment.