cover

Uniswap对接Python开发包

汇智网 / hubwiz.com

为Python应用快速增加Uniswap协议对接能力。

1、开发包概述

Uniswap.py 开发包适用于为Python应用快速增加对Uniswap协议的支持能力。 即支持使用自有部署以太坊区块链节点的应用场景,也支持使用第三方节点的 轻量级部署场景。

Uniswap.py开发包主要包含以下特性:

  • 一键部署Uniswap协议,便于快速开发与测试
  • 支持Uniswap协议的全部接口,并提供开发人员友好的API
  • 支持ERC20/ERC20、ETH/ERC20等各种交易对的流动性添加、移除与兑换交易
  • 支持自动做市价格计算与滑点计算
  • 支持以太坊交易gas用量与gas价格的自动估算与手动设定
  • 支持EIP712签名授权,单一交易内完成流动性维护

Uniswap.py开发包运行在 Python 3.6+ 环境下,当前版本1.0.0,主要类/接口及关系如下图所示:

Uniswap.py uml

Uniswap.py开发包的主要代码文件清单如下:

代码文件说明
uniswap/swapkit.pyUniswap.py开发包入口类
uniswap/order_builder.py流动性维护订单构造器
uniswap/order.py流动性维护订单类
uniswap/trade_builder.py兑换交易构造器
uniswap/trade.py兑换交易类
uniswap/permit_signer.pyEIP712签名实现类
uniswap/deployer.py智能合约部署器
uniswap/protocol_meta.pyUniswap协议元信息
demo/config.py演示程序参数配置文件
demo/deploy_contracts.pyUniswap及测试Token合约部署工具,可用于快速开发与测试
demo/token_approve.py代币授权演示
demo/add_liquidity.pyERC20/ERC20交易对流动性添加演示
demo/add_liquidity_eth.pyERC20/ETH交易对流动性添加演示
demo/remove_liquidity.pyERC20/ERC20交易对流动性移除演示
demo/remove_liquidity_eth.pyERC20/ETH交易对流动性移除演示
demo/swap_exact_tokens_for_tokens.pyERC20/ERC20兑换演示,以输入token数量为基准
demo/swap_tokens_for_exact_tokens.pyERC20/ERC20兑换演示,以输出token数量为基准
demo/swap_exact_eth_for_tokens.pyETH/ERC20兑换演示,以输入eth数量为基准
demo/swap_eth_for_exact_tokens.pyETH/ERC20兑换演示,以输出token数量为基准
demo/swap_exact_tokens_for_eth.pyERC20/ETH兑换演示,以输入token数量为基准
demo/swap_tokens_for_exact_eth.pyERC20/ETH兑换演示,以输出eth数量为基准
contract/HappyToken.sol标准ERC20 token实现,开发测试用
contract/TetherToken.solTether/USDT实现,开发测试用
contract/UniswapV2Factory.solUniswap V2工厂合约实现
contract/UniswapV2Router02.solUniswap V2路由合约实现
contract/WETH9.solWETH9合约实现
bin/build-contract合约编译脚本
setup.py包安装脚本

2、使用示例代码

2.1 编译合约

在终端进入项目目录,执行如下命令编译Uniswap及开发包提供的测试Token合约:

~$ cd ~/uniswap.py
~/uniswap.py$ bin/build-contracts

执行结果如下:

uniswap.py build contracts

2.2 部署合约

首先在另一个终端启动开发私链:

~$ ganache-cli -d

然后进入项目demo目录,执行如下命令部署合约:

~$ cd ~/uniswap.py/demo
~/uniswap.py/demo$ python deploy_contracts.py

执行结果如下:

uniswap.py deploy contracts

注意 :如果需要在以太坊测试链,例如Ropsten、Rinkeby等,或者在主链 部署上述合约,或者使用其他参数启动ganache-cli,需要修改demo/config.py 中的账号配置。

2.3 Token授权

代码token_approve.py演示了如何授权Uniswap路由合约代理操作 当前账号的HAPY token和USDT token。

在终端进入项目demo目录,执行如下命令:

