Skip to main content

Uniswap

链接

  1. https://docs.uniswap.org/concepts/overview
  2. https://help.tokenpocket.pro/cn/wallet-operation/how-to-trade-on-dex/dex-uniswap
  3. https://y1cunhui.github.io/uniswapV3-book-zh-cn/docs/introduction/introduction-to-markets/

做市

做市(Market Making)是金融市场中的一个术语,指的是为金融资产提供流动性的行为,即同时提供买入(Bid)和卖出(Ask)报价,使得其他市场参与者可以随时买入或卖出该资产。做市商(Market Maker)是指那些专门从事这种活动的个人或机构。

做市商的主要职责和特点包括:

  1. 提供流动性:做市商通过在交易平台上持续提供买卖报价,帮助确保市场的流动性,使得其他交易者可以更容易地进入或退出市场。

  2. 收取买卖差价:做市商通常会设置一个买价(较低)和卖价(较高),两者之间的差距被称为买卖差价(Bid-Ask Spread)。这个差价是做市商盈利的主要来源。

  3. 降低价格波动:通过提供稳定的买卖报价,做市商有助于减少市场价格的剧烈波动。

  4. 增加市场深度:做市商提供的报价增加了市场的深度,即在不同价格水平上可交易的资产数量。

  5. 风险管理:做市商需要有效地管理库存风险和价格波动风险,以确保自身的盈利性和市场的稳定性。

做市活动在各种金融市场中都非常重要,包括股票市场、债券市场、外汇市场、商品市场和加密货币市场。在传统金融市场中,做市商通常是大型金融机构,如银行或专业的交易公司。而在加密货币市场中,除了传统的做市商外,还有一些基于算法的自动化做市(Automated Market Making, AMM)协议,如 Uniswap 和 Balancer,它们通过智能合约和预设的规则来自动提供流动性。

无常损失,理解为 不正常的损失?

"无常损失"(Impermanent Loss)这个术语可能会让人误解,它听起来像是指一种临时或者不正常的损失,但实际上它指的是一种特定的损失,这种损失在提供流动性给自动做市商(AMM)的流动性池时可能会发生。

在 AMM 中,流动性提供者将两种代币按照一定比例存入流动性池。如果这两种代币中的一种相对于另一种在外部市场上的价格发生变化,那么存入池中的代币比例也会随之调整以保持价格的平衡。这种价格调整可能会导致流动性提供者的资产组合的价值相对于单独持有这些代币的价值减少,这种现象就是所谓的“无常损失”。

这种损失被称为“无常”的原因是,如果代币价格返回流动性提供者最初存入资产时的相对价格,那么这种损失是可以被恢复的。换句话说,只要价格波动是暂时的,并且最终回到最初的状态,那么无常损失就可以消失。然而,如果价格波动是永久性的,那么无常损失也会变成永久性损失。

因此,“无常损失”并不意味着不正常的损失,而是一种特定条件下可能发生的损失,这种损失在某些情况下是可逆的,但在其他情况下可能是永久性的。

uniswap 使用

https://github.com/eth-infinitism/account-abstraction/blob/2b1aac46a8b532121dab4af739bcb019f8693ae1/contracts/samples/TokenPaymaster.sol#L12C17-L12C30 https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/samples/utils/UniswapHelper.sol

js example

要使用 JavaScript 与 Uniswap v3 进行代币兑换,你将需要使用 Ethers.js 或 Web3.js 这样的库来与以太坊区块链交互,以及 Uniswap v3 SDK 来帮助构建交易。以下是一个基于 Ethers.js 和 Uniswap v3 SDK 的基本示例代码,用于执行代币兑换。

首先,确保你已经安装了必要的 npm 包:

npm install @uniswap/sdk-core @uniswap/v3-sdk ethers

然后,你可以使用以下代码作为参考,构建并提交一个 Uniswap v3 交易:

const { ethers } = require('ethers');
const { Token, CurrencyAmount, TradeType, Percent } = require('@uniswap/sdk-core');
const { Pool, Trade } = require('@uniswap/v3-sdk');

// 配置你的提供者(这里使用的是 Infura)
const provider = new ethers.providers.JsonRpcProvider('https://mainnet.infura.io/v3/your_infura_project_id');

// 钱包私钥(在服务器端保持私密!不要暴露在客户端或公共代码库中)
const privateKey = 'your_private_key';
const wallet = new ethers.Wallet(privateKey, provider);

// Uniswap v3 Pool 地址(这个例子中使用的是 ETH/DAI 池)
const poolAddress = 'uniswap_v3_pool_address';

// 代币信息(以 ETH/DAI 交易对为例)
const tokenA = new Token(1, 'tokenA_address', 18, 'ETH', 'Ethereum');
const tokenB = new Token(1, 'tokenB_address', 18, 'DAI', 'Dai Stablecoin');

// 假设我们已经有了 Pool 的状态信息(如 tokenA 的储备量、tokenB 的储备量、费率等)
const pool = new Pool(tokenA, tokenB, feeTier, sqrtPriceX96, liquidity, tick);

// 构建交易
const amountIn = CurrencyAmount.fromRawAmount(tokenA, ethers.utils.parseUnits('1', 'ether')); // 假设我们想要交换 1 ETH
const trade = new Trade({
pool,
inputAmount: amountIn,
outputAmount: undefined, // 未定义,因为我们正在计算
tradeType: TradeType.EXACT_INPUT,
});

