Skip to content

Commit

Permalink
feat: CP-9258 add new calculation (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
vvava authored Oct 28, 2024
1 parent 996cdd0 commit 773858f
Show file tree
Hide file tree
Showing 14 changed files with 75 additions and 135 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,8 @@ export class AvalancheBridgeAsset extends DAppRequestHandler<BridgeActionParams>
);

const highFeeRate = Number(
(await this.networkFeeService.getNetworkFee(network))?.high.maxFee ??
0
(await this.networkFeeService.getNetworkFee(network))?.high
.maxFeePerGas ?? 0
);

const token = balances.tokens[network.chainId]?.[addressBTC]?.[
Expand Down Expand Up @@ -438,9 +438,10 @@ export class AvalancheBridgeAsset extends DAppRequestHandler<BridgeActionParams>
// then use current instant (high) fee rate as a fallback.
const customGasSettings = pendingAction.displayData.gasSettings;
const maxFeePerGas =
customGasSettings?.maxFeePerGas ?? feeData?.high.maxFee;
customGasSettings?.maxFeePerGas ?? feeData?.high.maxFeePerGas;
const maxPriorityFeePerGas =
customGasSettings?.maxPriorityFeePerGas ?? feeData?.high.maxTip;
customGasSettings?.maxPriorityFeePerGas ??
feeData?.high.maxPriorityFeePerGas;

if (!maxFeePerGas) {
throw new Error('Required option missing: maxFeePerGas');
Expand Down
50 changes: 26 additions & 24 deletions src/background/services/networkFee/NetworkFeeService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('src/background/services/networkFee/NetworkFeeService', () => {
describe('for EVM chains', () => {
const provider = {
getFeeData: jest.fn(),
getNetworkFee: jest.fn(),
};
const evm = {
vmName: NetworkVMType.EVM,
Expand Down Expand Up @@ -54,37 +55,38 @@ describe('src/background/services/networkFee/NetworkFeeService', () => {
expect(serializeToJSON(result)).toEqual(
serializeToJSON({
isFixedFee: false,
low: { maxFee: 3n },
medium: { maxFee: 4n },
high: { maxFee: 5n },
low: { maxFeePerGas: 3n },
medium: { maxFeePerGas: 4n },
high: { maxFeePerGas: 5n },
displayDecimals: 0,
})
);
});

it('provides base information and three rate presets', async () => {
const maxFeePerGas = BigInt(30e9); // 30 gwei

provider.getFeeData.mockResolvedValueOnce({ maxFeePerGas });

const service = new NetworkFeeService({} as any);

expect(await service.getNetworkFee(evm)).toEqual({
displayDecimals: 9, // use Gwei to display amount
baseMaxFee: maxFeePerGas,
low: {
maxFee: maxFeePerGas + BigInt(5e8),
maxTip: BigInt(5e8),
},
medium: {
maxFee: maxFeePerGas + BigInt(2e9),
maxTip: BigInt(2e9),
},
high: {
maxFee: maxFeePerGas + BigInt(3e9),
maxTip: BigInt(3e9),
},
const networkFees = {
isFixedFee: false,
low: { maxFeePerGas: 3n },
medium: { maxFeePerGas: 4n },
high: { maxFeePerGas: 5n },
};
const getNetworkFee = jest.fn().mockResolvedValue(networkFees);

const moduleManager = {
loadModuleByNetwork: jest
.fn()
.mockResolvedValue({ getNetworkFee } as any),
} as any;

const service = new NetworkFeeService(moduleManager);

const fees = await service.getNetworkFee(evm);

expect(moduleManager.loadModuleByNetwork).toHaveBeenCalledWith(evm);

expect(fees).toEqual({
displayDecimals: 9,
...networkFees,
});
});
});
Expand Down
76 changes: 6 additions & 70 deletions src/background/services/networkFee/NetworkFeeService.ts
Original file line number Diff line number Diff line change
@@ -1,87 +1,23 @@
import { NetworkVMType } from '@avalabs/core-chains-sdk';
import { JsonRpcBatchInternal } from '@avalabs/core-wallets-sdk';
import { isSwimmer } from '@src/utils/isSwimmerNetwork';
import { getProviderForNetwork } from '@src/utils/network/getProviderForNetwork';
import { singleton } from 'tsyringe';
import { FeeRate, NetworkFee, TransactionPriority } from './models';
import { NetworkFee } from './models';
import { NetworkWithCaipId } from '../network/models';
import { ModuleManager } from '@src/background/vmModules/ModuleManager';

const EVM_BASE_TIP = BigInt(5e8); // 0.5 Gwei
const EVM_TIP_MODIFIERS: Record<TransactionPriority, bigint> = {
low: 1n, // use the base tip
medium: 4n, // 4x base
high: 6n, // 6x base
};

@singleton()
export class NetworkFeeService {
constructor(private moduleManager: ModuleManager) {}

async getNetworkFee(network: NetworkWithCaipId): Promise<NetworkFee | null> {
if (network.vmName === NetworkVMType.EVM) {
const provider = getProviderForNetwork(network);
return this.getEip1559NetworkFeeRates(
network,
provider as JsonRpcBatchInternal
);
} else if (network.vmName === NetworkVMType.BITCOIN) {
const module = await this.moduleManager.loadModuleByNetwork(network);
const { low, medium, high, isFixedFee } = await module.getNetworkFee(
network
);

return {
isFixedFee,
low: {
maxFee: low.maxFeePerGas,
},
medium: {
maxFee: medium.maxFeePerGas,
},
high: {
maxFee: high.maxFeePerGas,
},
displayDecimals: 0, // display btc fees in satoshi
};
}

return null;
}

private async getEip1559NetworkFeeRates(
network: NetworkWithCaipId,
provider: JsonRpcBatchInternal
): Promise<NetworkFee> {
const { maxFeePerGas: baseMaxFee } = await (
provider as JsonRpcBatchInternal
).getFeeData();

if (baseMaxFee === null) {
throw new Error('Fetching fee data failed');
}

return {
displayDecimals: 9, // use Gwei to display amount
baseMaxFee,
low: this.getEVMFeeForPriority(baseMaxFee, EVM_BASE_TIP, 'low'),
medium: this.getEVMFeeForPriority(baseMaxFee, EVM_BASE_TIP, 'medium'),
high: this.getEVMFeeForPriority(baseMaxFee, EVM_BASE_TIP, 'high'),
isFixedFee: isSwimmer(network) ? true : false,
};
}

private getEVMFeeForPriority(
baseMaxFee: bigint,
maxPriorityFee: bigint,
priority: TransactionPriority
): FeeRate {
const maxTip = maxPriorityFee * EVM_TIP_MODIFIERS[priority];
const maxFee = baseMaxFee + maxTip;
const module = await this.moduleManager.loadModuleByNetwork(network);
const displayDecimals = network.vmName === NetworkVMType.BITCOIN ? 0 : 9;

const fees = await module.getNetworkFee(network);
return {
maxFee,
maxTip,
...fees,
displayDecimals: fees.displayDecimals || displayDecimals,
};
}

Expand Down
6 changes: 3 additions & 3 deletions src/background/services/networkFee/models.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
export type FeeRate = {
maxFee: bigint;
maxTip?: bigint;
maxFeePerGas: bigint;
maxPriorityFeePerGas?: bigint;
};

export interface NetworkFee {
displayDecimals: number;
baseMaxFee?: bigint;
baseFee?: bigint;
low: FeeRate;
medium: FeeRate;
high: FeeRate;
Expand Down
3 changes: 1 addition & 2 deletions src/background/services/secrets/SecretsService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ describe('src/background/services/secrets/SecretsService.ts', () => {
const result = await secretsService.getAccountSecrets(
activeAccountData
);
console.log('result: ', result);

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { ...rest } = secrets.wallets[0];
expect(result).toEqual({
Expand Down Expand Up @@ -1534,7 +1534,6 @@ describe('src/background/services/secrets/SecretsService.ts', () => {
},
isMainnet
);
console.log('result: ', result);

expect(result).toStrictEqual({
account: {
Expand Down
34 changes: 17 additions & 17 deletions src/components/common/CustomFees.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export function CustomFees({

// update
const updatedFees = calculateGasAndFees({
maxFeePerGas: rate.maxFee,
maxFeePerGas: rate.maxFeePerGas,
tokenPrice,
tokenDecimals: network?.networkToken.decimals,
gasLimit,
Expand All @@ -217,8 +217,8 @@ export function CustomFees({
// call cb with limit and gas
onChange({
customGasLimit: customGasLimit,
maxFeePerGas: rate.maxFee,
maxPriorityFeePerGas: rate.maxTip,
maxFeePerGas: rate.maxFeePerGas,
maxPriorityFeePerGas: rate.maxPriorityFeePerGas,
feeType: modifier,
});
return;
Expand All @@ -227,8 +227,8 @@ export function CustomFees({
// call cb with limit and gas
onChange({
customGasLimit: customGasLimit,
maxFeePerGas: rate.maxFee,
maxPriorityFeePerGas: rate.maxTip,
maxFeePerGas: rate.maxFeePerGas,
maxPriorityFeePerGas: rate.maxPriorityFeePerGas,
feeType: modifier,
});
},
Expand All @@ -245,7 +245,7 @@ export function CustomFees({
const getFeeRateForCustomGasPrice = useCallback(
(customFeePerGas: string, fee: NetworkFee): FeeRate => {
const maxFee = parseUnits(customFeePerGas, fee.displayDecimals);
const { baseMaxFee } = fee;
const { baseFee: baseMaxFee } = fee;
// When the user manually sets a max. fee, we also use it to calculate
// the max. priority fee (tip) for EVM transactions.
// If the custom max. fee is greater than the current base fee,
Expand All @@ -254,8 +254,8 @@ export function CustomFees({
baseMaxFee && maxFee > baseMaxFee ? maxFee - baseMaxFee : undefined;

return {
maxFee,
maxTip,
maxFeePerGas: maxFee,
maxPriorityFeePerGas: maxTip,
};
},
[]
Expand Down Expand Up @@ -362,7 +362,7 @@ export function CustomFees({
<Typography variant="caption" sx={{ fontWeight: 'semibold' }}>
{getGasFeeToDisplay(
getUpToTwoDecimals(
networkFee.low.maxFee,
networkFee.low.maxFeePerGas,
networkFee.displayDecimals
),
networkFee
Expand All @@ -387,7 +387,7 @@ export function CustomFees({
<Typography variant="caption" sx={{ fontWeight: 'semibold' }}>
{getGasFeeToDisplay(
getUpToTwoDecimals(
networkFee.medium.maxFee,
networkFee.medium.maxFeePerGas,
networkFee.displayDecimals
),
networkFee
Expand All @@ -412,7 +412,7 @@ export function CustomFees({
<Typography variant="caption" sx={{ fontWeight: 'semibold' }}>
{getGasFeeToDisplay(
getUpToTwoDecimals(
networkFee.high.maxFee,
networkFee.high.maxFeePerGas,
networkFee.displayDecimals
),
networkFee
Expand Down Expand Up @@ -441,7 +441,7 @@ export function CustomFees({
type="number"
value={getGasFeeToDisplay(
getUpToTwoDecimals(
customFee?.maxFee ?? 0n,
customFee?.maxFeePerGas ?? 0n,
networkFee.displayDecimals
),
networkFee
Expand Down Expand Up @@ -521,21 +521,21 @@ export function CustomFees({
open={Boolean(
network?.vmName === NetworkVMType.EVM &&
showEditGasLimit &&
customFee?.maxFee
customFee?.maxFeePerGas
)}
>
<CustomGasSettings
isLimitReadonly={isLimitReadonly}
feeDisplayDecimals={networkFee.displayDecimals}
gasLimit={gasLimit}
maxFeePerGas={customFee?.maxFee || 0n}
maxPriorityFeePerGas={customFee?.maxTip || 0n}
maxFeePerGas={customFee?.maxFeePerGas || 0n}
maxPriorityFeePerGas={customFee?.maxPriorityFeePerGas || 0n}
onCancel={() => setShowEditGasLimit(false)}
onSave={(data) => {
setCustomGasLimit(data.customGasLimit);
setCustomFee({
maxFee: data.maxFeePerGas,
maxTip: data.maxPriorityFeePerGas,
maxFeePerGas: data.maxFeePerGas,
maxPriorityFeePerGas: data.maxPriorityFeePerGas,
});
setSelectedFee(GasFeeModifier.CUSTOM);
setShowEditGasLimit(false);
Expand Down
8 changes: 5 additions & 3 deletions src/pages/ApproveAction/hooks/useFeeCustomizer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export const useFeeCustomizer = ({
getInitialFeeRate(signingData)
);
const [maxPriorityFeePerGas, setMaxPriorityFeePerGas] = useState(
networkFee?.low?.maxTip
networkFee?.low?.maxPriorityFeePerGas
);

useEffect(() => {
Expand All @@ -183,8 +183,10 @@ export const useFeeCustomizer = ({
}

// Initialize fee config with default values if they are not set at all
setMaxFeePerGas((previous) => previous ?? networkFee.low.maxFee);
setMaxPriorityFeePerGas((previous) => previous ?? networkFee.low.maxTip);
setMaxFeePerGas((previous) => previous ?? networkFee.low.maxFeePerGas);
setMaxPriorityFeePerGas(
(previous) => previous ?? networkFee.low.maxPriorityFeePerGas
);
}, [networkFee, isFeeSelectorEnabled]);

const setCustomFee = useCallback(
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Bridge/hooks/useBtcBridge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ describe('src/pages/Bridge/hooks/useBtcBridge', () => {
jest.mocked(useNetworkFeeContext).mockReturnValue({
networkFee: {
high: {
maxFee: highFee,
maxFeePerGas: highFee,
},
low: {
maxFee: lowFee,
maxFeePerGas: lowFee,
},
displayDecimals: 8,
},
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Bridge/hooks/useBtcBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function useBtcBridge(amountInBtc: Big): BridgeAdapter {
// defaulting to cheaper preset makes testing easier.
const preset: TransactionPriority = isDeveloperMode ? 'low' : 'high';

return Number(currentFeeInfo[preset].maxFee);
return Number(currentFeeInfo[preset].maxFeePerGas);
}, [currentFeeInfo, isDeveloperMode]);

// Calculate the maximum bridgable BTC amount whwnever
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Bridge/hooks/useEthBridge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,10 @@ describe('src/pages/Bridge/hooks/useEthBridge', () => {
jest.mocked(useNetworkFeeContext).mockReturnValue({
networkFee: {
high: {
maxFee: highFee,
maxFeePerGas: highFee,
},
low: {
maxFee: lowFee,
maxFeePerGas: lowFee,
},
displayDecimals: 8,
},
Expand Down
Loading

0 comments on commit 773858f

Please sign in to comment.