Skip to content

Commit

Permalink
Merge pull request #1046 from yingjingyang/main
Browse files Browse the repository at this point in the history
add LI.FI
  • Loading branch information
yanyanho authored Mar 20, 2024
2 parents 877a64d + 45b345e commit e5eb40c
Show file tree
Hide file tree
Showing 19 changed files with 565 additions and 79 deletions.
2 changes: 2 additions & 0 deletions basic/80-crossChainTransfer/LI.FI/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
PRIVATE_KEY=yyyy
INFURA_ID=zzzz
16 changes: 16 additions & 0 deletions basic/80-crossChainTransfer/LI.FI/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
node_modules
.env

# Hardhat files
/cache
/artifacts

# TypeChain files
/typechain
/typechain-types

# solidity-coverage files
/coverage
/coverage.json

deployment.json
43 changes: 43 additions & 0 deletions basic/80-crossChainTransfer/LI.FI/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# LI.FI 介绍
LI.FI 是一种跨链桥接聚合协议,通过聚合 Bridge, DEX, Solver 来支持 Token 在任意链之间转移,交易。
应用场景举例如下:
1)Optim 上的 USDT 转移到 Polygon 上
2)Optim 上的 USDT 转移到 Polygon 上,同时将 USDT swap 为 USDC
3)Optim 上的 USDT 转移到 Polygon 上,同时将 10% USDT 变为 Polygon 上原生的 gas ( Matic ),以便后续的操作。这个特性对 EVM 链和非 EVM 链来说非常便利,但目前只支持 4 个链
4)Optim 上的 USDT 转移到 Polygon 上的同时,调用 Polygon 链上的一个合约进行相应处理

LI.FI 的操作很简单,用户只需在 origin chain 上发起一笔交易即可完成上述这些场景。
同时为方便其他 Dapp 集成,LI.FI 提供了对应的 [SDK](https://docs.li.fi/integrate-li.fi-js-sdk/install-li.fi-sdk) 和 相应的前端 [UI components](https://docs.li.fi/integrate-li.fi-widget/li.fi-widget-overview)

# 跨链测试
下面我们将使用 ARB 和 OP 这两个 Layer2 演示 USDT 跨链
- 软件要求
```
node 版本需要为 v20.11.0
```

- 安装依赖
```
npm install
```

- 配置 env 文件
```shell
cp .env.example .env
## 之后在 .env 文件中配置对应的私钥和infura
```

- 发起跨链请求
```
npx hardhat run scripts/1-crossChainTokenTransfer.js --network optim
```

- 检查跨链结果
```
npx hardhat run scripts/2-checkTokenTransferStatus.js --network optim
```


## 参考文档
- 官方文档: https://docs.li.fi/
- 合约仓库: https://github.com/lifinance/contracts/blob/main/docs/README.md
79 changes: 79 additions & 0 deletions basic/80-crossChainTransfer/LI.FI/contracts/IERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);

/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);

/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);

/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);

/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);

/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);

/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);

/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
77 changes: 77 additions & 0 deletions basic/80-crossChainTransfer/LI.FI/hardhat.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require("@nomicfoundation/hardhat-toolbox");
require('dotenv').config();

/** @type import('hardhat/config').HardhatUserConfig */
// module.exports = {
// solidity: "0.8.24",
// };


const settings = {
optimizer: {
enabled: true,
runs: 200,
},
};

function mnemonic() {
return [process.env.PRIVATE_KEY];
}

