Skip to content

FeeProxy (Native API example) ​

Pay gas in a token other than XRP, around a remark, a batch, an EVM call and a FuturePass proxy call. Reference: feeProxy pallet.

Copied from Futureverse’s futureversecom/trn-examples, deleted with its GitHub organisation, at commit 44c9e16 of 2024-10-31. Apache-2.0: license. Before you run it, read Running the examples.

You don’t need a FuturePass

This page treats FuturePass as something to use. You don’t need one: where the page uses a FuturePass, use an account in your own wallet instead — any EIP-6963 wallet app, MetaMask preferred. If you have a FuturePass, you can move what’s in it to your wallet at any time: https://futurepass.trn.codeology.co.nz/wallet/futurepass. FuturePass explains how it works.

Offline destinations

Some places this page points to did not answer when last checked:

  • docs-beta.therootnetwork.com — does not resolve (checked 2026-10-18)
  • stackblitz.com/github/futureversecom — StackBlitz loads these projects from the deleted futureversecom organisation on GitHub (checked 2026-10-18)

Run in StackBlitz (repository deleted, checked 2026-10-18) · Pallet Documentation (offline, checked 2026-10-18)

IMPORTANT

Ensure the following ENV vars are available before running the examples

Examples ​

bash
# change your working directory to this example first
cd examples/substrate/use-feeProxy

# export all required environments
export CALLER_PRIVATE_KEY=

# `feeProxy.callWithFeePreferences` that wraps around `system.remarkWithEvent`
pnpm call:callSystemRemark

# `feeProxy.callWithFeePreferences` that wraps around `utility.batchAll`
pnpm call:callBatchAll

# `feeProxy.callWithFeePreferences` that wraps around `evm.call`
pnpm call:callEVMCall

# `feeProxy.callWithFeePreferences` that wraps around `futurepass.proxyExtrinsic`
pnpm call:callProxyExtrinsic

# `feeProxy.callWithFeePreferences` that wraps around `futurepass.proxyExtrinsic` then `emv.call`
pnpm call:callProxyExtrinsicEVMCall

Source files ​

src/callBatchAll.ts ​

ts
import { ALICE, BOB, CHARLIE } from "@trne/utils/accounts";
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { ASTO_ASSET_ID, XRP_ASSET_ID } from "@trne/utils/porcini-assets";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";

interface AmountsIn {
	Ok: [number, number];
}

/**
 * Use `feeProxy.callWithFeePreferences` to trigger `utility.batchAll` call, which contain multiple
 * calls of `assets.trafer`, and pay gas in ASTO token.
 *
 * Assumes the caller has ASTO balance.
 */
