make a swap using pancake smart router in solidity contract

overview

In this article i gonna show you how to make a swap using pancakeswap smart router in solidity contract.Here wo go. In this example i swap BUSD token to CAKE.Here is the address of each token:

soldity

Let’s go dive into the deep.Firstly we need set a swap amount, eg:0.01 BUSD, an wed need a min out amount as well.The last parameter is path which means the path of swap tokens.When we swap busd to cake we can find a pool consist of busd and cake。So the path is simple.For some other tokens maybe the path is a litte bit longer.We have to use two or three or more pools to get the token we want. In the pancakeswap front-end page , it returns a path before we make the swap. In the solidity code we have to do it ourself.We have to get the optimal path before we make the swap.Let’s dive into the pancakeswap smart router code.

1
2
3
4
5
interface PancakeRouter {
function swapExactTokensForTokens(uint256 amountIn,uint256 amountOutMin,address[] calldata path,address to,uint256 deadline) external;
function swapExactTokensForTokensSupportingFeeOnTransferTokens(uint256 amountIn,uint256 amountOutMin,address[] calldata path,address to,uint256 deadline) external;
function factory() external pure returns (address);
}

There are so many swap function we can use to help use swap tokens. In this demo we use swapExactTokensForTokens. As you see there is a function named swapExactTokensForTokensSupportingFeeOnTransferTokens. We need to know what’s the difference between this two functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external virtual override ensure(deadline) {
// transfer token from sender address to pool[0]
TransferHelper.safeTransferFrom(
path[0], msg.sender, PancakeLibrary.pairFor(factory, path[0], path[1]), amountIn
);

// the balance of des token that receiver hold.
uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);

// make the sawp.
_swapSupportingFeeOnTransferTokens(path, to);