/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: {
compilers: [
{ version: '0.8.4', settings },
{ version: '0.4.21', settings },
{ version: '0.8.24', settings },
],
},
networks: {
localhost: {
url: 'http://localhost:8545',
//gasPrice: 125000000000, // you can adjust gasPrice locally to see how much it will cost on production
/*
notice no mnemonic here? it will just use account 0 of the hardhat node to deploy
(you can put in a mnemonic here to set the deployer locally)
*/
},
mainnet: {
url: 'https://mainnet.infura.io/v3/' + process.env.INFURA_ID, //<---- YOUR INFURA ID! (or it won't work)
accounts: mnemonic(),
},
matic: {
url: 'https://polygon-mainnet.infura.io/v3/' + process.env.INFURA_ID,
accounts: mnemonic()
},
optim: {
url: "https://optimism-mainnet.infura.io/v3/" + process.env.INFURA_ID,
accounts: mnemonic()
},
sepolia: {
url: "https://sepolia.infura.io/v3/" + process.env.INFURA_ID,
accounts: mnemonic()
},
arbitrum: {
url: "https://arbitrum-mainnet.infura.io/v3/" + process.env.INFURA_ID,
accounts: mnemonic()
},
scroll: {
url: "https://rpc.scroll.io",
accounts: mnemonic()
},
optim_sepolia: {
url: "https://optimism-sepolia.infura.io/v3/" + process.env.INFURA_ID,
accounts: mnemonic()
},

},
mocha: {
timeout: 200000
},
sourcify: {
enabled: true
},
};
9 changes: 9 additions & 0 deletions basic/80-crossChainTransfer/LI.FI/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "hardhat-project",
"devDependencies": {
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
"dotenv": "^16.4.5",
"hardhat": "^2.21.0",
"node-fetch": "^2.7.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// You can also run a script with `npx hardhat run <script>`. If you do that, Hardhat
// will compile your contracts, add the Hardhat Runtime Environment's members to the
// global scope, and execute the script.
const {ethers} = require("hardhat");
require('dotenv').config();

const {
saveRedpacketDeployment,
} = require("../utils");

async function getQuote (fromChain, toChain, fromToken, toToken, fromAmount, slippage, fromAddress, toAddress) {
const result = await request(
"https://li.quest/v1/quote",
{
fromChain,
toChain,
fromToken,
toToken,
fromAmount,
slippage,
fromAddress,
toAddress,
}
)

return result;
}


async function main() {
const [deployer] = await ethers.getSigners();
const fromChain = 'OPT';
const fromToken = 'USDT';
const toChain = 'ARB';
const toToken = 'USDT';
const fromAmount = '1000000';
const slippage = 0.05;
const fromAddress = deployer.address;
const toAddress = deployer.address;
const signer = await ethers.provider.getSigner()
const OPUSDTaddress = "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58"
let quote = await getQuote(fromChain, toChain, fromToken, toToken, fromAmount, slippage,fromAddress,toAddress);
console.log(quote.transactionRequest)

// approve USDT
const usdtContracct = await ethers.getContractAt('IERC20', OPUSDTaddress, deployer);
let approveTx = await usdtContracct.approve(quote.transactionRequest.to, fromAmount);
await approveTx.wait();
console.log(`Approve USDT to ${quote.transactionRequest.to} successfully`)

// send cross chain transfer request
let tx = await signer.sendTransaction(quote.transactionRequest)
await tx.wait();
console.log("send request successfully")

// save tx has
saveRedpacketDeployment({
crossChainTxHash: tx.hash,
});
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// You can also run a script with `npx hardhat run <script>`. If you do that, Hardhat
// will compile your contracts, add the Hardhat Runtime Environment's members to the
// global scope, and execute the script.
const {ethers} = require("hardhat");
require('dotenv').config();

const {
readRedpacketDeployment,
getStatus,
} = require("../utils");

async function getQuote (fromChain, toChain, fromToken, toToken, fromAmount, slippage, fromAddress, toAddress) {
const result = await request(
"https://li.quest/v1/quote",
{
fromChain,
toChain,
fromToken,
toToken,
fromAmount,
slippage,
fromAddress,
toAddress,
}
)

return result;
}


async function main() {
const [deployer] = await ethers.getSigners();
const fromChain = 'OPT';
const fromToken = 'USDT';
const toChain = 'ARB';
const toToken = 'USDT';
const fromAmount = '1000000';
const slippage = 0.03;
const fromAddress = deployer.address;
const toAddress = deployer.address;
const signer = await ethers.provider.getSigner()
const OPUSDTaddress = "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58"
let quote = await getQuote(fromChain, toChain, fromToken, toToken, fromAmount, slippage,fromAddress,toAddress);
// console.log(quote.transactionRequest)

const deployment = readRedpacketDeployment();
let txHash = deployment.crossChainTxHash
// Waiting for the transfer to complete
let result;
do {
result = await getStatus(quote.tool, fromChain, toChain, txHash);
console.log(`Transfer status: ${result.status}`)
} while (result.status !== 'DONE' && result.status !== 'FAILED')
console.log(result)
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Loading

0 comments on commit e5eb40c

Please sign in to comment.