Skip to content

Commit

Permalink
fix!: throw an error when the source account has low balance (#417)
Browse files Browse the repository at this point in the history
Closes #338
  • Loading branch information
Lykhoyda authored Aug 20, 2024
1 parent 4200f28 commit 201942b
Show file tree
Hide file tree
Showing 25 changed files with 449 additions and 145 deletions.
35 changes: 23 additions & 12 deletions examples/evm-to-evm-fungible-transfer/src/transfer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Eip1193Provider, Environment } from "@buildwithsygma/core";
import type { Eip1193Provider } from "@buildwithsygma/core";
import { Environment } from "@buildwithsygma/core";
import { createEvmFungibleAssetTransfer } from "@buildwithsygma/evm";
import dotenv from "dotenv";
import { Wallet, providers } from "ethers";
Expand All @@ -13,12 +14,19 @@ if (!privateKey) {
}

const SEPOLIA_CHAIN_ID = 11155111;
const HOLESKY_CHAIN_ID = 17000;
const RESOURCE_ID = "0x0000000000000000000000000000000000000000000000000000000000000200";
const SEPOLIA_RPC_URL = process.env.SEPOLIA_RPC_URL || "https://eth-sepolia-public.unifra.io"
const AMOY_CHAIN_ID = 80002;
const RESOURCE_ID =
"0x0000000000000000000000000000000000000000000000000000000000000300";
const SEPOLIA_RPC_URL =
process.env.SEPOLIA_RPC_URL || "https://eth-sepolia-public.unifra.io";

const explorerUrls: Record<number, string> = { [SEPOLIA_CHAIN_ID]: 'https://sepolia.etherscan.io' };
const getTxExplorerUrl = (params: { txHash: string; chainId: number }): string => `${explorerUrls[params.chainId]}/tx/${params.txHash}`;
const explorerUrls: Record<number, string> = {
[SEPOLIA_CHAIN_ID]: "https://sepolia.etherscan.io",
};
const getTxExplorerUrl = (params: {
txHash: string;
chainId: number;
}): string => `${explorerUrls[params.chainId]}/tx/${params.txHash}`;