~$ cd ~/uniswap.py/demo
~/uniswap.py/demo$ python token_approve.py

执行结果如下:

uniswap.py token approve

2.4 ERC20/ERC20交易对添加流动性

代码add_liquidity.py演示了如何为ERC20/ERC20交易对添加流动性。

在终端进入项目demo目录,执行如下命令:

~$ cd ~/uniswap.py/demo
~/uniswap.py/demo$ python add_liquidity.py

执行结果如下:

uniswap.py add liquidity

Uniswap协议约定,第一次添加流动性将自动创建交易对 ,之后再次添加 流动性时,演示代码将首先显示当前的仓位信息。例如再次执行如下命令:

~/uniswap.py/demo$ python add_liquidity.py

执行结果如下:

uniswap.py add liquidity

2.5 ERC20/ERC20交易对移除流动性

代码remove_liquidity.py演示了如何移除指定的ERC20/ERC20 交易对的流动性。

在终端进入项目demo目录,执行如下命令:

~$ cd ~/uniswap.py/demo
~/uniswap.py/demo$ python remove_liquidity.py

执行结果如下:

uniswap.py remove liquidity

2.6 ERC20/ERC20兑换:以输入token数量为基准

代码swap_exact_tokens_for_tokens.py演示了如何实现以输入 token数量为基准的ERC20/ERC20兑换。

在终端进入项目demo目录,执行如下命令:

~$ cd ~/uniswap.py/demo
~/uniswap.py/demo$ python swap_exact_tokens_for_tokens.py

执行结果如下:

uniswap.py swap exact tokens for tokens

2.7 ERC20/ERC20兑换:以输出token数量为基准

代码swap_tokens_for_exact_tokens.py演示了如何实现以输出 token数量为基准的ERC20/ERC20兑换。

在终端进入项目demo目录,执行如下命令:

~$ cd ~/uniswap.py/demo
~/uniswap.py/demo$ python swap_tokens_for_exact_tokens.py

执行结果如下:

uniswap.py swap tokens for exact tokens

2.8 ETH/ERC20交易对添加流动性

代码add_liquidty_eth.py演示了如何为ETH/ERC20或ERC20/ETH交易对添加流动性。

在终端进入项目demo目录,执行如下命令:

~$ cd ~/uniswap.py/demo
~/uniswap.py/demo$ python add_liquidity_eth.py

执行结果如下:

uniswap.py add liquidity

第一次添加流动性将自动创建交易对,之后再次添加将首先显示 当前的持仓信息。例如再次执行如下命令:

~/uniswap.py/demo$ python add_liquidity_eth.py

执行结果如下:

uniswap.py add liquidity

2.9 ETH/ERC20交易对移除流动性

代码remove_liquidity_eth.py演示了如何移除ETH/ERC20或ERC20/ETH 交易对的流动性。

在终端进入项目demo目录,执行如下命令:

~$ cd ~/uniswap.py/demo
~/uniswap.py/demo$ python remove_liquidity_eth.py

执行结果如下:

uniswap.py remove liquidity

2.10 ETH/ERC20兑换:以输入ETH数量为基准

代码swap_exact_eth_for_tokens.py演示和如何将指定数量的ETH 兑换为ERC20 token。

在终端进入项目demo目录,执行如下命令:

~$ cd ~/uniswap.py/demo
~/uniswap.py/demo$ python swap_exact_eth_for_tokens.py

执行结果如下:

uniswap.py swap exact eth for tokens

2.11 ETH/ERC20兑换:以输出token数量为基准

代码swap_eth_for_exact_tokens.py演示了如何将ETH兑换为指定 数量的ERC20 token。

在终端进入项目demo目录,执行如下命令:

~$ cd ~/uniswap.py/demo
~/uniswap.py/demo$ python swap_eth_for_exact_tokens.py

执行结果如下:

uniswap.py swap eth for exact tokens

2.12 ERC20/ETH兑换:以输入token数量为基准

代码swap_exact_tokens_for_eth.py演示了如何将指定数量的 ERC20 token兑换为ETH。