withChainApi("porcini", async (api, caller, logger) => {
	/**
	 * 1. Create `utility.batchAll` call that batches 3 transfer calls to ALICE, BOB & CHARLIE
	 */
	const oneASTO = 1 * Math.pow(10, 18); // 1 ASTO in `wei` unit
	const transferToAliceCall = api.tx.assets.transfer(ASTO_ASSET_ID, ALICE, oneASTO.toString());
	const transferToBobCall = api.tx.assets.transfer(ASTO_ASSET_ID, BOB, oneASTO.toString());
	const transferToCharlieCall = api.tx.assets.transfer(ASTO_ASSET_ID, CHARLIE, oneASTO.toString());

	logger.info(
		{
			parameters: [
				transferToAliceCall.toJSON(),
				transferToBobCall.toJSON(),
				transferToCharlieCall.toJSON(),
			],
		},
		`create a "system.remarkWithEvent"`
	);
	const batchAllCall = api.tx.utility.batchAll([
		transferToAliceCall,
		transferToBobCall,
		transferToCharlieCall,
	]);

	/**
	 * 2. Determine the `maxPayment` in ASTO by estimate the gas cost and use `dex` to get a quote
	 */
	// we need a dummy feeProxy call (with maxPayment=0) to do a proper fee estimation
	const feeProxyCallForEstimation = api.tx.feeProxy.callWithFeePreferences(
		ASTO_ASSET_ID,
		0,
		batchAllCall
	);

	const paymentInfo = await feeProxyCallForEstimation.paymentInfo(caller.address);
	const estimatedFee = paymentInfo.partialFee.toString();

	// query the the `dex` to determine the `maxPayment` you are willing to pay
	const {
		Ok: [amountIn],
	} = (await api.rpc.dex.getAmountsIn(estimatedFee, [
		ASTO_ASSET_ID,
		XRP_ASSET_ID,
	])) as unknown as AmountsIn;

	// allow a buffer to avoid slippage, 5%
	const maxPayment = Number(amountIn * 1.05).toFixed();

	/**
	 * 3. Create and dispatch `feeProxy.callWithFeePreferences` extrinsic
	 */
	logger.info(
		{
			parameters: {
				paymentAsset: ASTO_ASSET_ID,
				maxPayment,
				call: batchAllCall.toJSON(),
			},
		},
		`create a "feeProxy.callWithFeePreferences"`
	);
	const feeProxyCall = api.tx.feeProxy.callWithFeePreferences(
		ASTO_ASSET_ID,
		maxPayment,
		batchAllCall
	);

	logger.info(`dispatch extrinsic from caller="${caller.address}"`);
	const { result, extrinsicId } = await sendExtrinsic(feeProxyCall, caller, { log: logger });
	const [proxyEvent, batchEvent, aliceTransferEvent, bobTransferEvent, charlieTransferEvent] =
		filterExtrinsicEvents(result.events, [
			"FeeProxy.CallWithFeePreferences",
			"Utility.BatchCompleted",
			{ name: "Assets.Transferred", key: "to", data: { value: ALICE, type: "T::AccountId" } },
			{ name: "Assets.Transferred", key: "to", data: { value: BOB, type: "T::AccountId" } },
			{ name: "Assets.Transferred", key: "to", data: { value: CHARLIE, type: "T::AccountId" } },
		]);

	logger.info(
		{
			result: {
				extrinsicId,
				blockNumber: result.blockNumber,
				proxyEvent: formatEventData(proxyEvent.event),
				batchEvent: formatEventData(batchEvent.event),
				aliceTransferEvent: formatEventData(aliceTransferEvent.event),
				bobTransferEvent: formatEventData(bobTransferEvent.event),
				charlieTransferEvent: formatEventData(charlieTransferEvent.event),
			},
		},
		"receive result"
	);
});

src/callEVMCall.ts ​

ts
import { ApiPromise } from "@polkadot/api";
import { ERC20_ABI } from "@therootnetwork/evm";
import { ALICE } from "@trne/utils/accounts";
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { getERC20Contract } from "@trne/utils/getERC20Contract";
import { getFeeProxyPricePair } from "@trne/utils/getFeeProxyPricePair";
import { Logger } from "@trne/utils/getLogger";
import { ASTO_ASSET_ID, SYLO_ASSET_ID, XRP_ASSET_ID } from "@trne/utils/porcini-assets";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";
import { provideEthersProvider } from "@trne/utils/withEthersProvider";
import { BigNumber, utils } from "ethers";

interface AmountsIn {
	Ok: [number, number];
}

/**
 * Use `feeProxy.callWithFeePreferencs` to trigger `evm.call`
 *
 * Assumes the caller has ASTO balance to pay for gas and SYLO balance to demonstrate
 * the transfer
 */
