Skip to content

Commit

Permalink
feat: adjust validateBurnedAmount params
Browse files Browse the repository at this point in the history
  • Loading branch information
ruijialin-avalabs committed Oct 9, 2024
1 parent 876c744 commit 4cdc656
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 78 deletions.
52 changes: 33 additions & 19 deletions src/utils/validateAvaxBurnedAmountEtna.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,10 @@ import {
} from '../vms/pvm';
import { TransferableOutput } from '../serializable';
import { nodeId } from '../fixtures/common';
import { feeState as testFeeState } from '../fixtures/pvm';
import { testSubnetId } from '../fixtures/transactions';
import { blsPublicKeyBytes, blsSignatureBytes } from '../fixtures/primitives';
import { validateAvaxBurnedAmountEtna } from './validateAvaxBurnedAmountEtna';

const incorrectBurnedAmount = 1n;
const correctBurnedAmount = 1000000n;

const utxoMock = new Utxo(
utxoId(),
Id.fromString(testContext.avaxAssetID),
Expand Down Expand Up @@ -91,14 +87,12 @@ describe('validateAvaxBurnedAmountEtna', () => {
try {
validateAvaxBurnedAmountEtna({
unsignedTx,
context: testContext,
burnedAmount: correctBurnedAmount,
feeState: testFeeState(),
burnedAmount: 1000000n,
baseFee: 1n,
feeTolerance: 20,
});
} catch (error) {
expect((error as Error).message).toEqual(
'Unsupported transaction type.',
);
expect((error as Error).message).toEqual('tx type is not supported');
}
});
});
Expand All @@ -113,6 +107,8 @@ describe('validateAvaxBurnedAmountEtna', () => {
[utxoMock],
[outputMock],
),
baseFee: 3830000n,
burnedAmount: 3840000n,
},
{
name: 'export from P',
Expand All @@ -123,6 +119,8 @@ describe('validateAvaxBurnedAmountEtna', () => {
[utxoMock],
[outputMock],
),
baseFee: 4190000n,
burnedAmount: 4390000n,
},
{
name: 'import to P',
Expand All @@ -133,6 +131,8 @@ describe('validateAvaxBurnedAmountEtna', () => {
[testAddress2],
[testAddress1],
),
baseFee: 3380000n,
burnedAmount: 3980000n,
},
{
name: 'create subnet',
Expand All @@ -142,6 +142,8 @@ describe('validateAvaxBurnedAmountEtna', () => {
[testAddress1],
[testAddress1],
),
baseFee: 3430000n,
burnedAmount: 3930000n,
},
{
name: 'create blockchain',
Expand All @@ -156,6 +158,8 @@ describe('validateAvaxBurnedAmountEtna', () => {
{},
[0],
),
baseFee: 5340000n,
burnedAmount: 5660000n,
},
{
name: 'add subnet validator',
Expand All @@ -170,6 +174,8 @@ describe('validateAvaxBurnedAmountEtna', () => {
'subnet',
[0],
),
baseFee: 4660000n,
burnedAmount: 4960000n,
},
{
name: 'remove subnet validator',
Expand All @@ -181,6 +187,8 @@ describe('validateAvaxBurnedAmountEtna', () => {
Id.fromHex(testSubnetId).toString(),
[0],
),
baseFee: 4420000n,
burnedAmount: 4420000n,
},
{
name: 'add permissionless validator (subnet)',
Expand All @@ -202,6 +210,8 @@ describe('validateAvaxBurnedAmountEtna', () => {
blsPublicKeyBytes(),
blsSignatureBytes(),
),
baseFee: 6570000n,
burnedAmount: 7570000n,
},
{
name: 'add permissionless delegator (subnet)',
Expand All @@ -219,6 +229,8 @@ describe('validateAvaxBurnedAmountEtna', () => {
1,
0n,
),
baseFee: 4850000n,
burnedAmount: 4900000n,
},
{
name: 'transfer subnet ownership',
Expand All @@ -230,35 +242,37 @@ describe('validateAvaxBurnedAmountEtna', () => {
[0, 2],
[testAddress2],
),
baseFee: 5300000n,
burnedAmount: 5900000n,
},
];

describe.each(testData)('$name', ({ unsignedTx }) => {
describe.each(testData)('$name', ({ unsignedTx, baseFee, burnedAmount }) => {
it('returns true if burned amount is correct', () => {
const result = validateAvaxBurnedAmountEtna({
unsignedTx,
context: testContext,
burnedAmount: correctBurnedAmount,
feeState: testFeeState(),
burnedAmount,
baseFee,
feeTolerance: 20,
});

expect(result).toStrictEqual({
isValid: true,
txFee: correctBurnedAmount,
txFee: burnedAmount,
});
});

it('returns false if burned amount is not correct', () => {
const result = validateAvaxBurnedAmountEtna({
unsignedTx,
context: testContext,
burnedAmount: incorrectBurnedAmount,
feeState: { ...testFeeState(), price: 10_000n },
burnedAmount: burnedAmount * 30n,
feeTolerance: 20,
baseFee,
});

expect(result).toStrictEqual({
isValid: false,
txFee: incorrectBurnedAmount,
txFee: burnedAmount * 30n,
});
});
});
Expand Down
28 changes: 13 additions & 15 deletions src/utils/validateAvaxBurnedAmountEtna.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { Context } from '../vms/context/model';
import {
isAddPermissionlessDelegatorTx,
isAddPermissionlessValidatorTx,
Expand All @@ -12,29 +11,28 @@ import {
isTransferSubnetOwnershipTx,
} from '../serializable/pvm';
import type { UnsignedTx } from '../vms/common';
import type { FeeState } from '../vms/pvm';
import { calculateFee } from '../vms/pvm/txs/fee/calculator';

export const validateAvaxBurnedAmountEtna = ({
unsignedTx,
context,
burnedAmount,
feeState,
baseFee,
feeTolerance,
}: {
unsignedTx: UnsignedTx;
context: Context;
burnedAmount: bigint;
feeState: FeeState;
baseFee: bigint; // pvm dynamic fee caculator: @see https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/txs/fee/dynamic_calculator.go
feeTolerance: number; // tolerance percentage range where the burned amount is considered valid. e.g.: with FeeTolerance = 20% -> (expectedFee <= burnedAmount <= expectedFee * 1.2)
}): { isValid: boolean; txFee: bigint } => {
const tx = unsignedTx.getTx();

const expectedFee = calculateFee(
unsignedTx.getTx(),
context.platformFeeConfig.weights,
feeState.price < context.platformFeeConfig.minPrice
? context.platformFeeConfig.minPrice
: feeState.price,
);
const feeToleranceInt = Math.floor(feeTolerance);

if (feeToleranceInt < 1 || feeToleranceInt > 100) {
throw new Error('feeTolerance must be [1,100]');
}

const min = baseFee;
const max = (baseFee * (100n + BigInt(feeToleranceInt))) / 100n;

if (
isPvmBaseTx(tx) ||
Expand All @@ -49,7 +47,7 @@ export const validateAvaxBurnedAmountEtna = ({
isTransferSubnetOwnershipTx(tx)
) {
return {
isValid: burnedAmount >= expectedFee,
isValid: burnedAmount >= min && burnedAmount <= max,
txFee: burnedAmount,
};
}
Expand Down
24 changes: 13 additions & 11 deletions src/utils/validateBurnedAmount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import type { EVMTx } from '../serializable/evm';
import { isImportExportTx as isEvmImportExportTx } from '../serializable/evm';
import { getBurnedAmountByTx } from './getBurnedAmountByTx';
import type { AvaxTx } from '../serializable/avax';
import type { FeeState } from '../vms/pvm';
import { validateEvmBurnedAmount } from './validateEvmBurnedAmount';
import type { GetUpgradesInfoResponse } from '../info/model';
import { isEtnaEnabled } from './isEtnaEnabled';
Expand Down Expand Up @@ -39,22 +38,25 @@ const isPreEtnaTx = (tx: Transaction) => {
);
};

/**
* baseFee:
* - evm fee: fetched from the network and converted into nAvax (https://docs.avax.network/quickstart/transaction-fees#c-chain-fees)
* - pvm dynamic fee caculator: @see https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/txs/fee/dynamic_calculator.go
*/
export const validateBurnedAmount = ({
unsignedTx,
context,
feeState,
upgradesInfo,
burnedAmount,
evmBaseFee,
evmFeeTolerance,
baseFee,
feeTolerance,
}: {
unsignedTx: UnsignedTx;
context: Context;
feeState: FeeState;
upgradesInfo: GetUpgradesInfoResponse;
burnedAmount?: bigint;
evmBaseFee?: bigint; // fetched from the network and converted into nAvax (https://docs.avax.network/quickstart/transaction-fees#c-chain-fees)
evmFeeTolerance?: number; // tolerance percentage range where the burned amount is considered valid. e.g.: with evmFeeTolerance = 20% -> (evmBaseFee * 0.8 <= burnedAmount <= evmBaseFee * 1.2)
baseFee: bigint;
feeTolerance: number; // tolerance percentage range where the burned amount is considered valid.
}): { isValid: boolean; txFee: bigint } => {
const tx = unsignedTx.getTx();
const burned = burnedAmount ?? _getBurnedAmount(tx, context);
Expand All @@ -63,16 +65,16 @@ export const validateBurnedAmount = ({
return validateEvmBurnedAmount({
unsignedTx,
burnedAmount: burned,
evmBaseFee,
evmFeeTolerance,
baseFee,
feeTolerance,
});
}
if (isEtnaEnabled(upgradesInfo) || !isPreEtnaTx(tx)) {
return validateAvaxBurnedAmountEtna({
unsignedTx,
context,
baseFee,
burnedAmount: burned,
feeState,
feeTolerance,
});
}
return validateAvaxBurnedAmountPreEtna({
Expand Down
46 changes: 23 additions & 23 deletions src/utils/validateEvmBurnedAmount.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,39 +30,39 @@ describe('validateEvmBurnedAmount', () => {
[testAddress1],
1n,
);
it('throws if evmFeeTolerance is incorrect', () => {
it('throws if feeTolerance is incorrect', () => {
expect(() =>
validateEvmBurnedAmount({
unsignedTx,
burnedAmount: (280750n * 75n) / 100n, // 25% lower,
evmBaseFee: 25n,
evmFeeTolerance: 0.5,
baseFee: 25n,
feeTolerance: 0.5,
}),
).toThrowError('evmFeeTolerance must be [1,100]');
).toThrowError('feeTolerance must be [1,100]');

expect(() =>
validateEvmBurnedAmount({
unsignedTx,
burnedAmount: (280750n * 75n) / 100n, // 25% lower,
evmBaseFee: 25n,
evmFeeTolerance: 101,
baseFee: 25n,
feeTolerance: 101,
}),
).toThrowError('evmFeeTolerance must be [1,100]');
).toThrowError('feeTolerance must be [1,100]');
});

it('returns true if burned amount is in the tolerance range', () => {
const resultLower = validateEvmBurnedAmount({
unsignedTx,
burnedAmount: (280750n * 75n) / 100n, // 25% lower
evmBaseFee: 25n,
evmFeeTolerance: 50.9,
baseFee: 25n,
feeTolerance: 50.9,
});

const resultHigher = validateEvmBurnedAmount({
unsignedTx,
burnedAmount: (280750n * 125n) / 100n, // 25% higher
evmBaseFee: 25n,
evmFeeTolerance: 50.9,
baseFee: 25n,
feeTolerance: 50.9,
});

expect(resultLower).toStrictEqual({
Expand All @@ -79,15 +79,15 @@ describe('validateEvmBurnedAmount', () => {
const resultLower = validateEvmBurnedAmount({
unsignedTx,
burnedAmount: (280750n * 49n) / 100n, // 51% lower
evmBaseFee: 25n,
evmFeeTolerance: 50.9,
baseFee: 25n,
feeTolerance: 50.9,
});

const resultHigher = validateEvmBurnedAmount({
unsignedTx,
burnedAmount: (280750n * 151n) / 100n, // 51% higher
evmBaseFee: 25n,
evmFeeTolerance: 50.9,
baseFee: 25n,
feeTolerance: 50.9,
});

expect(resultLower).toStrictEqual({
Expand Down Expand Up @@ -115,15 +115,15 @@ describe('validateEvmBurnedAmount', () => {
const resultLower = validateEvmBurnedAmount({
unsignedTx,
burnedAmount: (280750n * 75n) / 100n, // 25% lower
evmBaseFee: 25n,
evmFeeTolerance: 50.9,
baseFee: 25n,
feeTolerance: 50.9,
});

const resultHigher = validateEvmBurnedAmount({
unsignedTx,
burnedAmount: (280750n * 125n) / 100n, // 25% higher
evmBaseFee: 25n,
evmFeeTolerance: 50.9,
baseFee: 25n,
feeTolerance: 50.9,
});

expect(resultLower).toStrictEqual({
Expand All @@ -140,15 +140,15 @@ describe('validateEvmBurnedAmount', () => {
const resultLower = validateEvmBurnedAmount({
unsignedTx,
burnedAmount: (280750n * 49n) / 100n, // 51% lower
evmBaseFee: 25n,
evmFeeTolerance: 50.9,
baseFee: 25n,
feeTolerance: 50.9,
});

const resultHigher = validateEvmBurnedAmount({
unsignedTx,
burnedAmount: (280750n * 151n) / 100n, // 51% higher
evmBaseFee: 25n,
evmFeeTolerance: 50.9,
baseFee: 25n,
feeTolerance: 50.9,
});

expect(resultLower).toStrictEqual({
Expand Down
Loading

0 comments on commit 4cdc656

Please sign in to comment.