// make sure the out token is bigger than the set amountOutMin value.
require(
IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
'PancakeRouter: INSUFFICIENT_OUTPUT_AMOUNT'
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external virtual override ensure(deadline) returns (uint[] memory amounts) {
// get the out amount using PancakeLibrary via amountIn.
amounts = PancakeLibrary.getAmountsOut(factory, amountIn, path);

// after calculate all result make sure the out amount is bigger than amountOutMin.
require(amounts[amounts.length - 1] >= amountOutMin, 'PancakeRouter: INSUFFICIENT_OUTPUT_AMOUNT');

//transfer code.
TransferHelper.safeTransferFrom(
path[0], msg.sender, PancakeLibrary.pairFor(factory, path[0], path[1]), amounts[0]
);
_swap(amounts, path, to);
}

After compare the above two functions we know the difference. In the swapExactTokensForTokens function the exact transfer amount is set by PancakeLibrary getAmountsOut. And in this article we use swapExactTokensForTokensSupportingFeeOnTransferTokens.
Here is the entire code from github gist : code

deploy our code

Deploy code:

1
2
3
4
5
6
7
8
9
10
11
12
13
const hre = require("hardhat");

async function main() {
const Swap = await hre.ethers.getContractFactory("PancakeSwap");
const swap = await Swap.deploy();
await swap.deployed();
console.log(swap.address);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
1
npx hardhat run script/scriptName.js --network YOUR_NETWORK_NAME.

test swap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const hre = require("hardhat");

async function main() {
const Bundle = await hre.ethers.getContractFactory("PancakeSwap");
const bundle = Bundle.attach(YOUR_DEPLOYED_CONTRACT_ADDRESS);
const amountIn = hre.ethers.utils.parseEther("0.01");
const amountOutMin = 0;
const tokenSwapPath = [TOKEN1_ADDRESS, TOKEN2_ADDRESS];
let ret = await bundle.SimpleSwap(amountIn, amountOutMin, tokenSwapPath);
console.log(ret);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

balancer-v2-vault

概览

本文主要介绍了在 balancer v2 上实现借贷并在一个交易中归还所借到的 token。

环境

  • node
  • hardhat(solidity 0.8.0)

Balancer v2 介绍

Balancer 是一个多链部署(Ethereum、Polygon、Arbitrum),基于 AMM(自动做市商)模型的 Dex。
Balancer 于 2020 年正式上线 V1 版本,并于 2021 年 5 月升级为 Balancer V2,目前市面上主要使用的都是基于 V2 的产品。
Balancer Vault 是一个安全的、非监管的数字资产保管库,可以用来将数字资产存储到 Balancer 池中,进行高效分散。Balancer Vault 是一种特殊的智能合约,可以在没有任何门槛的情况下,保护您的数字资产不受恶意攻击。此外,它还可以提供具有开放式架构和不断改进的即时更新能力的强大安全性功能,可以有效防止欺诈和非法交易。

Show me the code

solidity

1
2
3
4
5
6
7
8
9
...
function makeFlashLoan(
IERC20[] memory tokens,
uint256[] memory amounts,
bytes memory userData
) external {
vault.flashLoan(this, tokens, amounts, userData);
emit eventLog("making flash...");
}

完整代码:
https://gist.github.com/coffiasd/440c42e5a0211d096080ad5b1b50b4ae

部署代码

1
2
3
4
5
6
7
8
9
10
11
12
const hre = require("hardhat");

async function main() {
const Swap = await hre.ethers.getContractFactory("Swap");
const swap = await Swap.deploy();
await swap.deployed();
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const hre = require("hardhat");

async function main() {
const Bundle = await hre.ethers.getContractFactory("Swap");
const bundle = Bundle.attach("替换成部署完的合约地址");
console.log(bundle.address);
const tokenIn = "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6"; //weth
let ret = await bundle.makeFlashLoan(
[tokenIn],
[hre.ethers.utils.parseEther("1")],
[]
);
console.log(ret);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

hardhat.config.js 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
require("dotenv").config();
require("@nomiclabs/hardhat-waffle");
require("@nomiclabs/hardhat-etherscan");

module.exports = {
solidity: "0.8.0",
networks: {
goerli: {
url: [process.env.GOERLI_ENDPOINT],
accounts: [process.env.PRIVATE_KEY],
}
}
};

运行代码

运行部署代码

1
npx hardhat run .\scripts\swap.js --network goerli

运行测试代码

1
npx hardhat run .\scripts\test.js --network goerli

输出

alt ""

How to quick start web3.storage

register an account

  • Email
  • Github (recommand)

install the client

1
2
3
4
javascript
npm install web3.storage
golang
go get github.com/web3-storage/go-w3s-client

create a client instance

1
2
3
4
5
6
7
8
//get the access token
function getAccessToken(){
return process.env.TOKEN
}
//creata an instance
function makeStorageClient(){
return new Web3storage({token:getAccessToken()})
}

preparing files for uploading

1
2
3
4
function getFiles(){
const fileInput = document.querySelector('input[type="file"]')
return fileInput.files
}

upload files to web3.storage

1
2
3
4
5
6
//async upload process
async function storageFiles(files){
const client = makeStorageClient()
const cid = await client.put(files)
return cid
}

directory wrapping

after uploading you’ll get a cid of the directory and then the entire link gonna be ipfs:///
to make a gateway link :

1
2
https://<cid>.ipfs.<gateway-host>/<filename>
https://<gateway-host>/ipfs/<cid>/<filename>

storing ipfs content archives?

if you already have some files you can use putCar client function.I don’t want to jump into this here.

how to access data we uploaded above ?

  • using an IPFS http gateway
  • using client library
  • ipfs command line
  • system command line

how to query web3.storage

1
2
3
4
async function checkStatus(cid){
const client = xxxx
const status = await client.status(cid)
}

return data structure

  • cid
  • created
  • dagSize
  • pins
  • deals

how to list files uploaded to web3.storage

1
2
3
4
const client = xxxx
for await (const upload of client.list(){
console.log(upload.name,upload.cid,upload.dagSize)
})

listing the content of an IPFS directory