withChainApi("porcini", async (api, caller, logger) => {
	/**
	 * 1. Create `emv.call` call
	 */
	const { call: evmCall, estimateGasCost } = await createEVMCall(caller.address, api, logger);

	/**
	 * 2. Determine the `maxPayment` in ASTO by estimate the gas cost and use `dex` to get a quote
	 */
	// we need a dummy feeProxy call (with maxPayment=0) to do a proper fee estimation
	const feeProxyCallForEstimation = api.tx.feeProxy.callWithFeePreferences(
		ASTO_ASSET_ID,
		0,
		evmCall
	);
	const paymentInfo = await feeProxyCallForEstimation.paymentInfo(caller.address);

	// we need to add the actual estimate cost for the EVM layer call as part of the estimation
	const estimatedFee = BigNumber.from(paymentInfo.partialFee.toString())
		.add(estimateGasCost)
		.toString();

	// query the the `dex` to determine the `maxPayment` you are willing to pay
	const {
		Ok: [amountIn],
	} = (await api.rpc.dex.getAmountsIn(estimatedFee, [
		ASTO_ASSET_ID,
		XRP_ASSET_ID,
	])) as unknown as AmountsIn;

	// allow a buffer to avoid slippage, 5%
	const maxPayment = Number(amountIn * 1.05).toFixed();

	/**
	 * 3. Create and dispatch `feeProxy.callWithFeePreferences` extrinsic
	 */
	logger.info(
		{
			parameters: {
				paymentAsset: ASTO_ASSET_ID,
				maxPayment,
				call: evmCall.toJSON(),
			},
		},
		`create a "feeProxy.callWithFeePreferences"`
	);
	const feeProxyCall = api.tx.feeProxy.callWithFeePreferences(ASTO_ASSET_ID, maxPayment, evmCall);

	logger.info(`dispatch extrinsic from caller="${caller.address}"`);
	const { result, extrinsicId } = await sendExtrinsic(feeProxyCall, caller, { log: logger });

	const [proxyEvent, evmLogEvent, aliceTransferEvent] = filterExtrinsicEvents(result.events, [
		"FeeProxy.CallWithFeePreferences",
		"Evm.Log",
		{ name: "Assets.Transferred", key: "to", data: { value: ALICE, type: "T::AccountId" } },
	]);

	logger.info(
		{
			result: {
				extrinsicId,
				blockNumber: result.blockNumber,
				proxyEvent: formatEventData(proxyEvent.event),
				evmLogEvent: formatEventData(evmLogEvent.event),
				aliceTransferEvent: formatEventData(aliceTransferEvent.event),
			},
		},
		"receive result"
	);
});

async function createEVMCall(caller: string, api: ApiPromise, logger: Logger) {
	// setup the actual EVM transaction, as interface
	const { provider, wallet } = await provideEthersProvider("porcini");
	const transferToken = getERC20Contract(SYLO_ASSET_ID).connect(wallet);
	const transferAmount = utils.parseUnits("0.1", 18); // 0.1 SYLO
	const transferInput = new utils.Interface(ERC20_ABI).encodeFunctionData("transfer", [
		ALICE,
		transferAmount,
	]);
	const transferEstimate = await transferToken.estimateGas.transfer(ALICE, transferAmount);
	const { maxFeePerGas, estimateGasCost } = await getFeeProxyPricePair(
		provider,
		transferEstimate,
		ASTO_ASSET_ID,
		0.05
	);

	logger.info(
		{
			parameters: {
				source: caller,
				target: transferToken.address,
				input: transferInput,
				gasLimit: transferEstimate.toString(),
				maxFeePerGas: maxFeePerGas.toString(),
			},
		},
		`create an "emv.call"`
	);

	return {
		call: api.tx.evm.call(
			caller,
			transferToken.address,
			transferInput,
			0,
			transferEstimate.toString(),
			maxFeePerGas.toString(),
			0,
			null,
			[]
		),
		estimateGasCost,
	};
}

src/callProxyExtrinsic.ts ​

ts
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { ASTO_ASSET_ID, XRP_ASSET_ID } from "@trne/utils/porcini-assets";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";
import assert from "node:assert";

interface AmountsIn {
	Ok: [number, number];
}

/**
 * Use `feeProxy.callWithFeePreferences` to trigger `system.remarkWithEvent` call via
 * `futurepass.proxyExtrinsic`, and have Futurepass account pays gas in ASTO.
 *
 * Assumes the caller has a valid Futurepass account and ASTO balance to pay for gas.
 */
