Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bugfix: lnv3 withdrawliquidity #43

Merged
merged 4 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"dependencies": {
"@composedb/client": "0.5.0",
"@composedb/types": "0.5.0",
"@helixbridge/helixconf": "1.1.15",
"@helixbridge/helixconf": "1.1.17",
"@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.2.0",
"@nestjs/core": "^9.0.0",
Expand Down
4 changes: 4 additions & 0 deletions src/base/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,10 @@ export class Lnv3BridgeContract extends EthereumContract {
]);
}

encodeLockInfo(transferId: string): string {
return this.interface.encodeFunctionData("lockInfos", [transferId]);
}

@rpcCallIfError
async transferIdExist(transferId: string): Promise<[boolean, any]> {
const lockInfo = await this.contract.lockInfos(transferId);
Expand Down
7 changes: 4 additions & 3 deletions src/base/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export class EthereumProvider {
notSupport1559: boolean = false
): Promise<GasPrice> {
const fee = await this.provider.getFeeData();
const stretchScale = BigInt(Math.floor(scale * 100));
if (!notSupport1559 && fee.maxFeePerGas && fee.maxPriorityFeePerGas) {
const maxFeePerGas =
fee.maxFeePerGas > fee.maxFeePerGas
Expand All @@ -116,8 +117,8 @@ export class EthereumProvider {
const feeInfo: EIP1559Fee = {
// maxFeePerGas is not accurate
//maxFeePerGas: fee.maxFeePerGas,
maxFeePerGas: maxFeePerGas * BigInt(scale),
maxPriorityFeePerGas: fee.maxPriorityFeePerGas * BigInt(scale),
maxFeePerGas: maxFeePerGas * stretchScale / BigInt(100),
maxPriorityFeePerGas: fee.maxPriorityFeePerGas * stretchScale / BigInt(100),
};
return {
eip1559fee: feeInfo,
Expand All @@ -128,7 +129,7 @@ export class EthereumProvider {
return {
isEip1559: false,
fee: {
gasPrice: fee.gasPrice * BigInt(scale),
gasPrice: fee.gasPrice * stretchScale / BigInt(100),
},
eip1559fee: null,
};
Expand Down
3 changes: 1 addition & 2 deletions src/dataworker/dataworker.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ export class DataworkerService implements OnModuleInit {
return feeUsed;
}

// MUST: countThreshold <= 40 ELSE invalid threshold
async queryLiquidity(
url: string,
fromChain: string,
Expand All @@ -174,7 +173,7 @@ export class DataworkerService implements OnModuleInit {
if (!amountThreshold && !countThreshold) return null;
let query = `{
historyRecords(
row: 40,
row: 200,
relayer: \"${relayer.toLowerCase()}\",
recvTokenAddress: \"${token.toLowerCase()}\",
fromChains: [\"${fromChain}\"],
Expand Down
168 changes: 107 additions & 61 deletions src/relayer/relayer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
Lnv3BridgeContract,
SafeContract,
zeroAddress,
MulticallContract,
MulticallArgs,
} from "../base/contract";
import { Any, EtherBigNumber, Ether, GWei } from "../base/bignumber";
import {
Expand All @@ -22,7 +24,7 @@ import { ConfigureService } from "../configure/configure.service";
import { Encrypto } from "../base/encrypto";
import { last } from "lodash";

import { ethers } from "ethers";
import { ethers, AbiCoder } from "ethers";
import { SafeWallet } from "../base/safewallet";
import { messagerInstance } from "../base/messager";
import { Aave } from "../liquidity/lend/aave";
Expand All @@ -33,6 +35,11 @@ import { SingleService } from "../base/safe-service/single.service";
import { SafeGlobalService } from "../base/safe-service/safeglobal.service";
import { SafeService } from "../base/safe-service/safe.service";

const kMaxWithdrawTransferCount = 16;
export const kMaxWithdrawTransferAmount: bigint = BigInt(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);

export class ChainInfo {
chainName: string;
rpcs: string[];
Expand All @@ -43,6 +50,7 @@ export class ChainInfo {
lnv3Address: string;
adjustingFee: boolean;
lendMarket: LendMarket[];
multicall: MulticallContract;
}

export class BridgeConnectInfo {
Expand Down Expand Up @@ -222,6 +230,7 @@ export class RelayerService implements OnModuleInit {
txHashCache: "",
checkTimes: 0,
lendMarket: lendMarket ?? [],
multicall: new MulticallContract(chainInfo.additional.multicallAddress, provider),
},
];
})
Expand Down Expand Up @@ -859,74 +868,111 @@ export class RelayerService implements OnModuleInit {
srcDecimals
);
if (needWithdrawRecords != null) {
// token transfer direction fromChain -> toChain
// withdrawLiquidity message direction toChain -> fromChain
const fromChannelAddress = this.configureService.getMessagerAddress(
fromChainInfo.chainName,
needWithdrawRecords.channel
);
const toChannelAddress = this.configureService.getMessagerAddress(
toChainInfo.chainName,
needWithdrawRecords.channel
);
const messager = messagerInstance(
needWithdrawRecords.channel,
toChannelAddress,
bridge.toWallet
);
const lnv3Contract = toBridgeContract as Lnv3BridgeContract;
const appPayload = lnv3Contract.encodeWithdrawLiquidity(
needWithdrawRecords.transferIds,
toChainInfo.chainId,
lnProvider.relayer
);
const payload = messager.encodePayload(
toChainInfo.chainId,
toChainInfo.lnv3Address,
fromChainInfo.lnv3Address,
appPayload
);
const params = await messager.params(
toChainInfo.chainId,
fromChainInfo.chainId,
fromChannelAddress,
payload,
lnProvider.relayer
);
const err = await lnv3Contract.tryWithdrawLiquidity(
fromChainInfo.chainId,
needWithdrawRecords.transferIds,
lnProvider.relayer,
params.extParams,
params.fee
);
if (err != null) {
this.logger.warn(
`try to withdraw liquidity failed, err ${err}, from ${fromChainInfo.chainId}, to ${toChainInfo.chainId}`
const fromLnv3Contract = fromBridgeContract as Lnv3BridgeContract;
const toLnv3Contract = toBridgeContract as Lnv3BridgeContract;
// filter unwithdrawn transferIds on chain
let args: MulticallArgs[] = [];
for (const transferId of needWithdrawRecords.transferIds) {
args.push({
address: fromLnv3Contract.address,
data: fromLnv3Contract.encodeLockInfo(transferId),
});
}
const response = await fromChainInfo.multicall.aggregate(args);
const lockInfos = response?.[1] ?? [];
let filterTransferIds = [];
let index = 0;
let totalAmount = BigInt(0);
for (const lockInfo of lockInfos) {
const [amountWithFeeAndPenalty, tokenIndex, txStatus] = AbiCoder.defaultAbiCoder().decode(
["uint", "uint", "uint"],
lockInfo
);
} else {
this.logger.log(
`withdrawLiquidity ${fromChainInfo.chainId}->${
toChainInfo.chainId
}, info: ${JSON.stringify(needWithdrawRecords)}, fee: ${
params.fee
}`
if (Number(txStatus) === 1) {
if (filterTransferIds.length < kMaxWithdrawTransferCount) {
filterTransferIds.push(needWithdrawRecords.transferIds[index]);
}
totalAmount += amountWithFeeAndPenalty;
}
index++;
}
let countThreshold = lnProvider.withdrawLiquidityCountThreshold;
if (!countThreshold || countThreshold > kMaxWithdrawTransferCount) {
countThreshold = kMaxWithdrawTransferCount;
}
let amountThreshold = kMaxWithdrawTransferAmount;
if (lnProvider.withdrawLiquidityAmountThreshold) {
amountThreshold = BigInt(lnProvider.withdrawLiquidityAmountThreshold);
}
if (filterTransferIds.length >= countThreshold || totalAmount >= amountThreshold) {
// token transfer direction fromChain -> toChain
// withdrawLiquidity message direction toChain -> fromChain
const fromChannelAddress = this.configureService.getMessagerAddress(
fromChainInfo.chainName,
needWithdrawRecords.channel
);
const toChannelAddress = this.configureService.getMessagerAddress(
toChainInfo.chainName,
needWithdrawRecords.channel
);
const messager = messagerInstance(
needWithdrawRecords.channel,
toChannelAddress,
bridge.toWallet
);
const appPayload = toLnv3Contract.encodeWithdrawLiquidity(
filterTransferIds,
toChainInfo.chainId,
lnProvider.relayer
);
const payload = messager.encodePayload(
toChainInfo.chainId,
toChainInfo.lnv3Address,
fromChainInfo.lnv3Address,
appPayload
);
let gasPrice = await toChainInfo.provider.feeData(
1,
toChainInfo.notSupport1559
const params = await messager.params(
toChainInfo.chainId,
fromChainInfo.chainId,
fromChannelAddress,
payload,
lnProvider.relayer
);
const tx = await lnv3Contract.withdrawLiquidity(
const err = await toLnv3Contract.tryWithdrawLiquidity(
fromChainInfo.chainId,
needWithdrawRecords.transferIds,
filterTransferIds,
lnProvider.relayer,
params.extParams,
gasPrice,
params.fee
);
this.logger.log(
`withdrawLiquidity tx ${tx.hash} on ${toChainInfo.chainId}`
);
if (err != null) {
this.logger.warn(
`try to withdraw liquidity failed, err ${err}, from ${fromChainInfo.chainId}, to ${toChainInfo.chainId}`
);
} else {
this.logger.log(
`withdrawLiquidity ${fromChainInfo.chainId}->${
toChainInfo.chainId
}, info: ${JSON.stringify(filterTransferIds)}, fee: ${
params.fee
}`
);
let gasPrice = await toChainInfo.provider.feeData(
1.1,
toChainInfo.notSupport1559
);
const tx = await toLnv3Contract.withdrawLiquidity(
fromChainInfo.chainId,
filterTransferIds,
lnProvider.relayer,
params.extParams,
gasPrice,
params.fee
);
this.logger.log(
`withdrawLiquidity tx ${tx.hash} on ${toChainInfo.chainId}`
);
}
}
}
} catch (e) {
Expand Down
Loading