BBCTool's blog BBCTool's blog
首页
  • Uniswap专题

    • V2交易DAPP开发-概述
  • 常见模式合约分析

    • 《多模式智能合约源码分析-持币推广多级分红燃烧通缩营销钱包分红》
  • 批量空投批量转账
  • 批量刷单刷K线
  • 科学家抢池子
关于
GitHub (opens new window)

BBCTool

Web3应用探索
首页
  • Uniswap专题

    • V2交易DAPP开发-概述
  • 常见模式合约分析

    • 《多模式智能合约源码分析-持币推广多级分红燃烧通缩营销钱包分红》
  • 批量空投批量转账
  • 批量刷单刷K线
  • 科学家抢池子
关于
GitHub (opens new window)
  • Uniswap专题

    • UniswapV2交易DAPP开发-概述
    • UniswapV2交易DAPP开发-环境搭建及数据准备
    • UniswapV2交易DAPP开发-基本实现
      • 程序结构
      • 实体类
      • 功能实现
        • 创建池子
        • 添加流动性
        • 移除流动性
        • 获取池子深度信息
        • Token代币买入
        • Token代币卖出
  • 各种模式合约

  • 博客
  • Uniswap专题
bbctool
2023-07-04
目录
程序结构
实体类
功能实现
创建池子
添加流动性
移除流动性
获取池子深度信息
Token代币买入
Token代币卖出

UniswapV2交易DAPP开发-基本实现

上两篇文章对Uniswap、开发环境及需要的基础数据进行了详细的说明,本篇将对用到的实体类、Nethereum相关的类、及对Uniswap具体的调用进行详细的讲解。具体包括通过Nethereum和区块链节点建立连接、根据任意币对创建流动池子、给池子添加流动性、撤出流动性、获得LP地址、获得池子深度计算Token代币价格以及进行Token兑换交易等。

# 程序结构

项目结构由Model、Resources、Utils等目录组成,Model主要保存节点、合约、账户、Swap交易信息及Web3操作等实体类、扩展类。路由、工厂、LP等合约的ABI信息已文件的形式保存在Resources目录下。Utils目录下,ABIFunction.cs为ABI操作函数定义的类型及File的扩展操作。

alt
工程目录结构

# 实体类

RPCModel
链、节点等相关信息保存在RPCModel类中,主要包括节点URL、网络ID以及链的GasPrice和操作的GasLimit。
ContractModel
ContractModel合约实体类主要包括合约地址、合约的ABI以及添加池子、撤池子、交易兑换的数量字段(AmountLiquidity)。
AccountModel
AccountModel账户类,主要用到了地址、私钥等属性。
SwapModel
SwapModel为代币交易兑换时用到的类,主要包括滑点设置和Token兑换路由数组。
Web3Ext为对Nethereum的扩展和封装,主要包括流动池的创建、添加流动池、撤出流动性、获取LP地址、获取流动池子的深度信息、对路由合约授权操作以及Swap交易兑换等。

# 功能实现

程序启动就创建了节点、路由合约、Factory合约、LP合约、账户等实例,并加载了合约对应的ABI和地址,账户对应的地址和私钥。

alt
功能流程图

# 创建池子

选择任意的Erc20币对,通过Factory工厂合约提供的“createPair"接口进行流动池子的创建。

