@avalabs/avalanchejs
Version:
Avalanche Platform JS Library
95 lines (86 loc) • 2.93 kB
text/typescript
import {
BigIntPr,
Id,
TransferOutput,
TransferableOutput,
} from '../../../../serializable';
import type { Context } from '../../../context';
import type { SpendReducerFunction } from './types';
/**
* Determines if the fee can be covered by the excess AVAX.
*
* @returns {boolean} - Whether the excess AVAX exceeds the fee. `true` greater than the fee, `false` if equal.
* @throws {Error} - If the excess AVAX is less than the required fee.
*/
const canPayFeeAndNeedsChange = (
excessAVAX: bigint,
requiredFee: bigint,
context: Context,
): boolean => {
// Not enough funds to pay the fee.
// NOTE: Time locked UTXOs can not be used to pay fees.
if (excessAVAX < requiredFee) {
throw new Error(
`Insufficient funds: provided UTXOs need ${
requiredFee - excessAVAX
} more unlocked nAVAX (asset id: ${context.avaxAssetID}) to cover fee.`,
);
}
// No need to add a change to change output.
// Just burn the fee.
if (excessAVAX === requiredFee) {
return false;
}
return true;
};
export const handleFeeAndChange: SpendReducerFunction = (
state,
spendHelper,
context,
) => {
const requiredFee = spendHelper.calculateFee();
// Checks for an existing change output that is for the AVAX asset assigned to the change owner.
const hasExistingChangeOutput: boolean = spendHelper.hasChangeOutput(
context.avaxAssetID,
state.changeOutputOwners,
);
if (canPayFeeAndNeedsChange(state.excessAVAX, requiredFee, context)) {
if (hasExistingChangeOutput) {
// Excess exceeds fee, return the change.
// This output will get consolidated with the existing output.
spendHelper.addChangeOutput(
new TransferableOutput(
Id.fromString(context.avaxAssetID),
new TransferOutput(
new BigIntPr(state.excessAVAX - requiredFee),
state.changeOutputOwners,
),
),
);
} else {
// Calculate the fee with a temporary output complexity
// as if we added the change output.
const requiredFeeWithChangeOutput = spendHelper.calculateFee(
new TransferableOutput(
Id.fromString(context.avaxAssetID),
new TransferOutput(new BigIntPr(0n), state.changeOutputOwners),
),
);
// If the excess AVAX is greater than the new fee, add a change output.
// Otherwise, ignore and burn the excess because it can't be returned
// (ie there is no point in adding a change output if you can't afford to add it).
if (state.excessAVAX > requiredFeeWithChangeOutput) {
spendHelper.addChangeOutput(
new TransferableOutput(
Id.fromString(context.avaxAssetID),
new TransferOutput(
new BigIntPr(state.excessAVAX - requiredFeeWithChangeOutput),
state.changeOutputOwners,
),
),
);
}
}
}
return state;
};