在终端进入项目demo目录,执行如下命令:

~$ cd ~/uniswap.py/demo
~/uniswap.py/demo$ python swap_exact_tokens_for_eth.py

执行结果如下:

uniswap.py swap exact tokens for eth

2.13 ERC20/ETH兑换:以输出ETH数量为基准

代码swap_tokens_for_exact_eth.py演示了如何将ERC20 token兑换为指定数量的ETH。

在终端进入项目demo目录,执行如下命令:

~$ cd ~/uniswap.py/demo
~/uniswap.py/demo$ python swap_tokens_for_exact_eth.py

执行结果如下:

uniswap.py swap tokens for exact eth

3、使用uniswap.py开发包

SwapKit 是开发包的入口,使用这个类可以快速实现如下功能:

  • 流动性添加与移除:支持ERC20/ERC20、ERC20/ETH、ETH/ERC20等各种交易对
  • 兑换交易的创建与执行:支持ERC20/ERC20、ERC20/ETH、ETH/ERC20等各种交易对,支持以输入或输出价格为准
  • 用户仓位查询:查询指定账号在指定交易对的持仓情况,例如持仓数量、占比等。

3.1 实例化SwapKit

SwapKit实例化需要传入三个参数:

  • 以太坊节点URL
  • Uniswap路由合约地址
  • 用于执行合约交互的以太坊账号的私钥。

例如,下面的代码创建一个接入以太坊主网Uniswap协议的SwapKit实例:

from uniswap import SwapKit

kit = SwapKit(
  'http://localhost:8545',                          # 以太坊节点URL
  '0x7a250d5630B4...b4c659F2488D',                  # Uniswap路由合约地址
  '0x4f3e...3b1d'                                   # 默认执行账号,指定私钥
);

3.2 使用ProtocolMeta(可选)

为了避免混淆各网络的Uniswap路由地址,uniswap.py 提供了 ProtocolMeta类的静态方法getPresetAddress()来获取Uniswap 官方在以太坊主网和测试网部署的Uniswap协议合约地址。例如 获取主网的路由合约:

from uniswap import ProtocolMeta

routerAddr = ProtocolMeta.getPresetAddress(
  SwapKit.MAINNET,                                   # 以太坊主网  
  SwapKit.ROUTER                                     # Uniswap路由合约
);
print('router address: %s' % routerAddr)             # 0x7a250d5630B4...

ProtocolMeta 目前支持的以太坊网络及标识如下:


