diff --git a/modules/sdk-coin-flrp/src/flrp.ts b/modules/sdk-coin-flrp/src/flrp.ts index 870249eb5e..e0dcb8d426 100644 --- a/modules/sdk-coin-flrp/src/flrp.ts +++ b/modules/sdk-coin-flrp/src/flrp.ts @@ -451,6 +451,24 @@ export class Flrp extends BaseCoin { }; } + // For a P→C export (ExportInP), the exported UTXO amount includes the C-chain + // import fee that will be deducted when ImportInC is confirmed. Subtract the + // minimum import-to-C fee so the displayed amount reflects the expected net + // C-chain receipt, consistent with how ExportInC subtracts minImportToPFee. + if (!tx.isTransactionForCChain && explanation.type === TransactionType.Export) { + const minImportToCFee = BigInt((this._staticsCoin.network as FlareNetwork).minImportToCFee); + const adjustedOutputs = explanation.outputs.map((o) => ({ + ...o, + amount: (BigInt(o.amount) - minImportToCFee).toString(), + })); + const adjustedOutputAmount = (BigInt(explanation.outputAmount) - minImportToCFee).toString(); + return { + ...explanation, + outputs: adjustedOutputs, + outputAmount: adjustedOutputAmount, + }; + } + return explanation; } catch (e) { throw new Error(`Invalid transaction: ${e.message}`); diff --git a/modules/sdk-coin-flrp/test/unit/flrp.ts b/modules/sdk-coin-flrp/test/unit/flrp.ts index 132084c09a..fc18c593ba 100644 --- a/modules/sdk-coin-flrp/test/unit/flrp.ts +++ b/modules/sdk-coin-flrp/test/unit/flrp.ts @@ -313,6 +313,30 @@ describe('Flrp test cases', function () { signedExplain.outputs[0].amount.should.equal(expectedAdjustedAmount); }); + it('should subtract minImportToCFee from ExportInP outputAmount to show expected net C-chain receipt', async () => { + // The ExportInP UTXO amount is the gross amount going to C-chain, which includes + // the C-chain import fee that will be deducted when ImportInC is confirmed. We + // subtract minImportToCFee to show the minimum net amount the user will receive + // on C-chain, preventing the confirmation UI from showing a higher-than-actual + // amount and causing the display to appear as "- TFLR" instead of the correct amount. + const minImportToCFee = BigInt('2850000'); // from FlarePTestnet.minImportToCFee + const grossOutputAmount = BigInt(EXPORT_IN_P.amount); // 55000000 (includes import fee) + const expectedAdjustedAmount = (grossOutputAmount - minImportToCFee).toString(); + + // Should work for both half-signed and fully-signed ExportInP hex + const halfSignedExplain = await basecoin.explainTransaction({ + halfSigned: { txHex: EXPORT_IN_P.halfSigntxHex }, + }); + halfSignedExplain.outputAmount.should.equal(expectedAdjustedAmount); + halfSignedExplain.outputs.should.be.an.Array(); + halfSignedExplain.outputs.length.should.equal(1); + halfSignedExplain.outputs[0].amount.should.equal(expectedAdjustedAmount); + + const signedExplain = await basecoin.explainTransaction({ txHex: EXPORT_IN_P.fullSigntxHex }); + signedExplain.outputAmount.should.equal(expectedAdjustedAmount); + signedExplain.outputs[0].amount.should.equal(expectedAdjustedAmount); + }); + it('should fail when transaction hex is not provided', async () => { await basecoin.explainTransaction({}).should.be.rejectedWith('missing transaction hex'); }); diff --git a/modules/statics/src/networks.ts b/modules/statics/src/networks.ts index 5f92117deb..1e7da18297 100644 --- a/modules/statics/src/networks.ts +++ b/modules/statics/src/networks.ts @@ -18,6 +18,7 @@ export interface FlareNetwork extends BaseNetwork { vm?: string; txFee: string; minImportToPFee: string; + minImportToCFee: string; maxImportFee: string; createSubnetTx?: string; createChainTx?: string; @@ -2318,7 +2319,8 @@ export class FlareP extends Mainnet implements FlareNetwork { hrp = 'flare'; alias = 'P'; vm = 'platformvm'; - minImportToPFee = '1261000'; // 0.1261 FLR + minImportToPFee = '1261000'; // minimum PVM import-to-P fee in nFLR + minImportToCFee = '2850000'; // minimum EVM import-to-C fee in nFLR (250 nFLR/gas * ~11400 gas) txFee = '200000'; // FLR P-chain import requires higher fee than base txFee baseTxFee = '1000000'; maxImportFee = '10000000'; // defaults @@ -2354,7 +2356,8 @@ export class FlarePTestnet extends Testnet implements FlareNetwork { alias = 'P'; assetId = 'fxMAKpBQQpFedrUhWMsDYfCUJxdUw4mneTczKBzNg3rc2JUub'; vm = 'platformvm'; - minImportToPFee = '1261000'; // 0.1261 FLR + minImportToPFee = '1261000'; // minimum PVM import-to-P fee in nFLR + minImportToCFee = '2850000'; // minimum EVM import-to-C fee in nFLR (250 nFLR/gas * ~11400 gas) txFee = '200000'; // FLR P-chain import requires higher fee than base txFee baseTxFee = '1000000'; maxImportFee = '10000000'; // defaults