export async function erc20Transfer(): Promise<void> {
const web3Provider = new Web3HttpProvider(SEPOLIA_RPC_URL);
Expand All @@ -28,29 +36,32 @@ export async function erc20Transfer(): Promise<void> {

const params = {
source: SEPOLIA_CHAIN_ID,
destination: HOLESKY_CHAIN_ID,
destination: AMOY_CHAIN_ID,
sourceNetworkProvider: web3Provider as unknown as Eip1193Provider,
resource: RESOURCE_ID,
amount: BigInt(2) * BigInt(1e18),
amount: BigInt(1) * BigInt(1e18),
destinationAddress: destinationAddress,
environment: Environment.DEVNET,
environment: (process.env.SYGMA_ENV as Environment) || Environment.TESTNET,
sourceAddress: destinationAddress,
};

const transfer = await createEvmFungibleAssetTransfer(params);

const approvals = await transfer.getApprovalTransactions();
console.log(`Approving Tokens (${approvals.length})...`);
for (const approval of approvals) {
const response = await wallet.sendTransaction(approval);
await response.wait();
console.log(`Approved, transaction: ${getTxExplorerUrl({ txHash: response.hash, chainId: SEPOLIA_CHAIN_ID })}`);
console.log(
`Approved, transaction: ${getTxExplorerUrl({ txHash: response.hash, chainId: SEPOLIA_CHAIN_ID })}`,
);
}

const transferTx = await transfer.getTransferTransaction();
const response = await wallet.sendTransaction(transferTx);
await response.wait();
console.log(`Depositted, transaction: ${getTxExplorerUrl({ txHash: response.hash, chainId: SEPOLIA_CHAIN_ID })}`);
console.log(
`Deposited, transaction: ${getTxExplorerUrl({ txHash: response.hash, chainId: SEPOLIA_CHAIN_ID })}`,
);
}

erc20Transfer().finally(() => {});
2 changes: 1 addition & 1 deletion examples/substrate-to-evm-fungible-transfer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"dependencies": {
"@buildwithsygma/core": "workspace:*",
"@buildwithsygma/substrate": "workspace:*",
"@polkadot/api": "^12.2.1",
"@polkadot/api": "^12.3.1",
"@polkadot/keyring": "^12.6.2",
"@polkadot/util-crypto": "^12.6.2",
"tsx": "^4.15.4"
Expand Down
7 changes: 4 additions & 3 deletions examples/substrate-to-evm-fungible-transfer/src/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ const substrateTransfer = async (): Promise<void> => {
const api = await ApiPromise.create({ provider: wsProvider });

const transferParams: SubstrateAssetTransferRequest = {
sourceDomain: RHALA_CHAIN_ID,
destinationDomain: SEPOLIA_CHAIN_ID,
source: RHALA_CHAIN_ID,
destination: SEPOLIA_CHAIN_ID,
sourceNetworkProvider: api,
resource: RESOURCE_ID_SYGMA_USD,
amount: BigInt("5000000"),
amount: BigInt("1"),
destinationAddress: recipient,
sourceAddress: account.address,
};

const transfer = await createSubstrateFungibleAssetTransfer(transferParams);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"resolveJsonModule": true
},
"exclude": ["node_modules/**"],
"include": ["./src/**/*.ts", "./test/**/*.ts", "substrate-asset-transfer.ts"],
"include": ["./src/**/*.ts", "./test/**/*.ts", "substrate-asset-transfer.ts", "./src/environment.d.ts"],
"ts-node": {
"esm": true,
"experimentalSpecifierResolution": "node"
Expand Down
99 changes: 97 additions & 2 deletions packages/evm/src/__test__/fungible.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,21 @@ jest.mock('@buildwithsygma/core', () => ({
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
jest.mock('@ethersproject/providers', () => ({
...jest.requireActual('@ethersproject/providers'),
Web3Provider: jest.fn(),
Web3Provider: jest.fn().mockImplementation(() => {
return {
getBalance: jest.fn().mockResolvedValue(BigNumber.from('110000000000000000')),
};
}),
}));

// eslint-disable-next-line @typescript-eslint/no-unsafe-return
jest.mock('@buildwithsygma/sygma-contracts', () => ({
...jest.requireActual('@buildwithsygma/sygma-contracts'),
Bridge__factory: { connect: jest.fn() },
ERC20__factory: { connect: jest.fn() },
ERC20__factory: {
balanceOf: jest.fn().mockResolvedValue(BigInt(1)),
connect: jest.fn(),
},
BasicFeeHandler__factory: { connect: jest.fn() },
PercentageERC20FeeHandler__factory: { connect: jest.fn() },
FeeHandlerRouter__factory: { connect: jest.fn() },
Expand Down Expand Up @@ -182,6 +189,7 @@ describe('Fungible - Approvals', () => {
});

(ERC20__factory.connect as jest.Mock).mockReturnValue({
balanceOf: jest.fn().mockResolvedValue(BigNumber.from(parseEther('50'))),
populateTransaction: {
approve: jest.fn().mockResolvedValue({}),
},
Expand All @@ -206,6 +214,55 @@ describe('Fungible - Approvals', () => {

expect(approvals.length).toBeGreaterThan(0);
});

it('should throw an error if balance is not sufficient - Basic', async () => {
(BasicFeeHandler__factory.connect as jest.Mock).mockReturnValue({
feeHandlerType: jest.fn().mockResolvedValue('basic'),
calculateFee: jest.fn().mockResolvedValue([parseEther('1')]),
});

const transfer = await createEvmFungibleAssetTransfer({
...TRANSFER_PARAMS,
amount: parseEther('0').toBigInt(),
});

await expect(transfer.getApprovalTransactions()).rejects.toThrow(
'Insufficient native token balance for network',
);
});

it('should throw an error if balance is not sufficient - Percentage', async () => {
(BasicFeeHandler__factory.connect as jest.Mock).mockReturnValue({
feeHandlerType: jest.fn().mockResolvedValue('percentage'),
calculateFee: jest.fn().mockResolvedValue([BigNumber.from(0)]),
});
(PercentageERC20FeeHandler__factory.connect as jest.Mock).mockReturnValue({
feeHandlerType: jest.fn().mockResolvedValue('percentage'),
calculateFee: jest.fn().mockResolvedValue([BigNumber.from(0)]),
_resourceIDToFeeBounds: jest.fn().mockResolvedValue({
lowerBound: parseEther('10'),
upperBound: parseEther('100'),
}),
_domainResourceIDToFee: jest.fn().mockResolvedValue(BigNumber.from(100)),
HUNDRED_PERCENT: jest.fn().mockResolvedValue(10000),
});
(ERC20__factory.connect as jest.Mock).mockReturnValue({
balanceOf: jest.fn().mockResolvedValue(BigNumber.from(parseEther('1').toBigInt())), // Mock balance less than the required amount
populateTransaction: {
approve: jest.fn().mockResolvedValue({}),
},
allowance: jest.fn().mockResolvedValue(parseEther('0')),
});

const transfer = await createEvmFungibleAssetTransfer({
...TRANSFER_PARAMS,
amount: parseEther('100').toBigInt(),
});

await expect(transfer.getApprovalTransactions()).rejects.toThrow(
'Insufficient ERC20 token balance',
);
});
});

describe('Fungible - Deposit', () => {
Expand All @@ -223,6 +280,14 @@ describe('Fungible - Deposit', () => {
.mockResolvedValue('0x98729c03c4D5e820F5e8c45558ae07aE63F97461'),
});

(ERC20__factory.connect as jest.Mock).mockReturnValue({
balanceOf: jest.fn().mockResolvedValue(BigNumber.from(parseEther('50').toBigInt())),
populateTransaction: {
approve: jest.fn().mockResolvedValue({}),
},
allowance: jest.fn().mockResolvedValue(parseEther('0')),
});

(Bridge__factory.connect as jest.Mock).mockReturnValue({
populateTransaction: {
deposit: jest.fn().mockReturnValue({
Expand All @@ -249,4 +314,34 @@ describe('Fungible - Deposit', () => {

expect(depositTransaction).toBeTruthy();
});

it('should throw ERROR - Insufficient account balance - Percentage', async () => {
(BasicFeeHandler__factory.connect as jest.Mock).mockReturnValue({
feeHandlerType: jest.fn().mockResolvedValue('percentage'),
calculateFee: jest.fn().mockResolvedValue([BigNumber.from(0)]),
});
(PercentageERC20FeeHandler__factory.connect as jest.Mock).mockReturnValue({
feeHandlerType: jest.fn().mockResolvedValue('percentage'),
calculateFee: jest.fn().mockResolvedValue([BigNumber.from(0)]),
_resourceIDToFeeBounds: jest.fn().mockResolvedValue({
lowerBound: parseEther('10'),
upperBound: parseEther('100'),
}),
_domainResourceIDToFee: jest.fn().mockResolvedValue(BigNumber.from(100)),
HUNDRED_PERCENT: jest.fn().mockResolvedValue(10000),
});
(ERC20__factory.connect as jest.Mock).mockReturnValue({
balanceOf: jest.fn().mockResolvedValue(BigNumber.from(parseEther('1').toBigInt())), // Mock balance less than the required amount
populateTransaction: {
approve: jest.fn().mockResolvedValue({}),
},
allowance: jest.fn().mockResolvedValue(parseEther('0')),
});

const transfer = await createEvmFungibleAssetTransfer(TRANSFER_PARAMS);

await expect(transfer.getTransferTransaction()).rejects.toThrow(
'Insufficient ERC20 token balance',
);
});
});
Empty file.
1 change: 0 additions & 1 deletion packages/evm/src/evmTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export class EvmTransfer extends BaseTransfer {

/**
* Returns fee based on transfer amount.
* @param amount By default it is original amount passed in constructor
*/
async getFee(): Promise<EvmFee> {
const provider = new providers.Web3Provider(this.sourceNetworkProvider);
Expand Down
1 change: 1 addition & 0 deletions packages/evm/src/fee/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './BasicFee.js';
export * from './getFeeInformation.js';
export * from './PercentageFee.js';
export * from './TwapFee.js';
export * from './types.js';
3 changes: 2 additions & 1 deletion packages/evm/src/fee/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { FeeHandlerType } from '@buildwithsygma/core';
import type { ethers } from 'ethers';
import type { EvmFee } from 'types';

import type { EvmFee } from '../types.js';

/**
* Parameters that are required to
Expand Down
Loading

0 comments on commit 201942b

Please sign in to comment.