Skip to content

Commit

Permalink
fix: using erc20 interface for usdt transfers
Browse files Browse the repository at this point in the history
  • Loading branch information
dafuga committed Nov 10, 2023
1 parent 1f41dc9 commit 47de294
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 46 deletions.
32 changes: 28 additions & 4 deletions src/lib/evm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ import {getClient} from '~/api-client'
import erc20_abi from './data/erc20_abi.json'

export type AvailableEvms = 'eos-mainnet' | 'telos'

export interface EvmToken {
name: string;
symbol: string;
decimals: number;
address?: string,
nativeToken?: boolean
}
export interface EvmChainConfig {
chainId: string
chainName: string
tokens: {name: string; symbol: string; decimals: number; address?: string, nativeToken?: boolean}[]
tokens: EvmToken[]
rpcUrls: string[]
blockExplorerUrls: string[]
}
Expand Down Expand Up @@ -79,6 +87,23 @@ export class EvmSession {
return evmChainConfigs[this.chainName]
}

erc20ContractBySymbol(tokenSymbol: Asset.SymbolType) {
const token = this.getTokens()?.find(token => token.symbol === String(tokenSymbol))

return token && this.erc20Contract(token)
}

erc20Contract(token: EvmToken) {
if (!token.address) {
return
}
return new ethers.Contract(token.address, erc20_abi, this.signer);
}

erc20Interface() {
return new ethers.utils.Interface(erc20_abi);
}