public static ResultModel createPair(RPCModel rPCModel, AccountModel accountModel, ContractModel factoryContract, ContractModel contractA, ContractModel contractB)
        {
            ResultModel resultModel=new ResultModel();
            try
            {
                Account account = new Account(accountModel.PrivateKey, rPCModel.NetworkId);
                Web3 web3 = new Web3(account, rPCModel.URL);
                Contract myContract = web3.Eth.GetContract(factoryContract.ABI, factoryContract.Address);
                BigInteger gPrice = Web3.Convert.ToWeiFromUnit(rPCModel.GasPrice, 1000000000);
                BigInteger gasLimit = new BigInteger(rPCModel.GasLimit);
                TransactionInput transactionInput = new TransactionInput
                {
                    Data = "",
                    Gas = new HexBigInteger(gasLimit),
                    From = account.Address,
                    Value = new HexBigInteger(0),
                    GasPrice = new HexBigInteger(gPrice)
                };
                var swapResult = myContract.GetFunction("createPair").SendTransactionAsync(transactionInput, contractA.Address, contractB.Address);
                swapResult.Wait();
                int count = 0;
                resultModel.TransHash = swapResult.Result;
                while (true)
                {
                    var receipt = web3.Eth.Transactions.GetTransactionReceipt.SendRequestAsync(swapResult.Result);
                    receipt.Wait();
                    if (receipt.Result != null)
                    {
                        resultModel.State = (int)receipt.Result.Status.ToUlong();
                        break;
                    }
                    if (count >= 20)
                    {
                        resultModel.State = 2;
                        break;
                    }
                    Thread.Sleep(2000);
                    count++;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                if (ex.InnerException != null)
                {
                    Console.WriteLine(ex.InnerException.ToString());
                }
                resultModel.Message = ex.Message;
            }
            return resultModel;
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 添加流动性

添加流动是调用Uniswap的路由合约中的addLiquidity进行操作。其中需要注意的参数:amountADesired、amountBDesired用户期望添加的数量,amountAMin、amountBMin为了避免价格波动带来损失,最小允许添加的数量。

public static ResultModel addLiquidity(RPCModel rPCModel, AccountModel accountModel, ContractModel routerContract, ContractModel contractA, ContractModel contractB)
        {
            ResultModel resultModel = new ResultModel();
            try
            {
                Account account = new Account(accountModel.PrivateKey, rPCModel.NetworkId);
                Web3 web3 = new Web3(account, rPCModel.URL);
                Contract myContract = web3.Eth.GetContract(routerContract.ABI, routerContract.Address);
                BigInteger gPrice = Web3.Convert.ToWeiFromUnit(rPCModel.GasPrice, 1000000000);
                BigInteger gasLimit = new BigInteger(rPCModel.GasLimit);
                BigInteger amountADesired = Web3.Convert.ToWei(contractA.AmountLiquidity, contractA.UnitDecimal);
                BigInteger amountBDesired = Web3.Convert.ToWei(contractB.AmountLiquidity, contractB.UnitDecimal);
                TransactionInput transactionInput = new TransactionInput
                {
                    Data = "",
                    Gas = new HexBigInteger(gasLimit),
                    From = account.Address,
                    Value = new HexBigInteger(0),
                    GasPrice = new HexBigInteger(gPrice)
                };
                var swapResult = myContract.GetFunction("addLiquidity").SendTransactionAsync(transactionInput, contractA.Address, contractB.Address, amountADesired, amountBDesired, 0, 0, account.Address,GetSeconds(DateTime.Now));
                swapResult.Wait();
                int count = 0;
                resultModel.TransHash = swapResult.Result;
                while (true)
                {
                    var receipt = web3.Eth.Transactions.GetTransactionReceipt.SendRequestAsync(swapResult.Result);
                    receipt.Wait();
                    if (receipt.Result != null)
                    {
                        resultModel.State = (int)receipt.Result.Status.ToUlong();
                        break;
                    }
                    if (count >= 20)
                    {
                        resultModel.State = 2;
                        break;
                    }
                    Thread.Sleep(2000);
                    count++;
                }
            }
            catch (Exception ex)
            {
                resultModel.State = -1;
                resultModel.Message = ex.Message;
                if (ex.InnerException != null)
                {
                    resultModel.Message=ex.InnerException.Message;
                }
            }
            return resultModel;
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

# 移除流动性

移除流动是调用Uniswap的路由合约中的removeLiquidity进行操作。其中liquidity为LP的数量,amountAMin、amountBMin为了避免价格波动带来损失,最小允许获得的代币数量。

public static ResultModel removeLiquidity(RPCModel rPCModel, AccountModel accountEntity, ContractModel routerContract, ContractModel contractLP, ContractModel contractA, ContractModel contractB)
        {
            ResultModel resultModel = new ResultModel();
            try
            {
                Account account = new Account(accountEntity.PrivateKey, rPCModel.NetworkId);
                Web3 web3 = new Web3(account, rPCModel.URL);
                Contract myContract = web3.Eth.GetContract(routerContract.ABI, routerContract.Address);
                BigInteger gPrice = Web3.Convert.ToWeiFromUnit(rPCModel.GasPrice, 1000000000);
                BigInteger gasLimit = new BigInteger(rPCModel.GasLimit);
                BigInteger liquidity = Web3.Convert.ToWei(contractLP.AmountLiquidity, contractLP.UnitDecimal);
                TransactionInput transactionInput = new TransactionInput
                {
                    Data = "",
                    Gas = new HexBigInteger(gasLimit),
                    From = account.Address,
                    Value = new HexBigInteger(0),
                    GasPrice = new HexBigInteger(gPrice)
                };
                var swapResult = myContract.GetFunction("removeLiquidity").SendTransactionAsync(transactionInput, contractA.Address, contractB.Address, liquidity, 0, 0, account.Address,GetSeconds(DateTime.Now));
                swapResult.Wait();
                int count = 0;
                resultModel.TransHash = swapResult.Result;
                resultModel.Message = "LP撤出成功!";
                while (true)
                {
                    var receipt = web3.Eth.Transactions.GetTransactionReceipt.SendRequestAsync(swapResult.Result);
                    receipt.Wait();
                    if (receipt.Result != null)
                    {
                        resultModel.State = (int)receipt.Result.Status.ToUlong();
                        break;
                    }
                    if (count >= 20)
                    {
                        resultModel.State = 2;
                        break;
                    }
                    Thread.Sleep(2000);
                    count++;
                }
            }
            catch (Exception ex)
            {
                resultModel.State = -1;
                resultModel.Message=ex.Message;
                if (ex.InnerException != null)
                {
                    resultModel.Message=ex.Message+ex.InnerException.ToString();
                }
            }
            return resultModel;
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

# 获取池子深度信息

通过获得的深度信息可以计算代币的价格,getReserves是LP合约提供的方法,该方法无参数。

public static ResultModel getLPReserves(RPCModel rPCModel, ContractModel lPContract, ContractModel contractA, ContractModel contractB)
        {
            ResultModel resultModel = new ResultModel();
            try
            {
                Web3 web3 = new Web3(rPCModel.URL);
                Contract myContract = web3.Eth.GetContract(lPContract.ABI, lPContract.Address);
                var reservesResult = myContract.GetFunction("getReserves").CallDecodingToDefaultAsync();// .CallAsync<BigInteger>();
                reservesResult.Wait();
                if ((BigInteger)reservesResult.Result[0].Result > 0)
                {
                    decimal reserves0;// = Web3.Convert.FromWei((BigInteger)reservesResult.Result[0].Result);
                    decimal reserves1;// = Web3.Convert.FromWei((BigInteger)reservesResult.Result[1].Result);
                    reserves0 = Web3.Convert.FromWei((BigInteger)reservesResult.Result[0].Result, contractA.UnitDecimal);
                    reserves1 = Web3.Convert.FromWei((BigInteger)reservesResult.Result[1].Result, contractB.UnitDecimal);
                    resultModel.TokenAReserves = reserves0;
                    resultModel.TokenBReserves = reserves1;
                    resultModel.State = 1;
                }
            }
            catch (Exception ex)
            {
                resultModel.State = -1;
                resultModel.Message = ex.Message;
            }
            return resultModel;
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# Token代币买入

这里调用了swapExactTokensForTokens方法进行代币的买入兑换,在发送代币买入指令之前可以先通过getAmountsOut函数计算能得到的代币数量,然后减去允许的滑点得到amountOutMin参数。

public static ResultModel swapExactTokensForTokens(RPCModel rPCModel, AccountModel accountEntity, ContractModel routerContract, ContractModel contractA, ContractModel contractB, SwapModel swapModel)
        {
            ResultModel resultModel = new ResultModel();
            try
            {
                Account account = new Account(accountEntity.PrivateKey, rPCModel.NetworkId);
                Web3 web3 = new Web3(account, rPCModel.URL);
                Contract myContract = web3.Eth.GetContract(routerContract.ABI, routerContract.Address);
                BigInteger amountIn = 0;
                amountIn = Web3.Convert.ToWei(contractB.AmountLiquidity, contractB.UnitDecimal);
                BigInteger gPrice = Web3.Convert.ToWeiFromUnit(rPCModel.GasPrice, 1000000000);
                var amountResult = myContract.GetFunction("getAmountsOut").CallDecodingToDefaultAsync(amountIn, swapModel.Path);
                amountResult.Wait();
                System.Collections.Generic.List<BigInteger> list = (System.Collections.Generic.List<BigInteger>)amountResult.Result[0].Result;
                decimal decOut = 0;
                BigInteger amountOutMin = 0;//
                decOut = (decimal)Web3.Convert.FromWei((BigInteger)list[1], contractA.UnitDecimal) * (decimal)(1 - swapModel.Slippage / 100);
                amountOutMin = Web3.Convert.ToWei(decOut, contractA.UnitDecimal);
                BigInteger gasLimit=new BigInteger(rPCModel.GasLimit);
                TransactionInput transactionInput = new TransactionInput
                {
                    Data = "",
                    Gas = new HexBigInteger(gasLimit),
                    From = account.Address,
                    Value = new HexBigInteger(0),
                    GasPrice = new HexBigInteger(gPrice)
                };
                var swapResult = myContract.GetFunction("swapExactTokensForTokens").SendTransactionAsync(transactionInput, amountIn, amountOutMin, swapModel.Path, account.Address, GetSeconds(DateTime.Now));
                swapResult.Wait();
                int count = 0;
                resultModel.TransHash = swapResult.Result;
                while (true)
                {
                    var receipt = web3.Eth.Transactions.GetTransactionReceipt.SendRequestAsync(swapResult.Result);
                    receipt.Wait();
                    if (receipt.Result != null)
                    {
                        resultModel.State = (int)receipt.Result.Status.ToUlong();
                        if (resultModel.State == 1)
                        {
                            resultModel.Message = "Swap交易成功!";
                        }
                        else
                        {
                            resultModel.Message = "Swap交易失败!";
                        }
                        break;
                    }
                    if (count >= 20)
                    {
                        resultModel.State = 2;
                        resultModel.Message = "获取交易hash超时!";
                        break;
                    }
                    Thread.Sleep(2000);
                    count++;
                }
            }
            catch (Exception ex)
            {
                resultModel.State =-1;
                resultModel.Message= ex.Message;
                if (ex.InnerException != null)
                {
                    resultModel.Message += ex.InnerException.Message;
                }
            }
            return resultModel;
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

# Token代币卖出

代币卖出调用了swapExactTokensForTokensSupportingFeeOnTransferTokens函数,该函数支持有滑点、销毁、通缩、通胀等模式的代币Token。

public static ResultModel swapExactTokensForTokensSupportingFeeOnTransferTokens(RPCModel rPCModel, AccountModel accountModel, ContractModel routerContract, ContractModel contractA, ContractModel contractB, SwapModel swapModel)
        {
            ResultModel resultModel = new ResultModel();
            try
            {
                Account account = new Account(accountModel.PrivateKey, rPCModel.NetworkId);
                Web3 web3 = new Web3(account, rPCModel.URL);
                Contract myContract = web3.Eth.GetContract(routerContract.ABI, routerContract.Address);
                BigInteger amountIn =  Web3.Convert.ToWei(contractA.AmountLiquidity, contractA.UnitDecimal);
                BigInteger gPrice = Web3.Convert.ToWeiFromUnit(rPCModel.GasPrice, 1000000000);
                var amountResult = myContract.GetFunction("getAmountsOut").CallDecodingToDefaultAsync(amountIn, swapModel.Path);
                amountResult.Wait();
                System.Collections.Generic.List<BigInteger> list = (System.Collections.Generic.List<BigInteger>)amountResult.Result[0].Result;
                decimal decOut = 0;
                BigInteger amountOutMin = 0;
                decOut = (decimal)Web3.Convert.FromWei((BigInteger)list[1], contractB.UnitDecimal) * (decimal)(1 - swapModel.Slippage / 100);
                amountOutMin = Web3.Convert.ToWei(decOut, contractB.UnitDecimal);
                BigInteger gasLimit = new BigInteger(rPCModel.GasLimit);
                TransactionInput transactionInput = new TransactionInput
                {
                    Data = "",
                    Gas = new HexBigInteger(gasLimit),
                    From = account.Address,
                    Value = new HexBigInteger(0),
                    GasPrice = new HexBigInteger(gPrice)
                };
                var swapResult = myContract.GetFunction("swapExactTokensForTokensSupportingFeeOnTransferTokens").SendTransactionAsync(transactionInput, amountIn, amountOutMin, swapModel.Path, account.Address, GetSeconds(DateTime.Now));
                swapResult.Wait();
                int count = 0;
                resultModel.TransHash = swapResult.Result;
                while (true)
                {
                    try
                    {
                        var receipt = web3.Eth.Transactions.GetTransactionReceipt.SendRequestAsync(swapResult.Result);
                        receipt.Wait();
                        if (receipt.Result != null)
                        {
                            resultModel.State = (int)receipt.Result.Status.ToUlong();
                            if (resultModel.State == 1)
                            {
                                resultModel.Message = "Swap交易成功!";
                            }
                            else
                            {
                                resultModel.Message = "Swap交易失败!";
                            }
                            break;
                        }
                    }
                    catch (Exception ex)
                    {
                    }
                    if (count >= 20)
                    {
                        resultModel.State = 2;
                        resultModel.Message = "获取交易hash超时!";
                        break;
                    }
                    Thread.Sleep(2000);
                    count++;
                }
                return resultModel;
            }
            catch (Exception ex)
            {
                resultModel.State = -1;
                resultModel.Message= "交易失败:" + ex.Message;
                if (ex.InnerException != null)
                {
                    resultModel.Message += ex.InnerException.Message;
                }
                return resultModel;
            }
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
编辑 (opens new window)
上次更新: 2023/07/25, 02:22:08
UniswapV2交易DAPP开发-环境搭建及数据准备
持币推广多级分红燃烧通缩营销钱包分红等多个模式智能合约源码分析

← UniswapV2交易DAPP开发-环境搭建及数据准备 持币推广多级分红燃烧通缩营销钱包分红等多个模式智能合约源码分析→

最近更新
01
软件整体说明及Windows版下载运行 操作手册
07-17
02
科学家抢池子
07-17
03
MacOS版安装及更新说明
07-17
更多文章>
Copyright © 2023-2025 By BBCTool
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式