withChainApi("porcini", async (api, caller, logger) => {
	/**
	 * 1. Create `futurepass.proxyExtrinsic` call that wraps around `system.remarkWithEvent` call
	 */
	const fpAccount = (await api.query.futurepass.holders(caller.address)).unwrap();
	logger.info(
		{
			futurepass: {
				holder: caller.address,
				account: fpAccount.toString(),
			},
		},
		"futurepass details"
	);

	assert(fpAccount);

	// can be any extrinsic, using `system.remarkWithEvent` for simplicity
	const remarkCall = api.tx.system.remarkWithEvent("Hello World");
	logger.info(
		{
			parameters: {
				futurepass: fpAccount,
				call: remarkCall.toJSON(),
			},
		},
		`create a "futurepass.proxyExtrinsic"`
	);
	const futurepassCall = api.tx.futurepass.proxyExtrinsic(fpAccount, remarkCall);

	/**
	 * 2. Determine the `maxPayment` in ASTO by estimate the gas cost and use `dex` to get a quote
	 */
	// we need a dummy feeProxy call (with maxPayment=0) to do a proper fee estimation
	const feeProxyCallForEstimation = api.tx.feeProxy.callWithFeePreferences(
		ASTO_ASSET_ID,
		0,
		futurepassCall
	);
	const paymentInfo = await feeProxyCallForEstimation.paymentInfo(caller.address);
	const estimatedFee = paymentInfo.partialFee.toString();

	// query the the `dex` to determine the `maxPayment` you are willing to pay
	const {
		Ok: [amountIn],
	} = (await api.rpc.dex.getAmountsIn(estimatedFee, [
		ASTO_ASSET_ID,
		XRP_ASSET_ID,
	])) as unknown as AmountsIn;

	// allow a buffer to avoid slippage, 5%
	const maxPayment = Number(amountIn * 1.05).toFixed();

	/**
	 * 3. Create and dispatch `feeProxy.callWithFeePreferences` extrinsic
	 */
	logger.info(
		{
			parameters: {
				paymentAsset: ASTO_ASSET_ID,
				maxPayment,
				call: futurepassCall.toJSON(),
			},
		},
		`create a "feeProxy.callWithFeePreferences"`
	);
	const feeProxyCall = api.tx.feeProxy.callWithFeePreferences(
		ASTO_ASSET_ID,
		maxPayment,
		futurepassCall
	);

	logger.info(`dispatch extrinsic from caller="${caller.address}"`);
	const { result, extrinsicId } = await sendExtrinsic(feeProxyCall, caller, { log: logger });
	const [proxyEvent, futurepassEvent, remarkEvent] = filterExtrinsicEvents(result.events, [
		"FeeProxy.CallWithFeePreferences",
		"Futurepass.ProxyExecuted",
		"System.Remarked",
	]);

	logger.info(
		{
			result: {
				extrinsicId,
				blockNumber: result.blockNumber,
				proxyEvent: formatEventData(proxyEvent.event),
				futurepassEvent: formatEventData(futurepassEvent.event),
				remarkEvent: formatEventData(remarkEvent.event),
			},
		},
		"receive result"
	);
});

src/callProxyExtrinsicEVMCall.ts ​

ts
import { ApiPromise } from "@polkadot/api";
import { ERC20_ABI } from "@therootnetwork/evm";
import { ALICE } from "@trne/utils/accounts";
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { getERC20Contract } from "@trne/utils/getERC20Contract";
import { getFeeProxyPricePair } from "@trne/utils/getFeeProxyPricePair";
import { Logger } from "@trne/utils/getLogger";
import { ASTO_ASSET_ID, SYLO_ASSET_ID, XRP_ASSET_ID } from "@trne/utils/porcini-assets";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";
import { provideEthersProvider } from "@trne/utils/withEthersProvider";
import { BigNumber, utils } from "ethers";
import assert from "node:assert";

interface AmountsIn {
	Ok: [number, number];
}

/**
 * Use `feeProxy.callWithFeePreferences` to trigger `emv.call` call via
 * `futurepass.proxyExtrinsic`, and have Futurepass account pays gas in ASTO.
 *
 * Assumes the caller has a valid Futurepass account, ASTO balance to pay for gas and
 * SYLO balance to demonstrate the transfer
 */