static async from({chainName, nativeAccountName}: EvmSessionFromParams) {
if (window.ethereum) {
const evmChainConfig = evmChainConfigs[chainName]
Expand Down Expand Up @@ -128,11 +153,10 @@ export class EvmSession {

let wei: BigNumber

if (token?.address) {
const contract = new ethers.Contract(token.address, erc20_abi, this.signer);
const contract = this.erc20Contract(token)

if (contract) {
wei = await contract.balanceOf(this.address)

} else if (token?.nativeToken) {
wei = await this.signer.getBalance()
} else {
Expand Down
1 change: 1 addition & 0 deletions src/pages/transfer/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
}
try {
console.log({from})
transferFee = await transferManager?.transferFee(transferAmount || received, from?.symbol)
} catch (error) {
if (
Expand Down
147 changes: 105 additions & 42 deletions src/pages/transfer/managers/evmEosBridge.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Asset} from 'anchor-link'
import {Asset, PackedTransaction} from 'anchor-link'
import {ethers} from 'ethers'

import {convertToEvmAddress, getProvider} from '~/lib/evm'
Expand All @@ -17,67 +17,130 @@ export class EvmEosBridge extends TransferManager {
}

async transferFee(amount: string, tokenSymbol: Asset.SymbolType = '4,EOS') {
const {gas, gasPrice} = await this.estimateGas(amount)
console.log('transferFee', {amount, tokenSymbol})
const {gas, gasPrice} = await this.estimateGas(amount, tokenSymbol)

console.log('transferFee', {gas, gasPrice})

const feeAmount = ethers.utils.formatEther(Number(gas) * Number(gasPrice))

return Asset.fromFloat(Number(amount), tokenSymbol)
return Asset.fromFloat(Number(feeAmount), tokenSymbol)
}

async transfer(amount: string, _tokenSymbol: Asset.SymbolType, amountReceived?: string) {
const targetEvmAddress = convertToEvmAddress(String(this.nativeSession.auth.actor))

const amountToTransfer = amountReceived || amount

const {gas} = await this.estimateGas(amountToTransfer)

// In the case of an USDT transfer, we must construct the transaction differently here
// From EVM bridge FE:
//
// USDT
// const fee = await this.erc20_contract().methods.egressFee().call()
// let tx = {
// from: this.address,
// to: this.erc20_addr(),
// value: fee,
// gasPrice: this.gasPrice,
// data: this.erc20_contract().methods.bridgeTransfer(this.addressEvm, this.transferValue, this.memo).encodeABI(),
// }

// if (gaslimit != null) {
// tx.gas = gaslimit;
// }
// return tx
//

// async transfer(amount: string, tokenSymbol: Asset.SymbolType, amountReceived?: string) {
// const amountToTransfer = amountReceived || amount;

// const { gas, gasPrice } = await this.estimateGas(amountToTransfer, tokenSymbol);

// const transaction = this.transactionParams(amountToTransfer, tokenSymbol);

// return this.evmSession.sendTransaction({
// ...transaction,
// gasPrice,
// gasLimit: gas,
// });
// }

async transfer(amount: string, tokenSymbol: Asset.SymbolType, amountReceived?: string) {
const token = this.evmSession.getTokens().find((t) => t.symbol === String(tokenSymbol));
console.log({ token, tokenSymbol, amount, amountReceived });

if (!token) {
throw new Error(`Token ${tokenSymbol} not found`);
}

// When no address is available, convert the native address to an EVM address
const targetEvmAddress = token.address || convertToEvmAddress(String(this.nativeSession.auth.actor));

const erc20Interface = this.evmSession.erc20Interface();

let data;

if (token.address) {
// Include the memo parameter, even if it's an empty string
const memo = ''; // You can replace this with an actual memo if needed

// Encode the bridgeTransfer data using the ERC20 interface
data = erc20Interface.encodeFunctionData("bridgeTransfer", [targetEvmAddress, ethers.utils.parseEther(amount), memo]);
console.log({ data });
} else {
data = ethers.utils.formatBytes32String('');
}

const amountToTransfer = amountReceived || amount;

const { gas } = await this.estimateGas(amountToTransfer);

const transaction = await this.transactionParams(amountToTransfer, tokenSymbol);

return this.evmSession.sendTransaction({
...transaction,
gasLimit: gas,
});
}


private async transactionParams(amount: string, tokenSymbol: Asset.SymbolType) {
const token = this.evmSession.getTokens().find((t) => t.symbol === String(tokenSymbol));

if (!token) {
throw new Error(`Token ${tokenSymbol} not found`);
}

console.log({token, tokenSymbol})

const erc20Contract = this.evmSession.erc20Contract(token);

console.log({ erc20Contract })

let data;
let egressFee;

console.log({erc20Contract})

if (erc20Contract) {
const erc20Interface = this.evmSession.erc20Interface();

// Correctly encode the bridgeTransfer function
data = erc20Interface.encodeFunctionData("bridgeTransfer", [this.evmSession.address, ethers.utils.parseEther(amount), '']);

console.log({data})
egressFee = await erc20Contract.egressFee();
} else {
data = ethers.utils.formatBytes32String('');
}

console.log({amount, egressFee})

return {
from: this.evmSession.address,
to: targetEvmAddress,
value: ethers.utils.parseEther(amountToTransfer),
to: token.address, // The contract address, not the recipient
value: erc20Contract ? egressFee || 0 : ethers.utils.formatEther(amount), // For ERC20 transfers, this is often 0
gasPrice: await getProvider().getGasPrice(),
gasLimit: gas,
data: ethers.utils.formatBytes32String(''),
})
data,
};
}


private async estimateGas(amount: string) {
private async estimateGas(amount: string, tokenSymbol: Asset.SymbolType = '4,EOS') {
const provider = getProvider()

const targetEvmAddress = convertToEvmAddress(String(this.nativeSession.auth.actor))

const gasPrice = await provider.getGasPrice()

console.log({gasPrice})

// Reducing the amount by 0.005 EOS to avoid getting an error when entire balance is sent. Proper amount is calculated once the gas fee is known.
const reducedAmount = String(Number(amount) - 0.005)

const transaction = await this.transactionParams(reducedAmount, tokenSymbol)

console.log({transaction})

const gas = await provider.estimateGas({
from: this.evmSession.address,
to: targetEvmAddress,
value: ethers.utils.parseEther(reducedAmount),
...transaction,
gasPrice,
data: ethers.utils.formatBytes32String(''),
})

return {gas, gasPrice}
}
}
}

0 comments on commit 47de294

Please sign in to comment.