// 确定滑点容忍度
const slippageTolerance = new Percent('50', '10000'); // 0.5%

// 计算最小接受金额
const amountOutMin = trade.minimumAmountOut(slippageTolerance).toSignificant(6);

// 获取 Uniswap v3 Router 合约实例
const routerContractAddress = '0xE592427A0AEce92De3Edee1F18E0157C05861564'; // Uniswap v3 Router 合约地址
const routerContract = new ethers.Contract(
routerContractAddress,
[
// 这里需要添加 Router 合约的 ABI
],
wallet
);

// 设置交易参数
const params = {
tokenIn: tokenA.address,
tokenOut: tokenB.address,
fee: pool.fee,
recipient: wallet.address,
deadline: Math.floor(Date.now() / 1000) + 60 * 20, // 20分钟后过期
amountIn: amountIn.quotient.toString(),
amountOutMinimum: ethers.utils.parseUnits(amountOutMin, 'ether').toString(),
sqrtPriceLimitX96: 0,
};

// 发起交易
async function swap() {
const tx = await routerContract.exactInputSingle(params, {
gasLimit: '210000', // 设置合适的 gas limit
gasPrice: ethers.utils.parseUnits('30', 'gwei'), // 设置合适的 gas price
});
console.log(`Transaction hash: ${tx.hash}`);

const receipt = await tx.wait();
console.log(`Transaction was mined in block ${receipt.blockNumber}`);
}

swap().catch(console.error);

请注意,这个代码仅仅是一个示例,实际使用时你需要根据你的具体需求进行调整:

  • 使用正确的代币地址和池地址。
  • 确保你有正确的 ABI。
  • 设置合适的滑点容忍度。
  • 根据网络状况设置合适的 Gas 价格和限制。
  • 确保你的私钥安全,不要在客户端代码中使用私钥。
  • 使用正确的 RPC 提供者和 Infura 项目 ID。

在实际部署之前,务必在测试网络上进行充分测试,并确保理解代码的每个部分是如何工作的。

solidity example

在 Solidity 中编写与 Uniswap v3 交互的代码需要对智能合约编程有一定了解,并且需要知道如何与 Uniswap v3 的 Router 合约进行交互。以下是一个简化的例子,展示了如何在 Solidity 合约中使用 Uniswap v3 进行代币交换。

首先,你需要安装 Uniswap v3 的 npm 包来获取接口定义:

npm install @uniswap/v3-periphery

然后,你可以编写一个 Solidity 合约,像这样:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// 导入 Uniswap v3 接口
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';

contract UniswapV3SwapExample {
// Uniswap v3 SwapRouter 地址
address private constant UNISWAP_V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;

// 以太坊和 DAI 的地址(这些地址在主网上是固定的)
address private constant WETH9 = 0xc778417E063141139Fce010982780140Aa0cD5Ab;
address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;

// Uniswap v3 SwapRouter 接口
ISwapRouter public immutable swapRouter;

// 构造函数
constructor() {
swapRouter = ISwapRouter(UNISWAP_V3_ROUTER);
}

// 兑换 ETH 为 DAI 的函数
function swapEthForDai(uint256 amountOutMinimum, uint24 poolFee) external payable {
require(msg.value > 0, "Must pass non-zero ETH amount");

// 创建单个交换的参数
ISwapRouter.ExactInputSingleParams memory params =
ISwapRouter.ExactInputSingleParams({
tokenIn: WETH9,
tokenOut: DAI,
fee: poolFee,
recipient: msg.sender,
deadline: block.timestamp,
amountIn: msg.value,
amountOutMinimum: amountOutMinimum,
sqrtPriceLimitX96: 0
});

// 执行交换
swapRouter.exactInputSingle{value: msg.value}(params);
}

// 允许合约接收 ETH
receive() external payable {}
}

请注意以下几点:

  1. 此示例中的 swapEthForDai 函数允许用户发送 ETH 并接收 DAI。用户必须指定他们愿意接受的最小 DAI 数量 (amountOutMinimum) 和用于交换的池子费率 (poolFee)。

  2. ISwapRouter 接口是从 Uniswap v3 的 npm 包导入的,这样你就可以在合约中使用它。

  3. swapRouter.exactInputSingle 函数是 Uniswap v3 的一个核心功能,用于执行单个代币对的精确输入交换。

  4. WETH9DAI 地址是在以太坊主网上的包装以太币 (WETH) 和 DAI 稳定币的地址。如果你在其他网络上部署,这些地址将不同。

  5. receive 函数允许合约接收 ETH,这是执行 ETH 到 ERC20 代币交换所必需的。

  6. 请确保在实际部署之前在测试网络上进行测试,并且在合约中处理好错误情况和安全性问题(比如重入攻击)。

  7. 你需要确保合约有足够的 Gas 来执行交易,并且用户发送的 ETH 足以支付 Gas 费用和代币交换。

  8. 请记住,这个例子是基于 Uniswap v3 的,而 Uniswap v3 的路由合约可能会随时间更新和变化,因此请始终查阅最新的 Uniswap v3 文档和代码库。