withChainApi("porcini", async (api, caller, logger) => {
	/**
	 * 1. Create `futurepass.proxyExtrinsic` call that wraps around `evm.call`
	 */
	const fpAccount = (await api.query.futurepass.holders(caller.address)).unwrapOr(undefined);
	assert(fpAccount);
	logger.info(
		{
			futurepass: {
				holder: caller.address,
				account: fpAccount.toString(),
			},
		},
		"futurepass details"
	);

	const { call: evmCall, estimateGasCost } = await createEVMCall(fpAccount.toString(), api, logger);
	logger.info(
		{
			parameters: {
				futurepass: fpAccount,
				call: evmCall.toJSON(),
			},
		},
		`create a "futurepass.proxyExtrinsic"`
	);
	const futurepassCall = api.tx.futurepass.proxyExtrinsic(fpAccount, evmCall);

	/**
	 * 2. Determine the `maxPayment` in ASTO by estimate the gas cost and use `dex` to get a quote
	 */
	// we need a dummy feeProxy call (with maxPayment=0) to do a proper fee estimation
	const feeProxyCallForEstimation = api.tx.feeProxy.callWithFeePreferences(
		ASTO_ASSET_ID,
		0,
		futurepassCall
	);
	const paymentInfo = await feeProxyCallForEstimation.paymentInfo(caller.address);
	// we need to add the actual estimate cost for the EVM layer call as part of the estimation
	const estimatedFee = BigNumber.from(paymentInfo.partialFee.toString())
		.add(estimateGasCost)
		.toString();

	// query the the `dex` to determine the `maxPayment` you are willing to pay
	const {
		Ok: [amountIn],
	} = (await api.rpc.dex.getAmountsIn(estimatedFee, [
		ASTO_ASSET_ID,
		XRP_ASSET_ID,
	])) as unknown as AmountsIn;

	// allow a buffer to avoid slippage, 5%
	const maxPayment = Number(amountIn * 1.05).toFixed();

	/**
	 * 3. Create and dispatch `feeProxy.callWithFeePreferences` extrinsic
	 */
	logger.info(
		{
			parameters: {
				paymentAsset: ASTO_ASSET_ID,
				maxPayment,
				call: futurepassCall.toJSON(),
			},
		},
		`create a "feeProxy.callWithFeePreferences"`
	);
	const feeProxyCall = api.tx.feeProxy.callWithFeePreferences(
		ASTO_ASSET_ID,
		maxPayment,
		futurepassCall
	);

	logger.info(`dispatch extrinsic from caller="${caller.address}"`);
	const { result, extrinsicId } = await sendExtrinsic(feeProxyCall, caller, { log: logger });
	const [proxyEvent, futurepassEvent, evmLogEvent, aliceTransferEvent] = filterExtrinsicEvents(
		result.events,
		[
			"FeeProxy.CallWithFeePreferences",
			"Futurepass.ProxyExecuted",
			"Evm.Log",
			{ name: "Assets.Transferred", key: "to", data: { value: ALICE, type: "T::AccountId" } },
		]
	);

	logger.info(
		{
			result: {
				extrinsicId,
				blockNumber: result.blockNumber,
				proxyEvent: formatEventData(proxyEvent.event),
				futurepassEvent: formatEventData(futurepassEvent.event),
				evmLogEvent: formatEventData(evmLogEvent.event),
				aliceTransferEvent: formatEventData(aliceTransferEvent.event),
			},
		},
		"receive result"
	);
});

async function createEVMCall(caller: string, api: ApiPromise, logger: Logger) {
	// setup the actual EVM transaction, as interface
	const { provider, wallet } = await provideEthersProvider("porcini");
	const transferToken = getERC20Contract(SYLO_ASSET_ID).connect(wallet);
	const transferAmount = utils.parseUnits("0.1", 18); // 0.1 SYLO
	const transferInput = new utils.Interface(ERC20_ABI).encodeFunctionData("transfer", [
		ALICE,
		transferAmount,
	]);
	const transferEstimate = await transferToken.estimateGas.transfer(ALICE, transferAmount);
	const { maxFeePerGas, estimateGasCost } = await getFeeProxyPricePair(
		provider,
		transferEstimate,
		ASTO_ASSET_ID,
		0.05
	);

	logger.info(
		{
			parameters: {
				source: caller,
				target: transferToken.address,
				input: transferInput,
				gasLimit: transferEstimate.toString(),
				maxFeePerGas: maxFeePerGas.toString(),
			},
		},
		`create an "emv.call"`
	);

	return {
		call: api.tx.evm.call(
			caller,
			transferToken.address,
			transferInput,
			0,
			transferEstimate.toString(),
			maxFeePerGas.toString(),
			0,
			null,
			[]
		),
		estimateGasCost,
	};
}

