From f991c529fc3c71df6379562274a44cfc445ef2d4 Mon Sep 17 00:00:00 2001 From: Karishma Date: Tue, 4 Jul 2023 14:16:23 +0530 Subject: [PATCH] Add test for feeProxy with evm call using pallet (#636) * Add test for feeProxy with evm call using pallet --- .github/workflows/image.yml | 1 + e2e/common/index.ts | 143 +++++++++++++++++- .../FeePreferences/FeePreferences.test.ts | 75 ++++++++- 3 files changed, 217 insertions(+), 2 deletions(-) diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml index 3acb51757..70b6c2137 100644 --- a/.github/workflows/image.yml +++ b/.github/workflows/image.yml @@ -23,6 +23,7 @@ on: - "**.lock" - "genesis/**" - "Dockerfile" + - "**.ts" env: REGISTRY: "ghcr.io" diff --git a/e2e/common/index.ts b/e2e/common/index.ts index 52e3de764..972e21f1b 100644 --- a/e2e/common/index.ts +++ b/e2e/common/index.ts @@ -9,7 +9,148 @@ import web3 from "web3"; export * from "./node"; /** TYPEDEFS */ - +export const rpcs = { + dex: { + quote: { + description: "Given some amount of an asset and pair reserves, returns an equivalent amount of the other asset", + params: [ + { + name: "amountA", + type: "u128", + }, + { + name: "reserveA", + type: "u128", + }, + { + name: "reserveB", + type: "u128", + }, + ], + type: "Json", + }, + getAmountsOut: { + description: "Given an array of AssetIds, return amounts out for an amount in", + params: [ + { + name: "amountIn", + type: "Balance", + }, + { + name: "path", + type: "Vec", + }, + ], + type: "Json", + }, + getAmountsIn: { + description: "Given an array of AssetIds, return amounts in for an amount out", + params: [ + { + name: "amountOut", + type: "Balance", + }, + { + name: "path", + type: "Vec", + }, + ], + type: "Json", + }, + getLPTokenID: { + description: "Given two AssetIds, return liquidity token created for the pair", + params: [ + { + name: "assetA", + type: "AssetId", + }, + { + name: "assetB", + type: "AssetId", + }, + ], + type: "Json", + }, + getLiquidity: { + description: "Given two AssetIds, return liquidity", + params: [ + { + name: "assetA", + type: "AssetId", + }, + { + name: "assetB", + type: "AssetId", + }, + ], + type: "Json", + }, + getTradingPairStatus: { + description: "Given two AssetIds, return whether trading pair is enabled or disabled", + params: [ + { + name: "assetA", + type: "AssetId", + }, + { + name: "assetB", + type: "AssetId", + }, + ], + type: "Text", + }, + }, + ethy: { + getEventProof: { + description: "Get ETH event proof for event Id", + params: [ + { + name: "eventId", + type: "EventProofId", + }, + ], + type: "Option", + }, + getXrplTxProof: { + description: "Get XRPL event proof for event Id", + params: [ + { + name: "eventId", + type: "EventProofId", + }, + ], + type: "Option", + }, + }, + nft: { + ownedTokens: { + description: "Get all NFTs owned by an account", + params: [ + { + name: "collectionId", + type: "CollectionUuid", + }, + { + name: "who", + type: "AccountId", + }, + { name: "cursor", type: "SerialNumber" }, + { name: "limit", type: "u16" }, + ], + type: "Json", + }, + tokenUri: { + description: "Get the URI of a token", + params: [ + { + name: "tokenId", + type: "TokenId", + }, + ], + type: "Json", + }, + }, +}; export const typedefs = { AccountId: "EthereumAccountId", AccountId20: "EthereumAccountId", diff --git a/e2e/test/FeePreferences/FeePreferences.test.ts b/e2e/test/FeePreferences/FeePreferences.test.ts index b3da19c41..0f0e19ce8 100644 --- a/e2e/test/FeePreferences/FeePreferences.test.ts +++ b/e2e/test/FeePreferences/FeePreferences.test.ts @@ -1,6 +1,7 @@ import { JsonRpcProvider } from "@ethersproject/providers"; import { ApiPromise, Keyring, WsProvider } from "@polkadot/api"; import { KeyringPair } from "@polkadot/keyring/types"; +import { Codec, IEventData } from "@polkadot/types/types"; import { hexToU8a } from "@polkadot/util"; import { expect } from "chai"; import { BigNumber, Contract, Wallet, utils } from "ethers"; @@ -19,6 +20,7 @@ import { NATIVE_TOKEN_ID, NodeProcess, assetIdToERC20ContractAddress, + rpcs, startNode, typedefs, } from "../../common"; @@ -46,7 +48,7 @@ describe("Fee Preferences", function () { // Setup PolkadotJS rpc provider const wsProvider = new WsProvider(`ws://localhost:${node.wsPort}`); - api = await ApiPromise.create({ provider: wsProvider, types: typedefs }); + api = await ApiPromise.create({ provider: wsProvider, types: typedefs, rpc: rpcs }); const keyring = new Keyring({ type: "ethereum" }); alith = keyring.addFromSeed(hexToU8a(ALITH_PRIVATE_KEY)); bob = keyring.addFromSeed(hexToU8a(BOB_PRIVATE_KEY)); @@ -558,6 +560,77 @@ describe("Fee Preferences", function () { expect(error.code).to.be.eq("INSUFFICIENT_FUNDS"); expect(error.reason).to.be.eq("insufficient funds for intrinsic transaction cost"); }); + + it("Pays fees in non-native token with extrinsic - check maxPayment works fine", async () => { + const erc20PrecompileAddress = assetIdToERC20ContractAddress(FEE_TOKEN_ASSET_ID); + const sender = alith.address; + const value = 0; //eth + const gasLimit = 22953; + const maxFeePerGas = "15000000000000"; + const maxPriorityFeePerGas = null; + const nonce = null; + const accessList = null; + const transferAmount = 1; + const iface = new utils.Interface(ERC20_ABI); + const encodedInput = iface.encodeFunctionData("transfer", [bob.address, transferAmount]); + + const evmCall = api.tx.evm.call( + sender, + erc20PrecompileAddress, + encodedInput, + value, + gasLimit, + maxFeePerGas, + maxPriorityFeePerGas, + nonce, + accessList, + ); + + // Find estimate cost for evm call + const evmCallGasEstimate = await evmCall.paymentInfo(sender); + const evmCallGasEstimateinXRP = evmCallGasEstimate.partialFee; + + // Find estimate cost for feeProxy call + const extrinsicInfo = await api.tx.feeProxy + .callWithFeePreferences( + FEE_TOKEN_ASSET_ID, + utils.parseEther("1").toString(), // 10e18 + api.createType("Call", evmCall).toHex(), + ) + .paymentInfo(sender); + const feeProxyGasEstimateinXRP = extrinsicInfo.partialFee; + + // cost for evm call + cost for fee proxy + const estimatedTotalGasCost = evmCallGasEstimateinXRP.toNumber() + feeProxyGasEstimateinXRP.toNumber(); + + const { + Ok: [estimatedTokenTxCost], + } = await (api.rpc as any).dex.getAmountsIn(estimatedTotalGasCost, [FEE_TOKEN_ASSET_ID, GAS_TOKEN_ID]); + + const eventData = await new Promise((resolve, reject) => { + return api.tx.feeProxy + .callWithFeePreferences( + FEE_TOKEN_ASSET_ID, + estimatedTokenTxCost.toString(), + api.createType("Call", evmCall).toHex(), + ) + .signAndSend(alith, ({ events, status }) => { + if (status.isInBlock) { + for (const { event } of events) { + if (event.section === "feeProxy" && event.method === "CallWithFeePreferences") { + resolve(event.data); + } + } + reject(null); + } + }); + expect(eventData).to.exist; + const [from, paymentAsset, maxPayment] = eventData; + expect(paymentAsset.toString()).to.equal(FEE_TOKEN_ASSET_ID.toString()); + expect(from.toString()).to.equal(alith.address.toString()); + expect(maxPayment.toString()).to.equal(estimatedTokenTxCost.toString()); + }); + }); }); async function getAmountIn(provider: JsonRpcProvider, estimate: BigNumber, feeTokenAssetId: number): Promise {