@kaiachain/kss-bridges-celer
Version:
TypeScript client and use-cases for cBridge
226 lines (202 loc) • 7.03 kB
text/typescript
import { BigNumber, Contract, ethers, utils } from "ethers"
import { TransactionResponse } from "@ethersproject/abstract-provider"
import { parseUnits } from "@ethersproject/units"
import { safeParseUnits } from "celer-web-utils/lib/format"
import { ITransferConfigs, ITransferObject } from "../constants/type"
import { getSigner } from "./constant"
import ERC20ABI from "../contract/abi/pegged/tokens/ERC20Permit/SingleBridgeTokenPermit.sol/SingleBridgeTokenPermit.json"
import { Token } from "../constants/type"
const tokenInterface = new utils.Interface(ERC20ABI.abi)
export const getBridgeContractAddress = (transferConfigs: ITransferConfigs, chainId: number) => {
return transferConfigs.chains.find(chain => chain.id === chainId)?.contract_addr
}
export const getTransferId = (
address: string,
tokenAddress?: string,
value?: BigNumber,
toChainId?: number,
nonce?: number,
fromChainId?: number
) => {
return utils.solidityKeccak256(
["address", "address", "address", "uint256", "uint64", "uint64", "uint64"],
[
address,
address,
tokenAddress,
value?.toString(),
toChainId?.toString(),
nonce?.toString(),
fromChainId?.toString(),
]
)
}
export const transactor = async (tx: any, rpcURL: string, privateKey: string): Promise<ethers.ContractTransaction> => {
let result: TransactionResponse;
if (tx instanceof Promise) {
result = await tx;
} else {
if (!tx.gasPrice) {
tx.gasPrice = parseUnits("4.1", "gwei")
}
if (!tx.gasLimit) {
tx.gasLimit = BigNumber.from(120000)
}
result = await getSigner(rpcURL, privateKey).sendTransaction(tx);
}
return result!;
}
export const getTransferObject = (
transferConfigs: ITransferConfigs,
srcChainId: number,
dstChainId: number,
tokenSymbol: string,
transferValue: string
): ITransferObject => {
const transferObject: ITransferObject = {}
const transferToken = transferConfigs.chain_token[`${srcChainId}`]?.token.find(
({ token }) => token.symbol === tokenSymbol
)
const fromChain = transferConfigs.chains.find(({ id }) => id === srcChainId)
const toChain = transferConfigs.chains.find(({ id }) => id === dstChainId)
const value = safeParseUnits(transferValue, transferToken?.token?.decimal ?? 18)
const nonce = new Date().getTime()
Object.assign(transferObject, { transferToken, fromChain, toChain, value, nonce })
return transferObject
}
export const getPegConfig = (
transferConfigs: ITransferConfigs,
srcChainId: number,
dstChainId: number,
tokenSymbol: string
) => {
const depositConfigs = transferConfigs.pegged_pair_configs.filter(
(e) =>
e.org_chain_id === srcChainId &&
e.pegged_chain_id === dstChainId &&
e.org_token.token.symbol === tokenSymbol
)
if (depositConfigs.length) {
return depositConfigs[0]
}
const burnConfigs = transferConfigs.pegged_pair_configs.filter(
(e) =>
e.org_chain_id === dstChainId &&
e.pegged_chain_id === srcChainId &&
e.org_token.token.symbol === tokenSymbol
)
if (burnConfigs.length) {
return burnConfigs[0]
}
return
}
const isNonEVMChain = (chainId: number) => {
if (
chainId === 12340001 ||
chainId === 12340002 ||
chainId === 999999998 ||
chainId === 999999999
) {
return true
}
return false
}
const getTokenBalanceAddress = (
originalAddress: string,
fromChainId: number | undefined = undefined,
tokenSymbol: string | undefined = undefined,
peggedPairs: Array<any> | undefined = undefined
) => {
if (!fromChainId || !tokenSymbol || !peggedPairs) {
return originalAddress
}
const peggedTokens = peggedPairs?.filter((item) => {
return (
item.pegged_chain_id === fromChainId && tokenSymbol === item.pegged_token.token.symbol
)
})
if (
peggedTokens &&
peggedTokens.length > 0 &&
peggedTokens[0].canonical_token_contract_addr.length > 0
) {
return peggedTokens[0].canonical_token_contract_addr
}
if (isNonEVMChain(fromChainId)) {
const nonEVMDeposit = peggedPairs?.find((peggedPairConfig) => {
return (
peggedPairConfig.org_chain_id === fromChainId &&
peggedPairConfig.org_token.token.symbol === tokenSymbol
)
})
if (nonEVMDeposit) {
return nonEVMDeposit.pegged_token.token.address
}
const nonEVMBurn = peggedPairs?.find((peggedPairConfig) => {
return (
peggedPairConfig.pegged_chain_id === fromChainId &&
peggedPairConfig.pegged_token.token.symbol === tokenSymbol
)
})
if (nonEVMBurn) {
return nonEVMBurn.org_token.token.address
}
}
return originalAddress
}
export const getAllowance = async (
walletAddress: string,
spenderAddress: string,
originalAddress: string,
fromChainId: number | undefined = undefined,
tokenSymbol: string | undefined = undefined,
rpcURL: string,
peggedPairs: Array<any> | undefined = undefined
) => {
const tokenAddress = getTokenBalanceAddress(
originalAddress,
fromChainId,
tokenSymbol,
peggedPairs
)
const tokenContract = new Contract(tokenAddress, tokenInterface, getSigner(rpcURL))
const allowance = await tokenContract?.allowance(walletAddress, spenderAddress)
return allowance
}
export const checkApprove = (allowance: BigNumber, amount: string, token?: Token, isNative?: boolean) : boolean => {
/**Native token case */
if(isNative) {
return false;
}
if (!allowance || allowance.isZero()) {
return true
}
console.log("Allowance: "+allowance.toString());
console.log("Amount : "+safeParseUnits(amount || "0", token?.decimal ?? 18).toString());
try {
const isGreatThanAllowance = safeParseUnits(amount || "0", token?.decimal ?? 18).gt(
allowance
)
return isGreatThanAllowance
} catch {
return true
}
}
export const approve = async (spenderAddress: string, rpcURL: string, privateKey: string, token?: Token, amount?: string) => {
if (!token) {
return
}
try {
const tokenContract = new Contract(token.address, tokenInterface, getSigner(rpcURL, privateKey))
const approveTx = await transactor(
tokenContract.approve(spenderAddress, safeParseUnits(amount || "0", token?.decimal ?? 18), {gasLimit: 100000 }),
rpcURL,
privateKey
)
await approveTx.wait()
return approveTx
} catch (e) {
console.error(`-Failed to approve token. Error:`, e)
return
}
}