网络标识(SwapKit. 合约标识(SwapKit.) 地址 说明
MAINNET FACTORY 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f 以太坊主网工厂合约
ROUTER 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D 以太坊主网路由合约
WETH 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 以太坊主网WETH9合约
ROPSTEN FACTORY 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f Ropsten测试网工厂合约
ROUTER 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D Ropsten测试网路由合约
WETH 0xc778417E063141139Fce010982780140Aa0cD5Ab Ropsten测试网WETH9合约
RINKEBY FACTORY 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f Rinkeby测试网工厂合约
ROUTER 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D Rinkeby测试网路由合约
WETH 0xc778417E063141139Fce010982780140Aa0cD5Ab Rinkeby测试网WETH9合约
GORLI FACTORY 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f Gorli测试网工厂合约
ROUTER 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D Gorli测试网路由合约
WETH 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6 Gorli测试网WETH9合约
KOVAN FACTORY 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f Kovan测试网工厂合约
ROUTER 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D Kovan测试网路由合约
WETH 0xd0A1E359811322d97991E03f863a0C30C2cF029C Kovan测试网WETH9合约

3.3 生成流动性添加/移除委托单

由于Uniswap协议中涉及到交易滑点处理以及价格预计算,因此 uniswap.py提供了一个专门的类 OrderBuilder 用于生成 流动性添加/移除委托单。OrderBuilder的实现内置了自动价格计算与 滑点计算,因此调用者只需要指定基本的数据即可生成可提交给 Uniswap合约的流动性添加/移除委托单。

使用SwapKit对象的orderBuilder睡醒获取预创建 的OrderBuilder对象,并调用OrderBuilder的build() 方法生成委托单。例如:

order = (
  kit.orderBuilder()                # 返回OrderBuilder对象 
     ....                           # 参数设置
     .build()                       # 生成并返回委托单
)

OrderBuilder对象提供了以下方法用于调整生成器的行为:

  • orderType(type) :设置委托单类型,目前支持两种:
    • Order.LIQUIDITY_ADD - 流动性添加委托单
    • Order.LIQUIDITY_REMOVE - 流动性移除委托单
  • tokenA(addr) / tokenB(addr) :交易对的两个Token地址 用SwapKit.ETH_TOKEN表示以太币的地址
  • amountA(bn) / amountB(bn) :两个token的添加数量,仅在添加流动时需要设置
  • liquidity(bn) :LP token数量,仅在移除流动性时需要设置
  • slippage(bn) :交易滑点设置,默认值为5, 表示0.5%的滑点容忍区间
  • to(addr) :委托单执行结果接收地址

例如,下面的代码生成一个流动性添加委托单:

from uniswap import SwapKit, Order

order = (
  kit.orderBuilder                              # 获取委托单生成器对象
     .orderType(Order.LIQUIDITY_ADD)            # 添加流动性
     .tokenA('0x...')                           # 交易对TokenA地址
     .tokenB('0x...')                           # 交易对TokenB地址
     .amountA(100000000000000000000)            # 以TokenA数量为基准按市价自动计算TokenB数量
     .slippage(10)                              # 滑点容忍范围1%
     .to(kit.credential.address)                # 设置LP Token的接收地址
     .build()                                   # 生成委托单 
)

print('amountA => %d' % order.amountA)          # TokenA设置数量  
print('amountB => %d' % order.amountB)          # TokenB计算数量 
print('amountAMin => %d' % order.amountAMin)    # 滑点处理后的TokenA最小可接受数量 
print('amountBMin => %d' % order.amountAMin)    # 滑点处理后的TokenB最小可接受数量

注意:

  • 使用特殊的 SwapKit.ETH_TOKEN 表示交易对中的ETH,值为:0x00000000000000000000000000000000000eEEEE
  • token数量均按最小单位指定

类似的,下面的代码生成一个流动性移除委托单:

order = (
  kit.orderBuilder                              # 获取委托单生成器对象
     .orderType(Order.LIQUIDITY_REMOVE)         # 移除流动性  
     .tokenA('0x...')                           # 交易对TokenA地址 
     .tokenB('0x...')                           # 交易对TokenB地址 
     .liquidity(10000000000000000000)           # 要移除的流动性数量
     .slippage(10)                              # 滑点容忍范围1%
     .to(kit.credential.address)                # token接收地址
     .build()                                   # 生成委托单 
)

print('amountAMin => %d' % order.amountAMin)   # 滑点处理后最少应收到的TokenA数量 
print('amountBMin => %d' % order.amountAMin)   # 滑点处理后最少应收到的TokenB数量

Order对象提供的以下值,有助于在执行委托单之前向用户提供更好的 反馈信息:

  • amountA/amountB :市价计算的tokenA或tokenB理想值
  • amountAMin/amountAMax :滑点处理后的tokenA最小值/最大值
  • amountBMin/amountBMax :滑点处理后的tokenB最小值/最大值

3.4 执行流动性委托单

使用SwapKit的 executeOrder() 方法即可执行生成的流动性委托单,最终 完成流动性添加/移除交易。例如:

from uniswap import to_hex

txid = kit.executeOrder(trade)                  # 执行委托单
print('txid => %s' % to_hex(txid))              # 显示交易ID

默认情况下,executeOrder()方法自动估算交易所需的gas限额与gas价格, 但可以传入额外的参数手动设置这两个值。

例如,下面的代码以指定的gas参数执行流动性维护单:

from uniswap import to_hex

opts = {
  'gas': 4000000,                                # 4 million
  'gasPrice': 200000000000                       # 200 gwei
}
txid = kit.executeOrder(order, opts)             # 执行委托单
print('txid => %s' % to_hex(txid))               # 显示交易ID

3.5 查询仓位信息

使用SwapKit的 getLiquidityInfo() 方法可以查询指定账号在指定交易对 的仓位信息。例如查询某地址的持仓情况:

info = kit.getLiquidityInfo(                      # 查询持仓信息
  '0x90F8...c9C1',                                # 要查询的地址
  '0x9956...F4B8',                                # 交易对tokenA地址
  '0x94dF...2Df7'                                 # 交易对tokenB地址
)       

print('total => %d' % info['totalSupply'])        # LP Token总量 
print('balance => %d' % info['balance'])          # 持仓数量
print('share => %.4f' % info['share'])            # 持仓比例

3.6 生成兑换交易对象

类似于流动性的添加与移除,在Uniswap中的交易对兑换也存在价格自动计算与滑点 处理问题。为此,uniswap.py开发包提供了一个专门的类 TradeBuilder 用来简化这一操作。TradeBuilder内置了价格自动计算与滑点处理,因此 调用者只需要设置基本的参数即可。

使用SwapKit对象的tradeBuilder属性获取预创建的TradeBuilder 对象,例如:

trade = (
  kit.tradeBuilder                      # 获取预创建的兑换交易生成器
     ...                                # 参数设置
     .build()                           # 生成兑换交易对象
)

TradeBuilder提供了以下方法用于调整生成器的行为:

  • tradeType(type) :兑换类型,可选值为:
    • Trade.EXACT_INPUT - 以tokenIn的数量为基准
    • Trade.EXACT_OUTPUT - 以tokenOut的数量为基准
  • tokenIn(addr) - 输入Token的地址
  • tokenOut(addr) - 输出Token的地址
  • amountIn(bn) - 输入数量,仅在兑换类型为EXACT_INPUT时需要设置
  • amountOut(bn) - 输出数量,仅在兑换类型为EXACT_OUTPUT时需要设置
  • slippage(bn) - 交易滑点设置,默认值5, 表示允许0.5%的滑点
  • to(addr) - 输出token接收地址

例如,下面的代码以输入token数量为基准生成一个兑换交易对象:

from uniswap import Trade

trade = (
  kit.tradeBuilder                            # 获取兑换交易生成器
     .tradeType(Trade.EXACT_INPUT)            # 以输入token数量为基准
     .tokenIn('0x...')                        # 输入token地址
     .tokenOut('0x...')                       # 输出token地址
     .amountIn(10000000000000000000)          # 输入token的数量
     .slippage(10)                            # 滑点容忍范围1%
     .to(kit.credential.address)              # 输出token的接收地址
     .build()                                 # 生成兑换交易对象
)

print('amountIn => %d' % trade.amountIn)          # 输入token的设置数量
print('amountOut => %d' % trade.amountOut)        # 自动做市算法得到的输出token的数量
print('amountOutMin => %d' % trade.amountOutMin)  # 滑点处理后的应收到的输出token最少数量

3.7 执行兑换交易

使用SwapKit对象的 executeTrade() 方法执行指定的兑换交易对象。 例如:

from uniswap import to_hex

txid = kit.executeTrade(trade)                       # 执行兑换交易
print('txid => %s' % to_hex(txid))                   # 显示交易ID

默认情况下,executeTrade()方法自动估算交易所需的gas限额 与gas价格,但可以传入额外的参数手动设置这两个值。

例如,下面的代码使用设定的gas参数执行兑换交易:

opts = {
  'gas': 4000000,                                     # 4 million
  'gasPrice': 200000000000                            # 200 gwei
}
txid = kit.executeTrade(trade, opts)                  # 执行交易
¥5000.00
查看授权信息
  • 付费成功自动开通下载
  • 三个月内代码免费升级
  • 专业人员在线技术支持
  • 支持按需定制(另付费)
下载代码包
版本发布日期地址
1.0.0 2021-4-4
下载最新版
QQ咨询
2860991437
9+