src/callSystemRemark.ts ​

ts
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { ASTO_ASSET_ID, XRP_ASSET_ID } from "@trne/utils/porcini-assets";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";

interface AmountsIn {
	Ok: [number, number];
}

/**
 * Use `feeProxy.callWithFeePreferences` to trigger `system.remarkWithEvent` call, and pay gas
 * in ASTO token.
 *
 * Assumes the caller has ASTO balance.
 */
withChainApi("porcini", async (api, caller, logger) => {
	/**
	 * 1. Create `system.remarkWithEvent` call
	 */
	// can be any extrinsic, using `system.remarkWithEvent` for simplicity
	logger.info(
		{
			parameters: {
				remark: "Hello World",
			},
		},
		`create a "system.remarkWithEvent"`
	);
	const remarkCall = api.tx.system.remarkWithEvent("Hello World");

	/**
	 * 2. Determine the `maxPayment` in ASTO by estimate the gas cost and use `dex` to get a quote
	 */
	// we need a dummy feeProxy call (with maxPayment=0) to do a proper fee estimation
	const feeProxyCallForEstimation = api.tx.feeProxy.callWithFeePreferences(
		ASTO_ASSET_ID,
		0,
		remarkCall
	);
	const paymentInfo = await feeProxyCallForEstimation.paymentInfo(caller.address);
	const estimatedFee = paymentInfo.partialFee.toString();

	// query the the `dex` to determine the `maxPayment` you are willing to pay
	const {
		Ok: [amountIn],
	} = (await api.rpc.dex.getAmountsIn(estimatedFee, [
		ASTO_ASSET_ID,
		XRP_ASSET_ID,
	])) as unknown as AmountsIn;

	// allow a buffer to avoid slippage, 5%
	const maxPayment = Number(amountIn * 1.05).toFixed();

	/**
	 * 3. Create and dispatch `feeProxy.callWithFeePreferences` extrinsic
	 */
	logger.info(
		{
			parameters: {
				paymentAsset: ASTO_ASSET_ID,
				maxPayment,
				call: remarkCall.toJSON(),
			},
		},
		`create a "feeProxy.callWithFeePreferences"`
	);
	const feeProxyCall = api.tx.feeProxy.callWithFeePreferences(
		ASTO_ASSET_ID,
		maxPayment,
		remarkCall
	);

	logger.info(`dispatch extrinsic from caller="${caller.address}"`);
	const { result, extrinsicId } = await sendExtrinsic(feeProxyCall, caller, { log: logger });
	const [proxyEvent, remarkEvent] = filterExtrinsicEvents(result.events, [
		"FeeProxy.CallWithFeePreferences",
		"System.Remarked",
	]);

	logger.info(
		{
			result: {
				extrinsicId,
				blockNumber: result.blockNumber,
				proxyEvent: formatEventData(proxyEvent.event),
				remarkEvent: formatEventData(remarkEvent.event),
			},
		},
		"receive result"
	);
});

package.json ​

json
{
  "scripts": {
    "call": "tsx --tsconfig ../../../tsconfig.json",
    "call:callSystemRemark": "pnpm call src/callSystemRemark.ts",
    "call:callProxyExtrinsic": "pnpm call src/callProxyExtrinsic.ts",
    "call:callBatchAll": "pnpm call src/callBatchAll.ts",
    "call:callEVMCall": "pnpm call src/callEVMCall.ts",
    "call:callProxyExtrinsicEVMCall": "pnpm call src/callProxyExtrinsicEVMCall.ts"
  }
}
Source: futureversecom/trn-examples, examples/substrate/use-feeProxy (deleted) · commit 44c9e16, 2024-10-31 · Apache-2.0, license · part of Code examples

Curated independently. Source-attributed reference for The Root Network. Not affiliated with Futureverse / TRN Labs.