From df72665c7c21e0d2a08f2bfacd8b3c14edbf958f Mon Sep 17 00:00:00 2001 From: Fumiko Date: Wed, 29 Jan 2025 10:52:48 -0700 Subject: [PATCH 1/8] feat: (pvm) Replace old builder with etna builder --- examples/p-chain/etna/base.ts | 2 +- examples/p-chain/etna/convertSubnetToL1.ts | 2 +- examples/p-chain/etna/createChain.ts | 2 +- examples/p-chain/etna/createSubnet.ts | 2 +- examples/p-chain/etna/delegate.ts | 2 +- examples/p-chain/etna/export.ts | 2 +- examples/p-chain/etna/import.ts | 2 +- examples/p-chain/etna/increaseBalanceTx.ts | 2 +- examples/p-chain/etna/validate.ts | 2 +- src/vms/pvm/builder.test.ts | 829 ------------------- src/vms/pvm/builder.ts | 888 --------------------- src/vms/pvm/index.ts | 6 +- 12 files changed, 13 insertions(+), 1728 deletions(-) delete mode 100644 src/vms/pvm/builder.test.ts delete mode 100644 src/vms/pvm/builder.ts diff --git a/examples/p-chain/etna/base.ts b/examples/p-chain/etna/base.ts index a0a5989b2..20bae3c84 100644 --- a/examples/p-chain/etna/base.ts +++ b/examples/p-chain/etna/base.ts @@ -14,7 +14,7 @@ const main = async () => { const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] }); - const tx = pvm.e.newBaseTx( + const tx = pvm.newBaseTx( { feeState, fromAddressesBytes: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], diff --git a/examples/p-chain/etna/convertSubnetToL1.ts b/examples/p-chain/etna/convertSubnetToL1.ts index c1d61eb03..0d51cf4b1 100644 --- a/examples/p-chain/etna/convertSubnetToL1.ts +++ b/examples/p-chain/etna/convertSubnetToL1.ts @@ -56,7 +56,7 @@ const convertSubnetToL1TxExample = async () => { pChainOwner, ); - const tx = pvm.e.newConvertSubnetToL1Tx( + const tx = pvm.newConvertSubnetToL1Tx( { feeState, fromAddressesBytes: [testPAddr], diff --git a/examples/p-chain/etna/createChain.ts b/examples/p-chain/etna/createChain.ts index a0164888d..e3f0c87c3 100644 --- a/examples/p-chain/etna/createChain.ts +++ b/examples/p-chain/etna/createChain.ts @@ -24,7 +24,7 @@ const createChainTxExample = async () => { const vmId = 'rWhpuQPF1kb72esV2momhMuTYGkEb1oL29pt2EBXWmSy4kxnT'; // platform vmId const subnetId = ''; // subnetId from createSubnetTx - const tx = pvm.e.newCreateChainTx( + const tx = pvm.newCreateChainTx( { feeState, fromAddressesBytes: [testPAddr], diff --git a/examples/p-chain/etna/createSubnet.ts b/examples/p-chain/etna/createSubnet.ts index c1fe5b9a5..0d93114a0 100644 --- a/examples/p-chain/etna/createSubnet.ts +++ b/examples/p-chain/etna/createSubnet.ts @@ -11,7 +11,7 @@ const createSubnetTxExample = async () => { const testPAddr = utils.bech32ToBytes(P_CHAIN_ADDRESS); - const tx = pvm.e.newCreateSubnetTx( + const tx = pvm.newCreateSubnetTx( { feeState, fromAddressesBytes: [testPAddr], diff --git a/examples/p-chain/etna/delegate.ts b/examples/p-chain/etna/delegate.ts index eb330b632..8190d5e48 100644 --- a/examples/p-chain/etna/delegate.ts +++ b/examples/p-chain/etna/delegate.ts @@ -23,7 +23,7 @@ const main = async () => { // TODO: Get this from an argument. const nodeId = 'NodeID-MqgFXT8JhorbEW2LpTDGePBBhv55SSp3M'; - const tx = pvm.e.newAddPermissionlessDelegatorTx( + const tx = pvm.newAddPermissionlessDelegatorTx( { end, feeState, diff --git a/examples/p-chain/etna/export.ts b/examples/p-chain/etna/export.ts index 2c5fb6523..cbdf5dc79 100644 --- a/examples/p-chain/etna/export.ts +++ b/examples/p-chain/etna/export.ts @@ -14,7 +14,7 @@ const main = async () => { addresses: [P_CHAIN_ADDRESS], }); - const exportTx = pvm.e.newExportTx( + const exportTx = pvm.newExportTx( { destinationChainId: context.xBlockchainID, feeState, diff --git a/examples/p-chain/etna/import.ts b/examples/p-chain/etna/import.ts index b7329edd0..8bb9696c5 100644 --- a/examples/p-chain/etna/import.ts +++ b/examples/p-chain/etna/import.ts @@ -13,7 +13,7 @@ const main = async () => { addresses: [P_CHAIN_ADDRESS], }); - const importTx = pvm.e.newImportTx( + const importTx = pvm.newImportTx( { feeState, fromAddressesBytes: [utils.bech32ToBytes(X_CHAIN_ADDRESS)], diff --git a/examples/p-chain/etna/increaseBalanceTx.ts b/examples/p-chain/etna/increaseBalanceTx.ts index bf0fc58de..9eb6e2dda 100644 --- a/examples/p-chain/etna/increaseBalanceTx.ts +++ b/examples/p-chain/etna/increaseBalanceTx.ts @@ -13,7 +13,7 @@ const increaseBalanceTx = async () => { const testPAddr = utils.bech32ToBytes(P_CHAIN_ADDRESS); - const unsignedTx = pvm.e.newIncreaseL1ValidatorBalanceTx( + const unsignedTx = pvm.newIncreaseL1ValidatorBalanceTx( { balance: BigInt(BALANCE_AVAX * 1e9), feeState, diff --git a/examples/p-chain/etna/validate.ts b/examples/p-chain/etna/validate.ts index 041e0e6b7..bf6d4f741 100644 --- a/examples/p-chain/etna/validate.ts +++ b/examples/p-chain/etna/validate.ts @@ -33,7 +33,7 @@ const main = async () => { const signature = utils.hexToBuffer(BLS_SIGNATURE); - const tx = pvm.e.newAddPermissionlessValidatorTx( + const tx = pvm.newAddPermissionlessValidatorTx( { end, delegatorRewardsOwner: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], diff --git a/src/vms/pvm/builder.test.ts b/src/vms/pvm/builder.test.ts deleted file mode 100644 index 895bd2a94..000000000 --- a/src/vms/pvm/builder.test.ts +++ /dev/null @@ -1,829 +0,0 @@ -import { testContext } from '../../fixtures/context'; -import { describe, it, expect } from 'vitest'; - -import { - fromAddressBytes, - getBaseTxForTest, - getStakeableLockedTransferableInputForTest, - getStakeableLockedTransferableOutForTest, - getTransferableInputForTest, - getTransferableOutForTest, - getValidUtxo, - testAvaxAssetID, - testGenesisData, - testOwnerXAddress, - testSubnetId, - testUTXOID1, - testUtxos, - testVMId, -} from '../../fixtures/transactions'; -import { expectTxs } from '../../fixtures/utils/expectTx'; -import { testAddress1 } from '../../fixtures/vms'; -import { - BaseTx as AvaxBaseTx, - TransferableInput, - TransferableOutput, - UTXOID, -} from '../../serializable/avax'; -import { Utxo } from '../../serializable/avax/utxo'; -import { Id } from '../../serializable/fxs/common'; -import { - Input, - OutputOwners, - TransferInput, - TransferOutput, -} from '../../serializable/fxs/secp256k1'; -import { - BigIntPr, - Byte, - Bytes, - Int, - Stringpr, -} from '../../serializable/primitives'; -import type { BaseTx } from '../../serializable/pvm'; -import { - AddDelegatorTx, - AddPermissionlessDelegatorTx, - AddPermissionlessValidatorTx, - AddSubnetValidatorTx, - AddValidatorTx, - CreateChainTx, - CreateSubnetTx, - ExportTx, - ImportTx, - Signer, - StakeableLockOut, - SubnetValidator, - TransferSubnetOwnershipTx, - TransformSubnetTx, - Validator, -} from '../../serializable/pvm'; -import { hexToBuffer } from '../../utils'; -import { - newAddDelegatorTx, - newAddPermissionlessValidatorTx, - newAddSubnetValidatorTx, - newAddValidatorTx, - newCreateBlockchainTx, - newCreateSubnetTx, - newExportTx, - newImportTx, - newRemoveSubnetValidatorTx, - newAddPermissionlessDelegatorTx, - newBaseTx, - newTransferSubnetOwnershipTx, - newTransformSubnetTx, -} from './builder'; -import { RemoveSubnetValidatorTx } from '../../serializable/pvm/removeSubnetValidatorTx'; -import { NodeId } from '../../serializable/fxs/common/nodeId'; -import { proofOfPossession } from '../../fixtures/pvm'; -import { - blsPublicKeyBytes, - blsSignatureBytes, -} from '../../fixtures/primitives'; -import { AvaxToNAvax } from '../../utils/avaxToNAvax'; -import { PrimaryNetworkID } from '../../constants/networkIDs'; - -describe('pvmBuilder', () => { - const nodeID = 'NodeID-2m38qc95mhHXtrhjyGbe7r2NhniqHHJRB'; - const toAddress = hexToBuffer('0x5432112345123451234512'); - - const getRewardsOwners = () => OutputOwners.fromNative([toAddress]); - - it('baseTx', () => { - const utxos = testUtxos(); - const toAddress = hexToBuffer('0x5432112345123451234512'); - const tnsOut = TransferableOutput.fromNative( - testAvaxAssetID.toString(), - BigInt(1 * 1e9), - [toAddress], - ); - const tx = newBaseTx(testContext, [testOwnerXAddress.toBytes()], utxos, [ - tnsOut, - ]); - const { - baseTx: { inputs, outputs }, - } = tx.getTx() as BaseTx; - - expect(outputs.length).toEqual(2); - expect(outputs as TransferableOutput[]).toEqual([ - tnsOut, - new TransferableOutput( - testAvaxAssetID, - new TransferOutput( - new BigIntPr(48999000000n), // input - amount sent - fee - OutputOwners.fromNative([testOwnerXAddress.toBytes()]), - ), - ), - ]); - - expect(inputs.length).toEqual(1); - expect(inputs as TransferableInput[]).toEqual([ - new TransferableInput( - utxos[2].utxoId, - testAvaxAssetID, - new TransferInput( - new BigIntPr(BigInt(50 * 1e9)), - Input.fromNative([0]), - ), - ), - ]); - }); - - it('importTx', () => { - const utxos = testUtxos(); - const tx = newImportTx( - testContext, - testContext.cBlockchainID, - utxos, - [testAddress1], - fromAddressBytes, - ); - const importTx = tx.getTx() as ImportTx; - - const expectedTx = new ImportTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [ - TransferableOutput.fromNative(testContext.avaxAssetID, 49999000000n, [ - testAddress1, - ]), - ], - [], - new Uint8Array([]), - ), - Id.fromString(testContext.cBlockchainID), - [TransferableInput.fromUtxoAndSigindicies(utxos[2], [0])], - ); - - expect(JSON.stringify(importTx, null, 2)).toEqual( - JSON.stringify(expectedTx, null, 2), - ); - }); - - it('exportTx', () => { - const tnsOut = TransferableOutput.fromNative( - testContext.avaxAssetID, - BigInt(5 * 1e9), - [toAddress], - ); - const unsignedTx = newExportTx( - testContext, - testContext.cBlockchainID, - fromAddressBytes, - testUtxos(), - [tnsOut], - ); - - const expectedTx = new ExportTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [ - TransferableOutput.fromNative( - testContext.avaxAssetID, - 44999000000n, - fromAddressBytes, - ), - ], - [getTransferableInputForTest()], - new Uint8Array(), - ), - Id.fromString(testContext.cBlockchainID), - [tnsOut], - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('AddValidatorTx', () => { - const unsignedTx = newAddValidatorTx( - testContext, - testUtxos(), - fromAddressBytes, - nodeID, - 100n, - 200n, - BigInt(1e9), - [toAddress], - 30 * 10000, - ); - - const expectedTx = new AddValidatorTx( - getBaseTxForTest(49000000000n, testContext.pBlockchainID), - Validator.fromNative(nodeID, 100n, 200n, BigInt(1e9)), - [getTransferableOutForTest(1000000000n)], - getRewardsOwners(), - new Int(30 * 10000), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('AddValidatorTx - stakeable locked', () => { - const utxos: Utxo[] = testUtxos(); - const lockTime = BigInt(Math.floor(new Date().getTime() / 1000)) + 10000n; - const lockedUtxo = new Utxo( - new UTXOID(testUTXOID1, new Int(0)), - testAvaxAssetID, - new StakeableLockOut( - new BigIntPr(lockTime), - new TransferOutput( - new BigIntPr(BigInt(50 * 1e9)), - OutputOwners.fromNative([testOwnerXAddress.toBytes()]), - ), - ), - ); - - utxos.push(lockedUtxo); - const unsignedTx = newAddValidatorTx( - testContext, - utxos, - fromAddressBytes, - nodeID, - 100n, - 200n, - BigInt(1e9), - [toAddress], - 30 * 10000, - ); - - const expectedTx = new AddValidatorTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [getStakeableLockedTransferableOutForTest(49000000000n, lockTime)], - [getStakeableLockedTransferableInputForTest(50000000000n, lockTime)], - new Uint8Array(), - ), - Validator.fromNative(nodeID, 100n, 200n, BigInt(1e9)), - [getStakeableLockedTransferableOutForTest(1000000000n, lockTime)], - getRewardsOwners(), - new Int(30 * 10000), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('AddDelegatorTx', () => { - const utxos: Utxo[] = testUtxos(); - const lockTime = BigInt(Math.floor(new Date().getTime() / 1000)) + 10000n; - const lockedUtxo = new Utxo( - new UTXOID(testUTXOID1, new Int(0)), - testAvaxAssetID, - new StakeableLockOut( - new BigIntPr(lockTime), - new TransferOutput( - new BigIntPr(BigInt(50 * 1e9)), - OutputOwners.fromNative([testOwnerXAddress.toBytes()]), - ), - ), - ); - - utxos.push(lockedUtxo); - - const unsignedTx = newAddDelegatorTx( - testContext, - utxos, - fromAddressBytes, - nodeID, - 100n, - 200n, - BigInt(1e9), - [toAddress], - ); - - const expectedTx = new AddDelegatorTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [getStakeableLockedTransferableOutForTest(49000000000n, lockTime)], - [getStakeableLockedTransferableInputForTest(50000000000n, lockTime)], - new Uint8Array(), - ), - Validator.fromNative(nodeID, 100n, 200n, BigInt(1e9)), - [getStakeableLockedTransferableOutForTest(1000000000n, lockTime)], - getRewardsOwners(), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('AddDelegatorTx - stakeable locked', () => { - const unsignedTx = newAddDelegatorTx( - testContext, - testUtxos(), - fromAddressBytes, - nodeID, - 100n, - 200n, - BigInt(1e9), - [toAddress], - ); - - const expectedTx = new AddDelegatorTx( - getBaseTxForTest(49000000000n, testContext.pBlockchainID), - Validator.fromNative(nodeID, 100n, 200n, BigInt(1e9)), - [getTransferableOutForTest(1000000000n)], - getRewardsOwners(), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('newCreateSubnetTx', () => { - const utxoInputAmt = BigInt(2 * 1e9); - const unsignedTx = newCreateSubnetTx( - testContext, - [getValidUtxo(new BigIntPr(utxoInputAmt))], - fromAddressBytes, - [toAddress], - ); - - const expectedTx = new CreateSubnetTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [ - getTransferableOutForTest( - utxoInputAmt - testContext.createSubnetTxFee, - ), - ], - [getTransferableInputForTest(utxoInputAmt)], - new Uint8Array(), - ), - getRewardsOwners(), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('newCreateBlockchainTx', () => { - const utxoInputAmt = BigInt(2 * 1e9); - const unsignedTx = newCreateBlockchainTx( - testContext, - [getValidUtxo(new BigIntPr(utxoInputAmt))], - fromAddressBytes, - Id.fromHex(testSubnetId).toString(), - 'Random Chain Name', - Id.fromHex(testVMId).toString(), - [], - testGenesisData, - [0], - ); - - const expectedTx = new CreateChainTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [ - getTransferableOutForTest( - utxoInputAmt - testContext.createBlockchainTxFee, - ), - ], - [getTransferableInputForTest(utxoInputAmt)], - new Uint8Array(), - ), - Id.fromHex(testSubnetId), - new Stringpr('Random Chain Name'), - Id.fromHex(testVMId), - [], - new Bytes(new TextEncoder().encode(JSON.stringify(testGenesisData))), - Input.fromNative([0]), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('newCreateSubnetValidatorTx', () => { - const utxoInputAmt = BigInt(2 * 1e9); - const unsignedTx = newAddSubnetValidatorTx( - testContext, - [getValidUtxo(new BigIntPr(utxoInputAmt))], - fromAddressBytes, - nodeID, - 100n, - 190000000n, - 1800000n, - Id.fromHex(testSubnetId).toString(), - [0], - ); - - const expectedTx = new AddSubnetValidatorTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [getTransferableOutForTest(utxoInputAmt - testContext.baseTxFee)], - [getTransferableInputForTest(utxoInputAmt)], - new Uint8Array(), - ), - SubnetValidator.fromNative( - nodeID, - 100n, - 190000000n, - 1800000n, - Id.fromHex(testSubnetId), - ), - Input.fromNative([0]), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('newRemoveSubnetValidatorTx', () => { - const utxoInputAmt = BigInt(2 * 1e9); - const unsignedTx = newRemoveSubnetValidatorTx( - testContext, - [getValidUtxo(new BigIntPr(utxoInputAmt))], - fromAddressBytes, - nodeID, - Id.fromHex(testSubnetId).toString(), - [0], - ); - - const expectedTx = new RemoveSubnetValidatorTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [getTransferableOutForTest(utxoInputAmt - testContext.baseTxFee)], - [getTransferableInputForTest(utxoInputAmt)], - new Uint8Array(), - ), - NodeId.fromString(nodeID), - Id.fromHex(testSubnetId), - Input.fromNative([0]), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('newAddPermissionlessValidatorTx - primary network', () => { - const utxoInputAmt = AvaxToNAvax(2); - - const unsignedTx = newAddPermissionlessValidatorTx( - testContext, - [getValidUtxo(new BigIntPr(utxoInputAmt))], - fromAddressBytes, - nodeID, - PrimaryNetworkID.toString(), - 0n, // startTime - 120n, //end time - 1800000n, // weight - [], // rewards owners - [], // delegatorRewardsOwner - 1, - {}, - 1, - 0n, - blsPublicKeyBytes(), - blsSignatureBytes(), - ); - - const expectedTx = new AddPermissionlessValidatorTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [ - getTransferableOutForTest( - utxoInputAmt - 1800000n - testContext.addPrimaryNetworkValidatorFee, - ), - ], - [getTransferableInputForTest(utxoInputAmt)], - new Uint8Array(), - ), - SubnetValidator.fromNative( - NodeId.fromString(nodeID).toString(), - 0n, - 120n, - 1800000n, - PrimaryNetworkID, - ), - new Signer(proofOfPossession()), - [getTransferableOutForTest(1800000n)], //stake - OutputOwners.fromNative([], 0n, 1), - OutputOwners.fromNative([], 0n, 1), - new Int(1), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('newAddPermissionlessValidatorTx - subnet', () => { - const utxoInputAmt = AvaxToNAvax(2); - - const unsignedTx = newAddPermissionlessValidatorTx( - testContext, - [getValidUtxo(new BigIntPr(utxoInputAmt))], - fromAddressBytes, - nodeID, - Id.fromHex(testSubnetId).toString(), - 0n, // startTime - 120n, //end time - 1800000n, // weight - [], // rewards owners - [], // delegatorRewardsOwner - 1, - {}, - 1, - 0n, - blsPublicKeyBytes(), - blsSignatureBytes(), - ); - - const expectedTx = new AddPermissionlessValidatorTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [ - getTransferableOutForTest( - utxoInputAmt - 1800000n - testContext.addSubnetValidatorFee, - ), - ], - [getTransferableInputForTest(utxoInputAmt)], - new Uint8Array(), - ), - SubnetValidator.fromNative( - NodeId.fromString(nodeID).toString(), - 0n, - 120n, - 1800000n, - Id.fromHex(testSubnetId), - ), - new Signer(proofOfPossession()), - [getTransferableOutForTest(1800000n)], //stake - OutputOwners.fromNative([], 0n, 1), - OutputOwners.fromNative([], 0n, 1), - new Int(1), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('newAddPermissionlessValidatorTx - subnet with non avax staking token', () => { - const utxoInputAmt = AvaxToNAvax(2); - const stakingAssetId = Id.fromHex('0102'); - const stakeAmount = 1_000_000n; - - const unsignedTx = newAddPermissionlessValidatorTx( - testContext, - [ - getValidUtxo(new BigIntPr(utxoInputAmt)), - getValidUtxo(new BigIntPr(2n * stakeAmount), stakingAssetId), - ], - fromAddressBytes, - nodeID, - Id.fromHex(testSubnetId).toString(), - 0n, // startTime - 120n, //end time - stakeAmount, // weight - [], // rewards owners - [], // delegatorRewardsOwner - 1, - {}, - 1, - 0n, - blsPublicKeyBytes(), - blsSignatureBytes(), - stakingAssetId.toString(), - ); - - const baseOuts = (unsignedTx.getTx() as AddPermissionlessValidatorTx).baseTx - .outputs; - const stakeUtxos = (unsignedTx.getTx() as AddPermissionlessValidatorTx) - .stake; - expect(stakeUtxos.length).toEqual(1); - // Expect correct stake out - expect(stakeUtxos[0].assetId.toString()).toEqual(stakingAssetId.toString()); - expect(stakeUtxos[0].amount()).toEqual(stakeAmount); - // Expect correct change utxos - expect(baseOuts.length).toEqual(2); - // Stake token change - expect(baseOuts[0].assetId.toString()).toEqual(stakingAssetId.toString()); - expect(baseOuts[0].amount()).toEqual(stakeAmount); - // AVAX Change - expect(baseOuts[1].assetId.toString()).toEqual(testContext.avaxAssetID); - expect(baseOuts[1].amount().toString()).toEqual( - (utxoInputAmt - testContext.addSubnetValidatorFee).toString(), - ); - }); - - it('newAddPermissionlessDelegatorTx - primary network', () => { - const utxoInputAmt = AvaxToNAvax(2); - - const unsignedTx = newAddPermissionlessDelegatorTx( - testContext, - [getValidUtxo(new BigIntPr(utxoInputAmt))], - fromAddressBytes, - nodeID, - PrimaryNetworkID.toString(), - 0n, // startTime - 120n, //end time - 1800000n, // weight - [], // rewards owners - {}, - 1, - 0n, - ); - - const expectedTx = new AddPermissionlessDelegatorTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [ - getTransferableOutForTest( - utxoInputAmt - 1800000n - testContext.addPrimaryNetworkDelegatorFee, - ), - ], - [getTransferableInputForTest(utxoInputAmt)], - new Uint8Array(), - ), - SubnetValidator.fromNative( - NodeId.fromString(nodeID).toString(), - 0n, - 120n, - 1800000n, - PrimaryNetworkID, - ), - [getTransferableOutForTest(1800000n)], //stake - OutputOwners.fromNative([], 0n, 1), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('newAddPermissionlessDelegatorTx - subnet', () => { - const utxoInputAmt = AvaxToNAvax(2); - - const unsignedTx = newAddPermissionlessDelegatorTx( - testContext, - [getValidUtxo(new BigIntPr(utxoInputAmt))], - fromAddressBytes, - nodeID, - Id.fromHex(testSubnetId).toString(), - 0n, // startTime - 120n, //end time - 1800000n, // weight - [], // rewards owners - {}, - 1, - 0n, - ); - - const expectedTx = new AddPermissionlessDelegatorTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [ - getTransferableOutForTest( - utxoInputAmt - 1800000n - testContext.addSubnetDelegatorFee, - ), - ], - [getTransferableInputForTest(utxoInputAmt)], - new Uint8Array(), - ), - SubnetValidator.fromNative( - NodeId.fromString(nodeID).toString(), - 0n, - 120n, - 1800000n, - Id.fromHex(testSubnetId), - ), - [getTransferableOutForTest(1800000n)], //stake - OutputOwners.fromNative([], 0n, 1), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('newAddPermissionlessDelegatorTx - subnet with non avax staking token', () => { - const utxoInputAmt = AvaxToNAvax(2); - const stakingAssetId = Id.fromHex('0102'); - const stakeAmount = 1_000_000n; - - const unsignedTx = newAddPermissionlessDelegatorTx( - testContext, - [ - getValidUtxo(new BigIntPr(utxoInputAmt)), - getValidUtxo(new BigIntPr(2n * stakeAmount), stakingAssetId), - ], - fromAddressBytes, - nodeID, - Id.fromHex(testSubnetId).toString(), - 0n, // startTime - 120n, //end time - stakeAmount, // weight - [], // rewards owners - {}, - 1, - 0n, - stakingAssetId.toString(), - ); - - const baseOuts = (unsignedTx.getTx() as AddPermissionlessDelegatorTx).baseTx - .outputs; - const stakeUtxos = (unsignedTx.getTx() as AddPermissionlessDelegatorTx) - .stake; - - expect(stakeUtxos.length).toEqual(1); - // Expect correct stake out - expect(stakeUtxos[0].assetId.toString()).toEqual(stakingAssetId.toString()); - expect(stakeUtxos[0].amount()).toEqual(stakeAmount); - // Expect correct change utxos - expect(baseOuts.length).toEqual(2); - // Stake token change - expect(baseOuts[0].assetId.toString()).toEqual(stakingAssetId.toString()); - expect(baseOuts[0].amount()).toEqual(stakeAmount); - // AVAX Change - expect(baseOuts[1].assetId.toString()).toEqual(testContext.avaxAssetID); - expect(baseOuts[1].amount().toString()).toEqual( - (utxoInputAmt - testContext.addSubnetDelegatorFee).toString(), - ); - }); - - it('newTransferSubnetOwnershipTx', () => { - const utxoInputAmt = BigInt(2 * 1e9); - const subnetAuth = [0, 1]; - const unsignedTx = newTransferSubnetOwnershipTx( - testContext, - [getValidUtxo(new BigIntPr(utxoInputAmt))], - fromAddressBytes, - Id.fromHex(testSubnetId).toString(), - subnetAuth, - [toAddress], - ); - - const expectedTx = new TransferSubnetOwnershipTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [getTransferableOutForTest(utxoInputAmt - testContext.baseTxFee)], - [getTransferableInputForTest(utxoInputAmt)], - new Uint8Array(), - ), - Id.fromHex(testSubnetId), - Input.fromNative(subnetAuth), - getRewardsOwners(), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); - - it('newTransformSubnetTx', () => { - const utxoInputAmt = BigInt(2 * 1e12); - const stakingAssetId = Id.fromHex('0102'); - const subnetAuth = [0, 1]; - - const unsignedTx = newTransformSubnetTx( - testContext, - [getValidUtxo(new BigIntPr(utxoInputAmt))], - fromAddressBytes, - Id.fromHex(testSubnetId).toString(), - stakingAssetId.toString(), - 1n, - 2n, - 3n, - 4n, - 5n, - 6n, - 1, - 2, - 3, - 4n, - 5, - 6, - subnetAuth, - ); - - const expectedTx = new TransformSubnetTx( - AvaxBaseTx.fromNative( - testContext.networkID, - testContext.pBlockchainID, - [ - getTransferableOutForTest( - utxoInputAmt - testContext.transformSubnetTxFee, - ), - ], - [getTransferableInputForTest(utxoInputAmt)], - new Uint8Array(), - ), - Id.fromHex(testSubnetId), - stakingAssetId, - new BigIntPr(1n), - new BigIntPr(2n), - new BigIntPr(3n), - new BigIntPr(4n), - new BigIntPr(5n), - new BigIntPr(6n), - new Int(1), - new Int(2), - new Int(3), - new BigIntPr(4n), - new Byte(hexToBuffer('0x5')), - new Int(6), - Input.fromNative(subnetAuth), - ); - - expectTxs(unsignedTx.getTx(), expectedTx); - }); -}); diff --git a/src/vms/pvm/builder.ts b/src/vms/pvm/builder.ts deleted file mode 100644 index ff80b3087..000000000 --- a/src/vms/pvm/builder.ts +++ /dev/null @@ -1,888 +0,0 @@ -import { PlatformChainID, PrimaryNetworkID } from '../../constants/networkIDs'; -import { - BaseTx as AvaxBaseTx, - TransferableInput, - TransferableOutput, -} from '../../serializable/avax'; -import type { Utxo } from '../../serializable/avax/utxo'; -import { Id } from '../../serializable/fxs/common'; -import { Input, OutputOwners } from '../../serializable/fxs/secp256k1'; -import { BigIntPr, Byte, Stringpr } from '../../serializable/primitives'; -import { Bytes, Int } from '../../serializable/primitives'; -import { - AddDelegatorTx, - AddValidatorTx, - BaseTx, - CreateChainTx, - CreateSubnetTx, - ExportTx, - ImportTx, - Validator, - AddSubnetValidatorTx, - SubnetValidator, - AddPermissionlessValidatorTx, - AddPermissionlessDelegatorTx, - RemoveSubnetValidatorTx, - TransferSubnetOwnershipTx, - TransformSubnetTx, -} from '../../serializable/pvm'; -import { addressesFromBytes, hexToBuffer } from '../../utils'; -import { AddressMaps } from '../../utils/addressMap'; -import { getImportedInputsFromUtxos } from '../../utils/builderUtils'; -import { compareTransferableOutputs } from '../../utils/sort'; -import { defaultSpendOptions } from '../common/defaultSpendOptions'; -import type { SpendOptions } from '../common/models'; -import { UnsignedTx } from '../common/unsignedTx'; -import type { Context } from '../context'; -import { calculateUTXOSpend } from '../utils/calculateSpend'; -import { - useConsolidateOutputs, - useSpendableLockedUTXOs, - useUnlockedUTXOs, -} from './utxoCalculationFns'; -import { NodeId } from '../../serializable/fxs/common/nodeId'; -import { createSignerOrSignerEmptyFromStrings } from '../../serializable/pvm/signer'; -import { baseTxUnsafePvm } from '../common'; - -/** - * @param fromAddresses - used for selecting which utxos are signable - * @param utxoSet - list of utxos to spend from - * @param outputs - the desired output (change outputs will be added to them automatically) - * @param options - see SpendingOptions - * - * @returns UnsignedTx containing a BaseTx - */ -export function newBaseTx( - context: Context, - fromAddressesBytes: Uint8Array[], - utxoSet: Utxo[], - outputs: TransferableOutput[], - options?: SpendOptions, -) { - const fromAddresses = addressesFromBytes(fromAddressesBytes); - const defaultedOptions = defaultSpendOptions(fromAddressesBytes, options); - const toBurn = new Map([ - [context.avaxAssetID, context.baseTxFee], - ]); - - outputs.forEach((out) => { - const assetId = out.assetId.value(); - toBurn.set(assetId, (toBurn.get(assetId) || 0n) + out.output.amount()); - }); - - const { inputs, inputUTXOs, changeOutputs, addressMaps } = calculateUTXOSpend( - toBurn, - undefined, - utxoSet, - fromAddresses, - defaultedOptions, - [useUnlockedUTXOs, useConsolidateOutputs], - ); - - const allOutputs = [...outputs, ...changeOutputs]; - allOutputs.sort(compareTransferableOutputs); - - return new UnsignedTx( - new BaseTx( - baseTxUnsafePvm(context, allOutputs, inputs, defaultedOptions.memo), - ), - inputUTXOs, - addressMaps, - ); -} - -/** - @param sourceChainID - base58 of the sourceChain. can pass in from context - @param utxos - list of utxos - @param toAddress - list of addresses to import into - @param fromAddressesBytes - used for utxo selection. provide all addresses that can sign Tx - @param options - see SpendOptions - @param threshold - the threshold to write on the utxo - @param locktime - the locktime to write onto the utxo - - @returns UnsignedTx -*/ -export function newImportTx( - context: Context, - sourceChainId: string, - utxos: Utxo[], - toAddresses: Uint8Array[], - fromAddressesBytes: Uint8Array[], - options?: SpendOptions, - threshold = 1, - locktime = 0n, -) { - const fromAddresses = addressesFromBytes(fromAddressesBytes); - const defaultedOptions = defaultSpendOptions(fromAddressesBytes, options); - - utxos = utxos.filter( - // Currently - only AVAX is allowed to be imported to the P-chain - (utxo) => utxo.assetId.toString() === context.avaxAssetID, - ); - - const { importedAmounts, importedInputs, inputUTXOs } = - getImportedInputsFromUtxos( - utxos, - fromAddressesBytes, - defaultedOptions.minIssuanceTime, - ); - - const importedAvax = importedAmounts[context.avaxAssetID] ?? 0n; - - importedInputs.sort(TransferableInput.compare); - const addressMaps = AddressMaps.fromTransferableInputs( - importedInputs, - utxos, - defaultedOptions.minIssuanceTime, - fromAddressesBytes, - ); - if (!importedInputs.length) { - throw new Error('no UTXOs available to import'); - } - - let inputs: TransferableInput[] = []; - let changeOutputs: TransferableOutput[] = []; - - if (importedAvax < context.baseTxFee) { - const toBurn = new Map([ - [context.avaxAssetID, context.baseTxFee - importedAvax], - ]); - - const spendRes = calculateUTXOSpend( - toBurn, - undefined, - utxos, - fromAddresses, - defaultedOptions, - [useUnlockedUTXOs], - ); - inputs = spendRes.inputs; - changeOutputs = spendRes.changeOutputs; - } else if (importedAvax > context.baseTxFee) { - changeOutputs.push( - TransferableOutput.fromNative( - context.avaxAssetID, - importedAvax - context.baseTxFee, - toAddresses, - locktime, - threshold, - ), - ); - } - - return new UnsignedTx( - new ImportTx( - new AvaxBaseTx( - new Int(context.networkID), - PlatformChainID, - changeOutputs, - inputs, - new Bytes(defaultedOptions.memo), - ), - Id.fromString(sourceChainId), - importedInputs, - ), - inputUTXOs, - addressMaps, - ); -} - -const getToBurn = ( - context: Context, - outputs: TransferableOutput[], - baseFee: bigint, -) => { - const toBurn = new Map([[context.avaxAssetID, baseFee]]); - - outputs.forEach((output) => { - const assetId = output.assetId.value(); - toBurn.set(assetId, (toBurn.get(assetId) || 0n) + output.output.amount()); - }); - return toBurn; -}; - -/** - * Helper function which creates an unsigned [[AddValidatorTx]]. For more granular control, you may create your own - * [[UnsignedTx]] manually and import the [[AddValidatorTx]] class directly. - * - * @deprecated since {@link https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/62-disable-addvalidatortx-and-adddelegatortx.md|Durango-upgrade} - * - * @param utxos A list of UTXOs that the transaction is built on - * @param fromAddresses An array of addresses as uint8Array who own the staking UTXOs the fees in AVAX - * @param nodeID The node ID of the validator being added. - * @param start The Unix time based on p-chain timestamp when the validator starts validating the Primary Network. - * @param end The Unix time based on p-chain timestamp when the validator stops validating the Primary Network (and staked AVAX is returned). - * @param weight The amount being delegated in nAVAX - * @param rewardAddresses The addresses which will receive the rewards from the delegated stake. - * @param shares A number for the percentage times 10,000 of reward to be given to the validator when someone delegates to them. - * @param threshold Opional. The number of signatures required to spend the funds in the resultant reward UTXO. Default 1. - * @param locktime Optional. The locktime field created in the resulting reward outputs - * - * @returns An unsigned transaction created from the passed in parameters. - */ -export function newAddValidatorTx( - context: Context, - utxos: Utxo[], - fromAddressesBytes: Uint8Array[], - nodeID: string, - start: bigint, - end: bigint, - weight: bigint, - rewardAddresses: Uint8Array[], - shares: number, - options?: SpendOptions, - threshold = 1, - locktime = 0n, -) { - const toBurn = new Map([ - [context.avaxAssetID, context.addPrimaryNetworkValidatorFee], - ]); - const toStake = new Map([[context.avaxAssetID, weight]]); - - const defaultedOptions = defaultSpendOptions(fromAddressesBytes, options); - const { addressMaps, changeOutputs, inputUTXOs, inputs, stakeOutputs } = - calculateUTXOSpend( - toBurn, - toStake, - utxos, - addressesFromBytes(fromAddressesBytes), - defaultedOptions, - [useSpendableLockedUTXOs, useUnlockedUTXOs, useConsolidateOutputs], - ); - - const validatorTx = new AddValidatorTx( - AvaxBaseTx.fromNative( - context.networkID, - context.pBlockchainID, - changeOutputs, - inputs, - defaultedOptions.memo, - ), - Validator.fromNative(nodeID, start, end, weight), - stakeOutputs, - OutputOwners.fromNative(rewardAddresses, locktime, threshold), - new Int(shares), - ); - return new UnsignedTx(validatorTx, inputUTXOs, addressMaps); -} - -/** - * - * @param destinationChainID chain to send the UTXOs to - * @param fromAddressesBytes used for filtering utxos. - * @param utxos list of utxos to choose from - * @param outputs list of outputs to create. - * @param options used for filtering UTXO's - * @returns UnsignedTx containing an exportTx - */ - -export function newExportTx( - context: Context, - destinationChainID: string, - fromAddressesBytes: Uint8Array[], - utxos: Utxo[], - outputs: TransferableOutput[], - options?: SpendOptions, -) { - const fromAddresses = addressesFromBytes(fromAddressesBytes); - - const defaultedOptions = defaultSpendOptions(fromAddressesBytes, options); - const toBurn = getToBurn(context, outputs, context.baseTxFee); - - const { inputs, changeOutputs, addressMaps, inputUTXOs } = calculateUTXOSpend( - toBurn, - undefined, - utxos, - fromAddresses, - defaultedOptions, - [useUnlockedUTXOs], - ); - - outputs.sort(compareTransferableOutputs); - return new UnsignedTx( - new ExportTx( - new AvaxBaseTx( - new Int(context.networkID), - PlatformChainID, - changeOutputs, - inputs, - new Bytes(defaultedOptions.memo), - ), - Id.fromString(destinationChainID), - outputs, - ), - inputUTXOs, - addressMaps, - ); -} - -/** - * @deprecated since {@link https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/62-disable-addvalidatortx-and-adddelegatortx.md|Durango-upgrade} - * - * @param utxos list of utxos to choose from - * @param fromAddressesBytes used for filtering utxos - * @param nodeID id of the node to delegate. starts with "NodeID-" - * @param start The Unix time based on p-chain timestamp when the validator starts validating the Primary Network. - * @param end The Unix time based on p-chain timestamp when the validator stops validating the Primary Network (and staked AVAX is returned). - * @param weight The amount being delegated in nAVAX - * @param rewardAddresses The addresses which will receive the rewards from the delegated stake. - * @param options - used for filtering utxos - * @param threshold Opional. The number of signatures required to spend the funds in the resultant reward UTXO. Default 1. - * @param locktime Optional. The locktime field created in the resulting reward outputs - * @returns UnsignedTx - */ - -export function newAddDelegatorTx( - context: Context, - utxos: Utxo[], - fromAddressesBytes: Uint8Array[], - nodeID: string, - start: bigint, - end: bigint, - weight: bigint, - rewardAddresses: Uint8Array[], - options?: SpendOptions, - threshold = 1, - locktime = 0n, -) { - const toBurn = new Map([ - [context.avaxAssetID, context.addPrimaryNetworkDelegatorFee], - ]); - const toStake = new Map([[context.avaxAssetID, weight]]); - - const defaultedOptions = defaultSpendOptions(fromAddressesBytes, options); - const { inputs, addressMaps, changeOutputs, inputUTXOs, stakeOutputs } = - calculateUTXOSpend( - toBurn, - toStake, - utxos, - addressesFromBytes(fromAddressesBytes), - defaultedOptions, - [useSpendableLockedUTXOs, useUnlockedUTXOs, useConsolidateOutputs], - ); - - const addDelegatorTx = new AddDelegatorTx( - AvaxBaseTx.fromNative( - context.networkID, - context.pBlockchainID, - changeOutputs, - inputs, - defaultedOptions.memo, - ), - Validator.fromNative(nodeID, start, end, weight), - stakeOutputs, - OutputOwners.fromNative(rewardAddresses, locktime, threshold), - ); - return new UnsignedTx(addDelegatorTx, inputUTXOs, addressMaps); -} - -/** - * @see https://docs.avax.network/specs/platform-transaction-serialization#unsigned-create-subnet-tx - * - * @param context - * @param utxos list of utxos to choose from - * @param fromAddressesBytes used for filtering utxos - * @param rewardAddresses The addresses which will receive the rewards from the delegated stake. - * @param options used for filtering utxos - * @param threshold Opional. The number of signatures required to spend the funds in the resultant reward UTXO. Default 1. - * @param locktime Optional. The locktime field created in the resulting reward outputs - * @returns UnsignedTx - */ -export function newCreateSubnetTx( - context: Context, - utxos: Utxo[], - fromAddressesBytes: Uint8Array[], - subnetOwners: Uint8Array[], - options?: SpendOptions, - threshold = 1, - locktime = 0n, -) { - const defaultedOptions = defaultSpendOptions(fromAddressesBytes, options); - - const { inputs, addressMaps, changeOutputs, inputUTXOs } = calculateUTXOSpend( - new Map([[context.avaxAssetID, context.createSubnetTxFee]]), - undefined, - utxos, - addressesFromBytes(fromAddressesBytes), - defaultedOptions, - [useUnlockedUTXOs], - ); - - const createSubnetTx = new CreateSubnetTx( - AvaxBaseTx.fromNative( - context.networkID, - context.pBlockchainID, - changeOutputs, - inputs, - defaultedOptions.memo, - ), - OutputOwners.fromNative(subnetOwners, locktime, threshold), - ); - - return new UnsignedTx(createSubnetTx, inputUTXOs, addressMaps); -} - -/** - * @see https://docs.avax.network/specs/platform-transaction-serialization#unsigned-create-chain-tx - * - * @param context - * @param utxos list of utxos to choose from - * @param fromAddressesBytes used for filtering utxos - * @param subnetID ID of the Subnet that validates this blockchain - * @param chainName A human readable name for the chain; need not be unique - * @param vmID ID of the VM running on the new chain - * @param fxIds IDs of the feature extensions running on the new chain - * @param genesisData json config for the genesis data - * @param subnetAuth specifies indices of subnet owners - * @param options used for filtering utxos - * @returns UnsignedTx - */ -export function newCreateBlockchainTx( - context: Context, - utxos: Utxo[], - fromAddressesBytes: Uint8Array[], - subnetID: string, - chainName: string, - vmID: string, - fxIds: string[], - genesisData: Record, - subnetAuth: number[], - options?: SpendOptions, -) { - const defaultedOptions = defaultSpendOptions(fromAddressesBytes, options); - - const genesisBytes = new Bytes( - new TextEncoder().encode(JSON.stringify(genesisData)), - ); - - const { inputs, addressMaps, changeOutputs, inputUTXOs } = calculateUTXOSpend( - new Map([[context.avaxAssetID, context.createBlockchainTxFee]]), - undefined, - utxos, - addressesFromBytes(fromAddressesBytes), - defaultedOptions, - [useUnlockedUTXOs], - ); - - const createChainTx = new CreateChainTx( - AvaxBaseTx.fromNative( - context.networkID, - context.pBlockchainID, - changeOutputs, - inputs, - defaultedOptions.memo, - ), - Id.fromString(subnetID), - new Stringpr(chainName), - Id.fromString(vmID), - fxIds.map(Id.fromString.bind(Id)), - genesisBytes, - Input.fromNative(subnetAuth), - ); - - return new UnsignedTx(createChainTx, inputUTXOs, addressMaps); -} - -export function newAddSubnetValidatorTx( - context: Context, - utxos: Utxo[], - fromAddressesBytes: Uint8Array[], - nodeId: string, - start: bigint, - end: bigint, - weight: bigint, - subnetID: string, - subnetAuth: number[], - options?: SpendOptions, -) { - const defaultedOptions = defaultSpendOptions(fromAddressesBytes, options); - - const { inputs, addressMaps, changeOutputs, inputUTXOs } = calculateUTXOSpend( - new Map([[context.avaxAssetID, context.addSubnetValidatorFee]]), - undefined, - utxos, - addressesFromBytes(fromAddressesBytes), - defaultedOptions, - [useUnlockedUTXOs], - ); - - const addSubnetValidatorTx = new AddSubnetValidatorTx( - AvaxBaseTx.fromNative( - context.networkID, - context.pBlockchainID, - changeOutputs, - inputs, - defaultedOptions.memo, - ), - SubnetValidator.fromNative( - nodeId, - start, - end, - weight, - Id.fromString(subnetID), - ), - Input.fromNative(subnetAuth), - ); - - return new UnsignedTx(addSubnetValidatorTx, inputUTXOs, addressMaps); -} -export function newRemoveSubnetValidatorTx( - context: Context, - utxos: Utxo[], - fromAddressesBytes: Uint8Array[], - nodeId: string, - subnetID: string, - subnetAuth: number[], - options?: SpendOptions, -) { - const defaultedOptions = defaultSpendOptions(fromAddressesBytes, options); - - const { inputs, addressMaps, changeOutputs, inputUTXOs } = calculateUTXOSpend( - new Map([[context.avaxAssetID, context.baseTxFee]]), - undefined, - utxos, - addressesFromBytes(fromAddressesBytes), - defaultedOptions, - [useUnlockedUTXOs], - ); - - const removeSubnetValidatorTx = new RemoveSubnetValidatorTx( - AvaxBaseTx.fromNative( - context.networkID, - context.pBlockchainID, - changeOutputs, - inputs, - defaultedOptions.memo, - ), - NodeId.fromString(nodeId), - Id.fromString(subnetID), - Input.fromNative(subnetAuth), - ); - - return new UnsignedTx(removeSubnetValidatorTx, inputUTXOs, addressMaps); -} - -/** - * Helper function which creates an unsigned [[newAddPermissionlessValidatorTx]]. For more granular control, you may create your own - * [[UnsignedTx]] manually and import the [[newAddPermissionlessValidatorTx]] class directly. - * - * @param utxos A list of UTXOs that the transaction is built on - * @param fromAddresses An array of addresses as uint8Array who own the staking UTXOs the fees in AVAX - * @param nodeID The node ID of the validator being added. - * @param subnetID ID of the subnet this validator is validating - * @param start The Unix time based on p-chain timestamp when the validator starts validating the Primary Network. - * @param end The Unix time based on p-chain timestamp when the validator stops validating the Primary Network (and staked AVAX is returned). - * @param weight The amount being locked for validation in nAVAX - * @param rewardAddresses The addresses which will receive the rewards from the delegated stake. Given addresses will share the reward UTXO. - * @param shares A number for the percentage times 10,000 of reward to be given to the validator when someone delegates to them. - * @param threshold Opional. The number of signatures required to spend the funds in the resultant reward UTXO. Default 1. - * @param locktime Optional. The locktime field created in the resulting reward outputs - * @param publicKey the BLS public key, If the subnet is the primary network - * @param signature the BLS signature, If the subnet is the primary network - * @param stakingAssetId Which asset to stake. Defaults to AVAX. - * - * @returns An unsigned transaction created from the passed in parameters. - */ -export function newAddPermissionlessValidatorTx( - context: Context, - utxos: Utxo[], - fromAddressesBytes: Uint8Array[], - nodeID: string, - subnetID: string, - start: bigint, - end: bigint, - weight: bigint, - rewardAddresses: Uint8Array[], - delegatorRewardsOwner: Uint8Array[], - shares: number, - options?: SpendOptions, - threshold = 1, - locktime = 0n, - publicKey?: Uint8Array, - signature?: Uint8Array, - stakingAssetId?: string, -) { - const isPrimaryNetwork = subnetID === PrimaryNetworkID.toString(); - const fee = isPrimaryNetwork - ? context.addPrimaryNetworkValidatorFee - : context.addSubnetValidatorFee; - const toBurn = new Map([[context.avaxAssetID, fee]]); - - const assetId = stakingAssetId ?? context.avaxAssetID; - - // Check if we use correct asset if on primary network - if (isPrimaryNetwork && assetId !== context.avaxAssetID) - throw new Error('Staking asset ID must be AVAX for the primary network.'); - - const toStake = new Map([[assetId, weight]]); - - const defaultedOptions = defaultSpendOptions(fromAddressesBytes, options); - - const signer = createSignerOrSignerEmptyFromStrings(publicKey, signature); - const validatorOutputOwners = OutputOwners.fromNative( - rewardAddresses, - locktime, - threshold, - ); - const delegatorOutputOwners = OutputOwners.fromNative( - delegatorRewardsOwner, - 0n, - ); - - const { addressMaps, changeOutputs, inputUTXOs, inputs, stakeOutputs } = - calculateUTXOSpend( - toBurn, - toStake, - utxos, - addressesFromBytes(fromAddressesBytes), - defaultedOptions, - [useSpendableLockedUTXOs, useUnlockedUTXOs, useConsolidateOutputs], - ); - - const validatorTx = new AddPermissionlessValidatorTx( - AvaxBaseTx.fromNative( - context.networkID, - context.pBlockchainID, - changeOutputs, - inputs, - defaultedOptions.memo, - ), - SubnetValidator.fromNative( - nodeID, - start, - end, - weight, - Id.fromString(subnetID), - ), - signer, - stakeOutputs, - validatorOutputOwners, - delegatorOutputOwners, - new Int(shares), - ); - return new UnsignedTx(validatorTx, inputUTXOs, addressMaps); -} - -/** - * Helper function which creates an unsigned [[newAddPermissionlessDelegatorTx]]. For more granular control, you may create your own - * [[UnsignedTx]] manually and import the [[newAddPermissionlessDelegatorTx]] class directly. - * - * @param context The context for the network - * @param utxos A list of UTXOs that the transaction is built on - * @param fromAddressesBytes An array of addresses as uint8Array who own the staking UTXOs the fees in AVAX - * @param nodeID The node ID of the validator being delegated to. - * @param subnetID ID of the subnet being delegated to - * @param start The Unix time based on p-chain timestamp when the delegation starts. - * @param end The Unix time based on p-chain timestamp when the delegation stops (and staked AVAX is returned). - * @param weight The amount being delegated in nAVAX - * @param rewardAddresses The addresses which will receive the rewards from the delegated stake. Given addresses will share the reward UTXO. - * @param threshold Opional. The number of signatures required to spend the funds in the resultant reward UTXO. Default 1. - * @param locktime Optional. The locktime field created in the resulting reward outputs - * @param options Optional. Config for the transaction such as change address, threshold and locktime. - * @param stakingAssetId Optional. Which asset to stake. Defaults to AVAX. - * - * @returns An unsigned transaction created from the passed in parameters. - */ -export function newAddPermissionlessDelegatorTx( - context: Context, - utxos: Utxo[], - fromAddressesBytes: Uint8Array[], - nodeID: string, - subnetID: string, - start: bigint, - end: bigint, - weight: bigint, - rewardAddresses: Uint8Array[], - options?: SpendOptions, - threshold = 1, - locktime = 0n, - stakingAssetId?: string, -) { - const isPrimaryNetwork = subnetID === PrimaryNetworkID.toString(); - const fee = isPrimaryNetwork - ? context.addPrimaryNetworkDelegatorFee - : context.addSubnetDelegatorFee; - - const assetId = stakingAssetId ?? context.avaxAssetID; - - // Check if we use correct asset if on primary network - if (isPrimaryNetwork && assetId !== context.avaxAssetID) - throw new Error('Staking asset ID must be AVAX for the primary network.'); - - const toBurn = new Map([[context.avaxAssetID, fee]]); - const toStake = new Map([[assetId, weight]]); - - const defaultedOptions = defaultSpendOptions(fromAddressesBytes, options); - - const delegatorRewardsOwner = OutputOwners.fromNative( - rewardAddresses, - locktime, - threshold, - ); - - const { addressMaps, changeOutputs, inputUTXOs, inputs, stakeOutputs } = - calculateUTXOSpend( - toBurn, - toStake, - utxos, - addressesFromBytes(fromAddressesBytes), - defaultedOptions, - [useSpendableLockedUTXOs, useUnlockedUTXOs, useConsolidateOutputs], - ); - - const delegatorTx = new AddPermissionlessDelegatorTx( - AvaxBaseTx.fromNative( - context.networkID, - context.pBlockchainID, - changeOutputs, - inputs, - defaultedOptions.memo, - ), - SubnetValidator.fromNative( - nodeID, - start, - end, - weight, - Id.fromString(subnetID), - ), - stakeOutputs, - delegatorRewardsOwner, - ); - return new UnsignedTx(delegatorTx, inputUTXOs, addressMaps); -} - -/** - * @param context - * @param utxos list of utxos to choose from - * @param fromAddressesBytes used for filtering utxos - * @param subnetID ID of the subnet - * @param assetID ID of the subnet's staking asset - * @param initialSupply the amount to initially specify as the current supply - * @param maximumSupply the amount to specify as the maximum token supply - * @param minConsumptionRate the rate to allocate funds if the validator's stake duration is 0 - * @param maxConsumptionRate the rate to allocate funds if the validator's stake duration is equal to the minting period - * @param minValidatorStake the minimum amount of funds required to become a validator - * @param maxValidatorStake the maximum amount of funds a single validator can be allocated, including delegated funds - * @param minStakeDuration the minimum number of seconds a staker can stake for - * @param maxStakeDuration the maximum number of seconds a staker can stake for - * @param minDelegationFee the minimum percentage a validator must charge a delegator for delegating - * @param minDelegatorStake the minimum amount of funds required to become a delegator - * @param maxValidatorWeightFactor the factor which calculates the maximum amount of delegation a validator can receive - * @param uptimeRequirement the minimum percentage a validator must be online and responsive to receive a reward - * @param subnetAuth specifies indices of existing subnet owners - * @param options used for filtering utxos - * @returns UnsignedTx containing a TransformSubnetTx - */ -export function newTransformSubnetTx( - context: Context, - utxos: Utxo[], - fromAddressesBytes: Uint8Array[], - subnetID: string, - assetID: string, - initialSupply: bigint, - maximumSupply: bigint, - minConsumptionRate: bigint, - maxConsumptionRate: bigint, - minValidatorStake: bigint, - maxValidatorStake: bigint, - minStakeDuration: number, - maxStakeDuration: number, - minDelegationFee: number, - minDelegatorStake: bigint, - maxValidatorWeightFactor: number, - uptimeRequirement: number, - subnetAuth: number[], - options?: SpendOptions, -) { - const defaultedOptions = defaultSpendOptions(fromAddressesBytes, options); - - const { inputs, addressMaps, changeOutputs, inputUTXOs } = calculateUTXOSpend( - new Map([[context.avaxAssetID, context.transformSubnetTxFee]]), - undefined, - utxos, - addressesFromBytes(fromAddressesBytes), - defaultedOptions, - [useUnlockedUTXOs], - ); - - return new UnsignedTx( - new TransformSubnetTx( - AvaxBaseTx.fromNative( - context.networkID, - context.pBlockchainID, - changeOutputs, - inputs, - defaultedOptions.memo, - ), - Id.fromString(subnetID), - Id.fromString(assetID), - new BigIntPr(initialSupply), - new BigIntPr(maximumSupply), - new BigIntPr(minConsumptionRate), - new BigIntPr(maxConsumptionRate), - new BigIntPr(minValidatorStake), - new BigIntPr(maxValidatorStake), - new Int(minStakeDuration), - new Int(maxStakeDuration), - new Int(minDelegationFee), - new BigIntPr(minDelegatorStake), - new Byte(hexToBuffer(maxValidatorWeightFactor.toString(16))), - new Int(uptimeRequirement), - Input.fromNative(subnetAuth), - ), - inputUTXOs, - addressMaps, - ); -} - -/** - * @param context - * @param utxos list of utxos to choose from - * @param fromAddressesBytes used for filtering utxos - * @param subnetID ID of the subnet - * @param subnetAuth specifies indices of existing subnet owners - * @param subnetOwners The new owner addresses - * @param options used for filtering utxos - * @param threshold Opional. The number of signatures required to spend the funds in the resultant reward UTXO. Default 1. - * @param locktime Optional. The locktime field created in the resulting reward outputs - * @returns UnsignedTx containing a TransferSubnetOwnershipTx - */ -export function newTransferSubnetOwnershipTx( - context: Context, - utxos: Utxo[], - fromAddressesBytes: Uint8Array[], - subnetID: string, - subnetAuth: number[], - subnetOwners: Uint8Array[], - options?: SpendOptions, - threshold = 1, - locktime = 0n, -) { - const defaultedOptions = defaultSpendOptions(fromAddressesBytes, options); - - const { inputs, addressMaps, changeOutputs, inputUTXOs } = calculateUTXOSpend( - new Map([[context.avaxAssetID, context.baseTxFee]]), - undefined, - utxos, - addressesFromBytes(fromAddressesBytes), - defaultedOptions, - [useUnlockedUTXOs], - ); - - return new UnsignedTx( - new TransferSubnetOwnershipTx( - AvaxBaseTx.fromNative( - context.networkID, - context.pBlockchainID, - changeOutputs, - inputs, - defaultedOptions.memo, - ), - Id.fromString(subnetID), - Input.fromNative(subnetAuth), - OutputOwners.fromNative(subnetOwners, locktime, threshold), - ), - inputUTXOs, - addressMaps, - ); -} diff --git a/src/vms/pvm/index.ts b/src/vms/pvm/index.ts index 73091b284..bb14edd96 100644 --- a/src/vms/pvm/index.ts +++ b/src/vms/pvm/index.ts @@ -1,7 +1,9 @@ -export * from './builder'; +export * from './etna-builder'; export * from './models'; export * from './api'; export * from './txs/fee'; -// Exposed Etna builder functions under `e` namespace +/** + * @deprecated Exposed Etna builder functions under `e` namespace + */ export * as e from './etna-builder'; From 438133a8e7388a1b04d61dbcd77688d941354c76 Mon Sep 17 00:00:00 2001 From: Fumiko Date: Thu, 30 Jan 2025 14:32:33 -0700 Subject: [PATCH 2/8] fix: (info) WIP get rid of info.getTxFee --- src/fixtures/context.ts | 13 +- src/info/api.ts | 19 -- src/info/model.ts | 20 -- src/utils/getBurnedAmountByTx.test.ts | 313 +++--------------- .../validateStaticBurnedAmount.test.ts | 243 ++------------ .../validateStaticBurnedAmount.ts | 58 +--- src/vms/avm/api.ts | 5 + src/vms/avm/builder.test.ts | 55 +-- src/vms/avm/models.ts | 5 + src/vms/context/context.ts | 23 +- src/vms/context/model.ts | 10 +- 11 files changed, 142 insertions(+), 622 deletions(-) diff --git a/src/fixtures/context.ts b/src/fixtures/context.ts index 362805fb2..ebe8fc62d 100644 --- a/src/fixtures/context.ts +++ b/src/fixtures/context.ts @@ -8,13 +8,6 @@ export const testContext: Context = { avaxAssetID: 'FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z', baseTxFee: 1000000n, createAssetTxFee: 10000000n, - createSubnetTxFee: 1000000000n, - transformSubnetTxFee: 10000000000n, - createBlockchainTxFee: 1000000000n, - addPrimaryNetworkValidatorFee: 0n, - addPrimaryNetworkDelegatorFee: 0n, - addSubnetValidatorFee: 1000000n, - addSubnetDelegatorFee: 1000000n, networkID: 1, hrp: 'avax', platformFeeConfig: { @@ -30,4 +23,10 @@ export const testContext: Context = { minPrice: 1n, excessConversionConstant: 5_000n, }, + feeState: { + capacity: 1000000n, + excess: 100000n, + price: 1n, + timestamp: '2024-12-16T17:19:07Z', + }, }; diff --git a/src/info/api.ts b/src/info/api.ts index 4ebd8b886..6c56d54ec 100644 --- a/src/info/api.ts +++ b/src/info/api.ts @@ -8,7 +8,6 @@ import type { GetNodeIpResponse, GetNodeVersionReply, GetPeersResponse, - GetTxFeeResponse, isBootstrapped, UptimeResponse, GetUpgradesInfoResponse, @@ -50,24 +49,6 @@ export class InfoApi extends Api { isBootstrapped(chain: string): Promise { return this.callRpc('peers', { chain }); } - /** - * @link https://docs.avax.network/apis/avalanchego/apis/info#infogettxfee - */ - async getTxFee(): Promise { - const resp = await this.callRpc('getTxFee'); - - return { - txFee: BigInt(resp.txFee), - createAssetTxFee: BigInt(resp.createAssetTxFee), - createSubnetTxFee: BigInt(resp.createSubnetTxFee), - transformSubnetTxFee: BigInt(resp.transformSubnetTxFee), - createBlockchainTxFee: BigInt(resp.createBlockchainTxFee), - addPrimaryNetworkValidatorFee: BigInt(resp.addPrimaryNetworkValidatorFee), - addPrimaryNetworkDelegatorFee: BigInt(resp.addPrimaryNetworkDelegatorFee), - addSubnetValidatorFee: BigInt(resp.addSubnetValidatorFee), - addSubnetDelegatorFee: BigInt(resp.addSubnetDelegatorFee), - }; - } uptime(): Promise { return this.callRpc('uptime'); diff --git a/src/info/model.ts b/src/info/model.ts index bb1c417d2..b2b4cd306 100644 --- a/src/info/model.ts +++ b/src/info/model.ts @@ -21,26 +21,6 @@ export type GetPeersResponse = { peers: Peer[]; }; -export type GetTxFeeApiResponse = { - txFee: string; - creationTxFee: string; - createAssetTxFee: string; - createSubnetTxFee: string; - createBlockchainTxFee: string; -}; - -export type GetTxFeeResponse = { - txFee: bigint; - createAssetTxFee: bigint; - createSubnetTxFee: bigint; - transformSubnetTxFee: bigint; - createBlockchainTxFee: bigint; - addPrimaryNetworkValidatorFee: bigint; - addPrimaryNetworkDelegatorFee: bigint; - addSubnetValidatorFee: bigint; - addSubnetDelegatorFee: bigint; -}; - export type UptimeResponse = { rewardingStakePercentage: string; weightedAveragePercentage: string; diff --git a/src/utils/getBurnedAmountByTx.test.ts b/src/utils/getBurnedAmountByTx.test.ts index d80978b5e..e3473f2a7 100644 --- a/src/utils/getBurnedAmountByTx.test.ts +++ b/src/utils/getBurnedAmountByTx.test.ts @@ -20,16 +20,12 @@ import { import { newExportTx as pvmExportTx, newImportTx as pvmImportTx, - newAddValidatorTx, - newAddDelegatorTx, newCreateSubnetTx, - newCreateBlockchainTx, newAddSubnetValidatorTx, } from '../vms/pvm'; import type { EVMTx } from '../serializable/evm/abstractTx'; import { testUTXOID1, testUTXOID2 } from '../fixtures/transactions'; import { costCorethTx } from './costs'; -import { StakeableLockOut } from '../serializable/pvm'; import { newConvertSubnetToL1Tx, newIncreaseL1ValidatorBalanceTx, @@ -59,26 +55,6 @@ const getUtxoMock = ( ), ); -const getStakeableLockoutUtxoMock = ( - utxoId: Id, - lockTime: bigint, - amount = 100000000n, - assetId: string = testContext.avaxAssetID, -) => - new Utxo( - new UTXOID(utxoId, new Int(0)), - Id.fromString(assetId), - new StakeableLockOut( - new BigIntPr(lockTime), - new TransferOutput( - new BigIntPr(amount), - new OutputOwners(new BigIntPr(0n), new Int(1), [ - Address.fromBytes(testAddress1)[0], - ]), - ), - ), - ); - const getOutputMock = ( amount = 100000000n, assetId: string = testContext.avaxAssetID, @@ -318,11 +294,14 @@ describe('getBurnedAmountByTx', () => { const output = getOutputMock(100000000n); const tx = pvmExportTx( + { + destinationChainId: 'C', + fromAddressesBytes: [testAddress1], + utxos: [utxo1, utxo2], + outputs: [output], + feeState: testContext.feeState, + }, testContext, - 'C', - [testAddress1], - [utxo1, utxo2], - [output], ).getTx() as AvaxTx; const amounts = getBurnedAmountByTx(tx, testContext); @@ -340,11 +319,14 @@ describe('getBurnedAmountByTx', () => { const output3 = getOutputMock(10000000n); const tx = pvmExportTx( + { + destinationChainId: 'C', + fromAddressesBytes: [testAddress1], + utxos: [utxo], + outputs: [output1, output2, output3], + feeState: testContext.feeState, + }, testContext, - 'C', - [testAddress1], - [utxo], - [output1, output2, output3], ).getTx() as AvaxTx; const amounts = getBurnedAmountByTx(tx, testContext); @@ -363,11 +345,14 @@ describe('getBurnedAmountByTx', () => { const output3 = getOutputMock(10000000n); const tx = pvmExportTx( + { + destinationChainId: 'C', + fromAddressesBytes: [testAddress1], + utxos: [utxo1, utxo2], + outputs: [output1, output2, output3], + feeState: testContext.feeState, + }, testContext, - 'C', - [testAddress1], - [utxo1, utxo2], - [output1, output2, output3], ).getTx() as AvaxTx; const amounts = getBurnedAmountByTx(tx, testContext); @@ -385,11 +370,14 @@ describe('getBurnedAmountByTx', () => { const utxo2 = getUtxoMock(testUTXOID2, 60000000n); const tx = pvmImportTx( + { + sourceChainId: 'C', + utxos: [utxo1, utxo2], + toAddressesBytes: [testAddress2], + fromAddressesBytes: [testAddress1], + feeState: testContext.feeState, + }, testContext, - 'C', - [utxo1, utxo2], - [testAddress2], - [testAddress1], ).getTx() as AvaxTx; const amounts = getBurnedAmountByTx(tx, testContext); @@ -400,189 +388,6 @@ describe('getBurnedAmountByTx', () => { }); }); - describe('add validator tx', () => { - it('calculates the burned amount of add validator tx correctly', () => { - // 110000000n total input -> 100000000n stake + 0 fee -> 10000000n change - const weight = 100000000n; - const utxo1 = getUtxoMock(testUTXOID1, 50000000n); - const utxo2 = getUtxoMock(testUTXOID2, 60000000n); - - const tx = newAddValidatorTx( - testContext, - [utxo1, utxo2], - [testAddress1], - nodeId().toString(), - 0n, - 1n, - weight, - [testAddress1], - 3, - ).getTx() as AvaxTx; - - const amounts = getBurnedAmountByTx(tx, testContext); - expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.addPrimaryNetworkValidatorFee, - ); - }); - - it('calculates the burned amount of add validator tx correctly (stakeable lock out)', () => { - // 110000000n total input -> 100000000n stake + 0 fee -> 10000000n change - const weight = 100000000n; - const lockTime = - BigInt(Math.floor(new Date().getTime() / 1000)) + 10000n; - const utxo1 = getStakeableLockoutUtxoMock( - testUTXOID1, - lockTime, - 50000000n, - ); - const utxo2 = getStakeableLockoutUtxoMock( - testUTXOID2, - lockTime, - 60000000n, - ); - - const tx = newAddValidatorTx( - testContext, - [utxo1, utxo2], - [testAddress1], - nodeId().toString(), - 0n, - 1n, - weight, - [testAddress1], - 3, - ).getTx() as AvaxTx; - - const amounts = getBurnedAmountByTx(tx, testContext); - expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.addPrimaryNetworkValidatorFee, - ); - }); - - it('calculates the burned amount of add validator tx correctly (hybrid)', () => { - // 110000000n total input -> 100000000n stake + 0 fee -> 10000000n change - const weight = 100000000n; - const lockTime = - BigInt(Math.floor(new Date().getTime() / 1000)) + 10000n; - const utxo1 = getStakeableLockoutUtxoMock( - testUTXOID1, - lockTime, - 50000000n, - ); - const utxo2 = getUtxoMock(testUTXOID2, 60000000n); - - const tx = newAddValidatorTx( - testContext, - [utxo1, utxo2], - [testAddress1], - nodeId().toString(), - 0n, - 1n, - weight, - [testAddress1], - 3, - ).getTx() as AvaxTx; - - const amounts = getBurnedAmountByTx(tx, testContext); - expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.addPrimaryNetworkValidatorFee, - ); - }); - }); - - describe('add delegator tx', () => { - it('calculates the burned amount of add delegator tx correctly', () => { - // 110000000n total input -> 100000000n stake + 0 fee -> 10000000n change - const weight = 100000000n; - const utxo1 = getUtxoMock(testUTXOID1, 50000000n); - const utxo2 = getUtxoMock(testUTXOID2, 60000000n); - - const tx = newAddDelegatorTx( - testContext, - [utxo1, utxo2], - [testAddress1], - nodeId().toString(), - 0n, - 1n, - weight, - [testAddress1], - ).getTx() as AvaxTx; - - const amounts = getBurnedAmountByTx(tx, testContext); - expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.addPrimaryNetworkDelegatorFee, - ); - }); - - it('calculates the burned amount of add delegator tx correctly (stakeable lock out)', () => { - // 110000000n total input -> 100000000n stake + 0 fee -> 10000000n change - const weight = 100000000n; - const lockTime = - BigInt(Math.floor(new Date().getTime() / 1000)) + 10000n; - const utxo1 = getStakeableLockoutUtxoMock( - testUTXOID1, - lockTime, - 50000000n, - ); - const utxo2 = getStakeableLockoutUtxoMock( - testUTXOID2, - lockTime, - 60000000n, - ); - - const tx = newAddDelegatorTx( - testContext, - [utxo1, utxo2], - [testAddress1], - nodeId().toString(), - 0n, - 1n, - weight, - [testAddress1], - ).getTx() as AvaxTx; - - const amounts = getBurnedAmountByTx(tx, testContext); - expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.addPrimaryNetworkDelegatorFee, - ); - }); - - it('calculates the burned amount of add delegator tx correctly (hybrid)', () => { - // 110000000n total input -> 100000000n stake + 0 fee -> 10000000n change - const weight = 100000000n; - const lockTime = - BigInt(Math.floor(new Date().getTime() / 1000)) + 10000n; - const utxo1 = getStakeableLockoutUtxoMock( - testUTXOID1, - lockTime, - 50000000n, - ); - const utxo2 = getUtxoMock(testUTXOID2, 60000000n); - - const tx = newAddDelegatorTx( - testContext, - [utxo1, utxo2], - [testAddress1], - nodeId().toString(), - 0n, - 1n, - weight, - [testAddress1], - ).getTx() as AvaxTx; - - const amounts = getBurnedAmountByTx(tx, testContext); - expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.addPrimaryNetworkDelegatorFee, - ); - }); - }); - describe('create subnet tx', () => { it('calculates the burned amount of create subnet tx correctly', () => { // 1100000000n total input -> 1000000000n fee -> 100000000n change @@ -590,43 +395,18 @@ describe('getBurnedAmountByTx', () => { const utxo2 = getUtxoMock(testUTXOID2, 600000000n); const tx = newCreateSubnetTx( + { + utxos: [utxo1, utxo2], + fromAddressesBytes: [testAddress1], + subnetOwners: [testAddress1], + feeState: testContext.feeState, + }, testContext, - [utxo1, utxo2], - [testAddress1], - [testAddress1], - ).getTx() as AvaxTx; - - const amounts = getBurnedAmountByTx(tx, testContext); - expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.createSubnetTxFee, - ); - }); - }); - - describe('create blockchain', () => { - it('calculates the burned amount of create blockchain tx correctly', () => { - // 1100000000n total input -> 1000000000n fee -> 100000000n change - const utxo1 = getUtxoMock(testUTXOID1, 500000000n); - const utxo2 = getUtxoMock(testUTXOID2, 600000000n); - - const tx = newCreateBlockchainTx( - testContext, - [utxo1, utxo2], - [testAddress1], - 'subnet', - 'chain', - 'vm', - ['fx1', 'fx2'], - {}, - [0], ).getTx() as AvaxTx; const amounts = getBurnedAmountByTx(tx, testContext); expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.createBlockchainTxFee, - ); + expect(amounts.get(testContext.avaxAssetID)).toEqual(1000000000n); }); }); @@ -638,22 +418,23 @@ describe('getBurnedAmountByTx', () => { const utxo2 = getUtxoMock(testUTXOID2, 600000n); const tx = newAddSubnetValidatorTx( + { + end: 1n, + feeState: testContext.feeState, + fromAddressesBytes: [testAddress1], + nodeId: nodeId().toString(), + start: 0n, + subnetAuth: [0], + subnetId: 'subnet', + utxos: [utxo1, utxo2], + weight, + }, testContext, - [utxo1, utxo2], - [testAddress1], - nodeId().toString(), - 0n, - 1n, - weight, - 'subnet', - [0], ).getTx() as AvaxTx; const amounts = getBurnedAmountByTx(tx, testContext); expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.addSubnetValidatorFee, - ); + expect(amounts.get(testContext.avaxAssetID)).toEqual(1000000n); }); }); diff --git a/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts b/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts index 5efb0b54b..4f5f71057 100644 --- a/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts +++ b/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts @@ -16,25 +16,12 @@ import { newBaseTx as pvmBaseTx, newExportTx as pvmExportTx, newImportTx as pvmImportTx, - newAddValidatorTx, - newAddDelegatorTx, - newCreateSubnetTx, - newCreateBlockchainTx, - newAddSubnetValidatorTx, - newTransformSubnetTx, - newAddPermissionlessValidatorTx, - newAddPermissionlessDelegatorTx, newRemoveSubnetValidatorTx, newTransferSubnetOwnershipTx, } from '../../vms/pvm'; import { TransferableOutput } from '../../serializable'; import { nodeId } from '../../fixtures/common'; import { testSubnetId } from '../../fixtures/transactions'; -import { PrimaryNetworkID } from '../../constants/networkIDs'; -import { - blsPublicKeyBytes, - blsSignatureBytes, -} from '../../fixtures/primitives'; import { validateStaticBurnedAmount } from './validateStaticBurnedAmount'; const utxoMock = new Utxo( @@ -95,229 +82,71 @@ describe('validateStaticBurnedAmount', () => { { name: 'base tx on P', unsignedTx: pvmBaseTx( + { + fromAddressesBytes: [testAddress1], + utxos: [utxoMock], + outputs: [outputMock], + feeState: testContext.feeState, + }, testContext, - [testAddress1], - [utxoMock], - [outputMock], ), correctBurnedAmount: testContext.baseTxFee, }, { name: 'export from P', unsignedTx: pvmExportTx( + { + destinationChainId: 'C', + fromAddressesBytes: [testAddress1], + utxos: [utxoMock], + outputs: [outputMock], + feeState: testContext.feeState, + }, testContext, - 'C', - [testAddress1], - [utxoMock], - [outputMock], ), correctBurnedAmount: testContext.baseTxFee, }, { name: 'import to P', unsignedTx: pvmImportTx( + { + sourceChainId: 'C', + utxos: [utxoMock], + toAddressesBytes: [testAddress2], + fromAddressesBytes: [testAddress1], + feeState: testContext.feeState, + }, testContext, - 'C', - [utxoMock], - [testAddress2], - [testAddress1], ), correctBurnedAmount: testContext.baseTxFee, }, - { - name: 'add validator', - unsignedTx: newAddValidatorTx( - testContext, - [utxoMock], - [testAddress1], - nodeId().toString(), - 0n, - 1n, - 2n, - [testAddress1], - 3, - ), - correctBurnedAmount: testContext.addPrimaryNetworkValidatorFee, - }, - { - name: 'add delegator', - unsignedTx: newAddDelegatorTx( - testContext, - [utxoMock], - [testAddress1], - nodeId().toString(), - 0n, - 1n, - 2n, - [testAddress1], - ), - correctBurnedAmount: testContext.addPrimaryNetworkDelegatorFee, - }, - { - name: 'create subnet', - unsignedTx: newCreateSubnetTx( - testContext, - [utxoMock], - [testAddress1], - [testAddress1], - ), - correctBurnedAmount: testContext.createSubnetTxFee, - }, - { - name: 'create blockchain', - unsignedTx: newCreateBlockchainTx( - testContext, - [utxoMock], - [testAddress1], - 'subnet', - 'chain', - 'vm', - ['fx1', 'fx2'], - {}, - [0], - ), - correctBurnedAmount: testContext.createBlockchainTxFee, - }, - { - name: 'add subnet validator', - unsignedTx: newAddSubnetValidatorTx( - testContext, - [utxoMock], - [testAddress1], - nodeId().toString(), - 0n, - 1n, - 2n, - 'subnet', - [0], - ), - correctBurnedAmount: testContext.addSubnetValidatorFee, - }, { name: 'remove subnet validator', unsignedTx: newRemoveSubnetValidatorTx( + { + utxos: [utxoMock], + fromAddressesBytes: [testAddress1], + nodeId: nodeId().toString(), + subnetId: Id.fromHex(testSubnetId).toString(), + subnetAuth: [0], + feeState: testContext.feeState, + }, testContext, - [utxoMock], - [testAddress1], - nodeId().toString(), - Id.fromHex(testSubnetId).toString(), - [0], ), correctBurnedAmount: testContext.baseTxFee, }, - { - name: 'transform subnet', - unsignedTx: newTransformSubnetTx( - testContext, - [utxoMock], - [testAddress1], - Id.fromHex(testSubnetId).toString(), - '123456789ABC', - 1n, - 2n, - 3n, - 4n, - 5n, - 6n, - 1, - 2, - 3, - 4n, - 5, - 6, - [0, 2], - ), - correctBurnedAmount: testContext.transformSubnetTxFee, - }, - { - name: 'add permissionless validator (primary network)', - unsignedTx: newAddPermissionlessValidatorTx( - testContext, - [utxoMock], - [testAddress1], - nodeId().toString(), - PrimaryNetworkID.toString(), - 0n, - 120n, - 1800000n, - [], - [], - 1, - {}, - 1, - 0n, - blsPublicKeyBytes(), - blsSignatureBytes(), - ), - correctBurnedAmount: testContext.addPrimaryNetworkValidatorFee, - }, - { - name: 'add permissionless validator (subnet)', - unsignedTx: newAddPermissionlessValidatorTx( - testContext, - [utxoMock], - [testAddress1], - nodeId().toString(), - Id.fromHex(testSubnetId).toString(), - 0n, - 120n, - 1800000n, - [], - [], - 1, - {}, - 1, - 0n, - blsPublicKeyBytes(), - blsSignatureBytes(), - ), - correctBurnedAmount: testContext.addSubnetValidatorFee, - }, - { - name: 'add permissionless delegator (primary network)', - unsignedTx: newAddPermissionlessDelegatorTx( - testContext, - [utxoMock], - [testAddress1], - nodeId().toString(), - PrimaryNetworkID.toString(), - 0n, - 120n, - 1800000n, - [], - {}, - 1, - 0n, - ), - correctBurnedAmount: testContext.addPrimaryNetworkDelegatorFee, - }, - { - name: 'add permissionless delegator (subnet)', - unsignedTx: newAddPermissionlessDelegatorTx( - testContext, - [utxoMock], - [testAddress1], - nodeId().toString(), - Id.fromHex(testSubnetId).toString(), - 0n, - 120n, - 1800000n, - [], - {}, - 1, - 0n, - ), - correctBurnedAmount: testContext.addSubnetDelegatorFee, - }, { name: 'transfer subnet ownership', unsignedTx: newTransferSubnetOwnershipTx( + { + utxos: [utxoMock], + fromAddressesBytes: [testAddress1], + subnetId: Id.fromHex(testSubnetId).toString(), + subnetAuth: [0, 2], + subnetOwners: [testAddress2], + feeState: testContext.feeState, + }, testContext, - [utxoMock], - [testAddress1], - Id.fromHex(testSubnetId).toString(), - [0, 2], - [testAddress2], ), correctBurnedAmount: testContext.baseTxFee, }, diff --git a/src/utils/validateBurnedAmount/validateStaticBurnedAmount.ts b/src/utils/validateBurnedAmount/validateStaticBurnedAmount.ts index ce272ed86..8dad548e9 100644 --- a/src/utils/validateBurnedAmount/validateStaticBurnedAmount.ts +++ b/src/utils/validateBurnedAmount/validateStaticBurnedAmount.ts @@ -1,18 +1,10 @@ import type { Context } from '../../vms/context/model'; import { - isAddDelegatorTx, - isAddPermissionlessDelegatorTx, - isAddPermissionlessValidatorTx, - isAddSubnetValidatorTx, - isAddValidatorTx, - isCreateChainTx, - isCreateSubnetTx, isPvmBaseTx, isExportTx as isPvmExportTx, isImportTx as isPvmImportTx, isRemoveSubnetValidatorTx, isTransferSubnetOwnershipTx, - isTransformSubnetTx, } from '../../serializable/pvm'; import type { UnsignedTx } from '../../vms/common'; import { @@ -20,7 +12,6 @@ import { isExportTx as isAvmExportTx, isImportTx as isAvmImportTx, } from '../../serializable/avm'; -import { PrimaryNetworkID } from '../../constants/networkIDs'; /** * Validate static burned amount for avalanche x/p transactions @@ -42,53 +33,6 @@ export const validateStaticBurnedAmount = ({ }): { isValid: boolean; txFee: bigint } => { const tx = unsignedTx.getTx(); - if (isAddValidatorTx(tx)) { - return validate(burnedAmount, context.addPrimaryNetworkValidatorFee); - } - - if (isAddDelegatorTx(tx)) { - return validate(burnedAmount, context.addPrimaryNetworkDelegatorFee); - } - - if (isCreateSubnetTx(tx)) { - return validate(burnedAmount, context.createSubnetTxFee); - } - - if (isCreateChainTx(tx)) { - return validate(burnedAmount, context.createBlockchainTxFee); - } - - if (isAddSubnetValidatorTx(tx)) { - return validate(burnedAmount, context.addSubnetValidatorFee); - } - - if (isTransformSubnetTx(tx)) { - return validate(burnedAmount, context.transformSubnetTxFee); - } - - if (isAddPermissionlessValidatorTx(tx)) { - const isPrimarySubnet = - tx.subnetValidator.subnetId.toString() === PrimaryNetworkID.toString(); - - return validate( - burnedAmount, - isPrimarySubnet - ? context.addPrimaryNetworkValidatorFee - : context.addSubnetValidatorFee, - ); - } - - if (isAddPermissionlessDelegatorTx(tx)) { - const isPrimarySubnet = - tx.subnetValidator.subnetId.toString() === PrimaryNetworkID.toString(); - return validate( - burnedAmount, - isPrimarySubnet - ? context.addPrimaryNetworkDelegatorFee - : context.addSubnetDelegatorFee, - ); - } - if ( isAvmBaseTx(tx) || isPvmBaseTx(tx) || @@ -102,7 +46,7 @@ export const validateStaticBurnedAmount = ({ return validate(burnedAmount, context.baseTxFee); } - throw new Error(`tx type is not supported`); + throw new Error('tx type is not supported'); }; const validate = (burnedAmount: bigint, expectedAmount: bigint) => ({ diff --git a/src/vms/avm/api.ts b/src/vms/avm/api.ts index dab7d1c2d..1bfcda6b4 100644 --- a/src/vms/avm/api.ts +++ b/src/vms/avm/api.ts @@ -5,6 +5,7 @@ import type { BuildGenesisResponse, GetAllBalancesParams, GetAllBalancesResponse, + GetTxFeeResponse, } from './models'; export class AVMApi extends AvaxApi { @@ -33,4 +34,8 @@ export class AVMApi extends AvaxApi { getAllBalancesParams, ); } + + getTxFee(): Promise { + return this.callRpc('getTxFee'); + } } diff --git a/src/vms/avm/builder.test.ts b/src/vms/avm/builder.test.ts index 1c78ef9ce..bb6bc68b4 100644 --- a/src/vms/avm/builder.test.ts +++ b/src/vms/avm/builder.test.ts @@ -33,11 +33,14 @@ describe('AVMBuilder', () => { it('importTx', async () => { const toAddress = hexToBuffer('0x5432112345123451234512'); const tx = newImportTx( + { + sourceChainId: testContext.cBlockchainID, + utxos, + toAddressesBytes: [toAddress], + fromAddressesBytes: [testOwnerXAddress.toBytes()], + feeState: testContext.feeState, + }, testContext, - testContext.cBlockchainID, - utxos, - [toAddress], - [testOwnerXAddress.toBytes()], ); const importTx = tx.getTx() as ImportTx; @@ -60,11 +63,14 @@ describe('AVMBuilder', () => { ); const tx = newImportTx( + { + sourceChainId: testContext.cBlockchainID, + utxos, + toAddressesBytes: [toAddress], + fromAddressesBytes: [testOwnerXAddress.toBytes()], + feeState: testContext.feeState, + }, testContext, - testContext.cBlockchainID, - utxos, - [toAddress], - [testOwnerXAddress.toBytes()], ); const importTx = tx.getTx() as ImportTx; @@ -84,11 +90,14 @@ describe('AVMBuilder', () => { expect(() => newImportTx( + { + sourceChainId: testContext.cBlockchainID, + utxos, + toAddressesBytes: [toAddress], + fromAddressesBytes: [testOwnerXAddress.toBytes()], + feeState: testContext.feeState, + }, testContext, - testContext.cBlockchainID, - utxos, - [toAddress], - [testOwnerXAddress.toBytes()], ), ).toThrow(); }); @@ -101,11 +110,14 @@ describe('AVMBuilder', () => { [toAddress], ); const tx = newExportTx( + { + destinationChainId: testContext.cBlockchainID, + fromAddressesBytes: [testOwnerXAddress.toBytes()], + utxos, + outputs: [tnsOut], + feeState: testContext.feeState, + }, testContext, - testContext.cBlockchainID, - [testOwnerXAddress.toBytes()], - utxos, - [tnsOut], ); const exportTx = tx.getTx() as ExportTx; expect(exportTx.outs as TransferableOutput[]).toEqual([tnsOut]); @@ -141,11 +153,14 @@ describe('AVMBuilder', () => { utxos.pop(); expect(() => newExportTx( + { + destinationChainId: testContext.cBlockchainID, + fromAddressesBytes: [testOwnerXAddress.toBytes()], + utxos, + outputs: [tnsOut], + feeState: testContext.feeState, + }, testContext, - testContext.cBlockchainID, - [testOwnerXAddress.toBytes()], - utxos, - [tnsOut], ), ).toThrow(); }); diff --git a/src/vms/avm/models.ts b/src/vms/avm/models.ts index 888397c24..affefd5af 100644 --- a/src/vms/avm/models.ts +++ b/src/vms/avm/models.ts @@ -35,3 +35,8 @@ export interface GetAddressTxsResponse { txIDs: string[]; cursor: bigint; } + +export interface GetTxFeeResponse { + txFee: string; + createAssetTxFee: string; +} diff --git a/src/vms/context/context.ts b/src/vms/context/context.ts index 5403a31f4..2b1c82402 100644 --- a/src/vms/context/context.ts +++ b/src/vms/context/context.ts @@ -17,17 +17,10 @@ export const getContextFromURI = async ( assetDescription, ); const info = new InfoApi(baseURL); - const { - txFee: baseTxFee, - createAssetTxFee, - createSubnetTxFee, - transformSubnetTxFee, - createBlockchainTxFee, - addPrimaryNetworkValidatorFee, - addPrimaryNetworkDelegatorFee, - addSubnetValidatorFee, - addSubnetDelegatorFee, - } = await info.getTxFee(); + const { txFee: baseTxFee, createAssetTxFee } = await xChainApi.getTxFee(); + + const feeState = await pChainApi.getFeeState(); + const { blockchainID: xBlockchainID } = await info.getBlockchainId('X'); const { blockchainID: pBlockchainID } = await info.getBlockchainId('P'); const { blockchainID: cBlockchainID } = await info.getBlockchainId('C'); @@ -44,15 +37,9 @@ export const getContextFromURI = async ( avaxAssetID, baseTxFee, createAssetTxFee, - createSubnetTxFee, - transformSubnetTxFee, - createBlockchainTxFee, - addPrimaryNetworkValidatorFee, - addPrimaryNetworkDelegatorFee, - addSubnetValidatorFee, - addSubnetDelegatorFee, networkID, hrp: getHRP(networkID), platformFeeConfig, + feeState, }); }; diff --git a/src/vms/context/model.ts b/src/vms/context/model.ts index bb807ff7a..7b935f102 100644 --- a/src/vms/context/model.ts +++ b/src/vms/context/model.ts @@ -1,4 +1,4 @@ -import type { FeeConfig } from '../pvm'; +import type { FeeConfig, FeeState } from '../pvm'; export type Context = { readonly networkID: number; @@ -9,14 +9,8 @@ export type Context = { readonly avaxAssetID: string; readonly baseTxFee: bigint; readonly createAssetTxFee: bigint; - readonly createSubnetTxFee: bigint; - readonly transformSubnetTxFee: bigint; - readonly createBlockchainTxFee: bigint; - readonly addPrimaryNetworkValidatorFee: bigint; - readonly addPrimaryNetworkDelegatorFee: bigint; - readonly addSubnetValidatorFee: bigint; - readonly addSubnetDelegatorFee: bigint; // Post Etna readonly platformFeeConfig: FeeConfig; + readonly feeState: FeeState; }; From 6d1ac78a99a3b649f650c68e9e440b7c9f0f507a Mon Sep 17 00:00:00 2001 From: Fumiko Date: Fri, 31 Jan 2025 15:57:32 -0700 Subject: [PATCH 3/8] fix: (tests) Fix tests --- src/utils/getBurnedAmountByTx.test.ts | 109 +++++++++++----- src/vms/avm/builder.test.ts | 37 +++++- src/vms/pvm/etna-builder/builder.test.ts | 114 +---------------- .../pvm/etna-builder/utils/feeForTesting.ts | 120 ++++++++++++++++++ 4 files changed, 230 insertions(+), 150 deletions(-) create mode 100644 src/vms/pvm/etna-builder/utils/feeForTesting.ts diff --git a/src/utils/getBurnedAmountByTx.test.ts b/src/utils/getBurnedAmountByTx.test.ts index e3473f2a7..252a911df 100644 --- a/src/utils/getBurnedAmountByTx.test.ts +++ b/src/utils/getBurnedAmountByTx.test.ts @@ -38,6 +38,13 @@ import { stringPr, warpMessageBytes, } from '../fixtures/primitives'; +import { checkFeeIsCorrect } from '../vms/pvm/etna-builder/utils/feeForTesting'; +import type { + AddSubnetValidatorTx, + CreateSubnetTx, + ExportTx, + ImportTx, +} from '../serializable/pvm'; const getUtxoMock = ( utxoId: Id, @@ -288,12 +295,11 @@ describe('getBurnedAmountByTx', () => { describe('P chain transactions', () => { describe('export tx', () => { it('calculates the burned amount of export tx correctly (multiple inputs)', () => { - // 110000000n total input -> 100000000n ouput + 1000000n fee -> 9000000n change const utxo1 = getUtxoMock(testUTXOID1, 50000000n); const utxo2 = getUtxoMock(testUTXOID2, 60000000n); const output = getOutputMock(100000000n); - const tx = pvmExportTx( + const unsignedTx = pvmExportTx( { destinationChainId: 'C', fromAddressesBytes: [testAddress1], @@ -302,23 +308,29 @@ describe('getBurnedAmountByTx', () => { feeState: testContext.feeState, }, testContext, - ).getTx() as AvaxTx; + ); + const tx = unsignedTx.getTx() as ExportTx; + const { inputs, outputs } = tx.baseTx; const amounts = getBurnedAmountByTx(tx, testContext); + const [, , expectedFee] = checkFeeIsCorrect({ + unsignedTx, + inputs, + outputs, + feeState: testContext.feeState, + }); + expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.baseTxFee, - ); + expect(amounts.get(testContext.avaxAssetID)).toEqual(expectedFee); }); it('calculates the burned amount of export tx correctly (multiple outputs)', () => { - // 150000000n total input -> 140000000n ouput + 1000000n fee -> 9000000n change const utxo = getUtxoMock(testUTXOID1, 150000000n); const output1 = getOutputMock(100000000n); const output2 = getOutputMock(30000000n); const output3 = getOutputMock(10000000n); - const tx = pvmExportTx( + const unsignedTx = pvmExportTx( { destinationChainId: 'C', fromAddressesBytes: [testAddress1], @@ -327,24 +339,30 @@ describe('getBurnedAmountByTx', () => { feeState: testContext.feeState, }, testContext, - ).getTx() as AvaxTx; + ); + const tx = unsignedTx.getTx() as ExportTx; + const { inputs, outputs } = tx.baseTx; const amounts = getBurnedAmountByTx(tx, testContext); + const [, , expectedFee] = checkFeeIsCorrect({ + unsignedTx, + inputs, + outputs, + feeState: testContext.feeState, + }); + expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.baseTxFee, - ); + expect(amounts.get(testContext.avaxAssetID)).toEqual(expectedFee); }); it('calculates the burned amount of export tx correctly (multiple inputs and outputs)', () => { - // 150000000n total input -> 140000000n ouput + 1000000n fee -> 9000000n change const utxo1 = getUtxoMock(testUTXOID1, 70000000n); const utxo2 = getUtxoMock(testUTXOID1, 80000000n); const output1 = getOutputMock(100000000n); const output2 = getOutputMock(30000000n); const output3 = getOutputMock(10000000n); - const tx = pvmExportTx( + const unsignedTx = pvmExportTx( { destinationChainId: 'C', fromAddressesBytes: [testAddress1], @@ -353,23 +371,29 @@ describe('getBurnedAmountByTx', () => { feeState: testContext.feeState, }, testContext, - ).getTx() as AvaxTx; + ); + const tx = unsignedTx.getTx() as ExportTx; + const { inputs, outputs } = tx.baseTx; const amounts = getBurnedAmountByTx(tx, testContext); + const [, , expectedFee] = checkFeeIsCorrect({ + unsignedTx, + inputs, + outputs, + feeState: testContext.feeState, + }); + expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.baseTxFee, - ); + expect(amounts.get(testContext.avaxAssetID)).toEqual(expectedFee); }); }); describe('import tx', () => { it('calculates the burned amount of import tx correctly', () => { - // 110000000n total input -> 1000000n fee -> 9000000n change const utxo1 = getUtxoMock(testUTXOID1, 50000000n); const utxo2 = getUtxoMock(testUTXOID2, 60000000n); - const tx = pvmImportTx( + const unsignedTx = pvmImportTx( { sourceChainId: 'C', utxos: [utxo1, utxo2], @@ -378,23 +402,28 @@ describe('getBurnedAmountByTx', () => { feeState: testContext.feeState, }, testContext, - ).getTx() as AvaxTx; + ); + const tx = unsignedTx.getTx() as ImportTx; + const { inputs, outputs } = tx.baseTx; const amounts = getBurnedAmountByTx(tx, testContext); + const [, , expectedFee] = checkFeeIsCorrect({ + unsignedTx, + inputs, + outputs, + feeState: testContext.feeState, + }); expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.baseTxFee, - ); + expect(amounts.get(testContext.avaxAssetID)).toEqual(expectedFee); }); }); describe('create subnet tx', () => { it('calculates the burned amount of create subnet tx correctly', () => { - // 1100000000n total input -> 1000000000n fee -> 100000000n change const utxo1 = getUtxoMock(testUTXOID1, 500000000n); const utxo2 = getUtxoMock(testUTXOID2, 600000000n); - const tx = newCreateSubnetTx( + const unsignedTx = newCreateSubnetTx( { utxos: [utxo1, utxo2], fromAddressesBytes: [testAddress1], @@ -402,22 +431,29 @@ describe('getBurnedAmountByTx', () => { feeState: testContext.feeState, }, testContext, - ).getTx() as AvaxTx; + ); + const tx = unsignedTx.getTx() as CreateSubnetTx; + const { inputs, outputs } = tx.baseTx; const amounts = getBurnedAmountByTx(tx, testContext); + const [, , expectedFee] = checkFeeIsCorrect({ + unsignedTx, + inputs, + outputs, + feeState: testContext.feeState, + }); expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual(1000000000n); + expect(amounts.get(testContext.avaxAssetID)).toEqual(expectedFee); }); }); describe('add subnet validor tx', () => { it('calculates the burned amount of add subnet validor tx correctly', () => { - // 1100000n total input -> 0 stake + 1000000n fee -> 100000n change const weight = 10000000n; const utxo1 = getUtxoMock(testUTXOID1, 500000n); const utxo2 = getUtxoMock(testUTXOID2, 600000n); - const tx = newAddSubnetValidatorTx( + const unsignedTx = newAddSubnetValidatorTx( { end: 1n, feeState: testContext.feeState, @@ -430,11 +466,18 @@ describe('getBurnedAmountByTx', () => { weight, }, testContext, - ).getTx() as AvaxTx; - + ); + const tx = unsignedTx.getTx() as AddSubnetValidatorTx; + const { inputs, outputs } = tx.baseTx; const amounts = getBurnedAmountByTx(tx, testContext); + const [, , expectedFee] = checkFeeIsCorrect({ + unsignedTx, + inputs, + outputs, + feeState: testContext.feeState, + }); expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual(1000000n); + expect(amounts.get(testContext.avaxAssetID)).toEqual(expectedFee); }); }); diff --git a/src/vms/avm/builder.test.ts b/src/vms/avm/builder.test.ts index bb6bc68b4..a50d11002 100644 --- a/src/vms/avm/builder.test.ts +++ b/src/vms/avm/builder.test.ts @@ -24,6 +24,7 @@ import { BigIntPr, Int } from '../../serializable/primitives'; import { hexToBuffer } from '../../utils'; import { newExportTx, newImportTx } from '../pvm'; import { newBaseTx } from './builder'; +import { checkFeeIsCorrect } from '../pvm/etna-builder/utils/feeForTesting'; describe('AVMBuilder', () => { let utxos: Utxo[]; @@ -62,7 +63,7 @@ describe('AVMBuilder', () => { ), ); - const tx = newImportTx( + const unsignedTx = newImportTx( { sourceChainId: testContext.cBlockchainID, utxos, @@ -73,13 +74,28 @@ describe('AVMBuilder', () => { testContext, ); - const importTx = tx.getTx() as ImportTx; + const importTx = unsignedTx.getTx() as ImportTx; + const { inputs, outputs } = importTx.baseTx; + const [, expectedAmountConsumed, expectedFee] = checkFeeIsCorrect({ + unsignedTx, + inputs, + outputs, + feeState: testContext.feeState, + }); expect(importTx.ins).toHaveLength(1); expect(importTx.ins[0].assetId).toEqual(testAvaxAssetID); expect(Number(importTx.ins[0].amount())).toEqual(50 * 1e5); + + const expectedAmountConsumedOriginal = expectedAmountConsumed[ + testContext.avaxAssetID + ] + .slice(0, -1) //remove n + .replaceAll('_', ''); + const expectedAmountConsumedBigInt = BigInt(expectedAmountConsumedOriginal); + expect((importTx.baseTx.outputs as TransferableOutput[])[0].amount()).toBe( - BigInt(40 * 1e5), + expectedAmountConsumedBigInt - expectedFee, ); expect(importTx.ins[0].utxoID.ID()).toEqual(utxos[2].ID()); }); @@ -120,6 +136,19 @@ describe('AVMBuilder', () => { testContext, ); const exportTx = tx.getTx() as ExportTx; + const { inputs, outputs } = exportTx.baseTx; + const [, expectedAmountConsumed, expectedFee] = checkFeeIsCorrect({ + unsignedTx: tx, + inputs, + outputs, + feeState: testContext.feeState, + }); + const expectedAmountConsumedOriginal = expectedAmountConsumed[ + testContext.avaxAssetID + ] + .slice(0, -1) //remove n + .replaceAll('_', ''); + const expectedAmountConsumedBigInt = BigInt(expectedAmountConsumedOriginal); expect(exportTx.outs as TransferableOutput[]).toEqual([tnsOut]); expect(exportTx.baseTx.inputs as TransferableInput[]).toEqual([ new TransferableInput( @@ -136,7 +165,7 @@ describe('AVMBuilder', () => { new TransferableOutput( testAvaxAssetID, new TransferOutput( - new BigIntPr(44999000000n), + new BigIntPr(expectedAmountConsumedBigInt - expectedFee), OutputOwners.fromNative([testOwnerXAddress.toBytes()]), ), ), diff --git a/src/vms/pvm/etna-builder/builder.test.ts b/src/vms/pvm/etna-builder/builder.test.ts index df0ee6d8c..b38a29d6d 100644 --- a/src/vms/pvm/etna-builder/builder.test.ts +++ b/src/vms/pvm/etna-builder/builder.test.ts @@ -50,8 +50,6 @@ import { } from '../../../serializable/pvm'; import { BaseTx as AvaxBaseTx } from '../../../serializable/avax'; import { hexToBuffer } from '../../../utils'; -import type { UnsignedTx } from '../../common'; -import { calculateFee } from '../txs/fee/calculator'; import { newAddPermissionlessDelegatorTx, newAddPermissionlessValidatorTx, @@ -81,120 +79,10 @@ import { feeState as testFeeState, proofOfPossession, } from '../../../fixtures/pvm'; -import type { FeeState } from '../models'; import { L1Validator } from '../../../serializable/fxs/pvm/L1Validator'; import { PChainOwner } from '../../../serializable/fxs/pvm/pChainOwner'; -const addTransferableAmounts = ( - transferableItems: - | readonly TransferableOutput[] - | readonly TransferableInput[], -): Map => { - const amounts = new Map(); - - for (const transferable of transferableItems) { - const assetId = transferable.getAssetId(); - - amounts.set(assetId, (amounts.get(assetId) ?? 0n) + transferable.amount()); - } - - return amounts; -}; - -const addAmounts = (...amounts: Map[]): Map => { - const amount = new Map(); - - for (const m of amounts) { - for (const [assetID, value] of m) { - amount.set(assetID, (amount.get(assetID) ?? 0n) + value); - } - } - - return amount; -}; - -/** - * Given a bigint, returns a human-readable string of the value. - * - * @example - * ```ts - * formatBigIntToHumanReadable(123456789n); // '123_456_789n' - * formatBigIntToHumanReadable(1234567890n); // '1_234_567_890n' - * ``` - */ -const formatBigIntToHumanReadable = (value: bigint): string => { - const bigIntStr = value.toString(); - - return `${bigIntStr.replace(/\B(?=(\d{3})+(?!\d))/g, '_')}n`; -}; - -/** - * Calculates the required fee for the unsigned transaction - * and verifies that the burned amount is exactly the required fee. - */ -const checkFeeIsCorrect = ({ - unsignedTx, - inputs, - outputs, - feeState, - additionalInputs = [], - additionalOutputs = [], - additionalFee = 0n, -}: { - unsignedTx: UnsignedTx; - inputs: readonly TransferableInput[]; - outputs: readonly TransferableOutput[]; - feeState: FeeState; - additionalInputs?: readonly TransferableInput[]; - additionalOutputs?: readonly TransferableOutput[]; - additionalFee?: bigint; -}): [ - amountConsumed: Record, - expectedAmountConsumed: Record, - expectedFee: bigint, -] => { - const amountConsumed = addTransferableAmounts([ - ...inputs, - ...additionalInputs, - ]); - const amountProduced = addTransferableAmounts([ - ...outputs, - ...additionalOutputs, - ]); - - const expectedFee = calculateFee( - unsignedTx.getTx(), - testContext.platformFeeConfig.weights, - feeState.price, - ); - - const expectedAmountBurned = addAmounts( - new Map([[testAvaxAssetID.toString(), expectedFee + additionalFee]]), - ); - - const expectedAmountConsumed = addAmounts( - amountProduced, - expectedAmountBurned, - ); - - // Convert each map into a object with a stringified bigint value. - const safeExpectedAmountConsumed = Object.fromEntries( - [...expectedAmountConsumed].map(([k, v]) => [ - k, - formatBigIntToHumanReadable(v), - ]), - ); - - const safeAmountConsumed = Object.fromEntries( - [...amountConsumed].map(([k, v]) => [k, formatBigIntToHumanReadable(v)]), - ); - - return [ - safeAmountConsumed, - safeExpectedAmountConsumed, - expectedFee + additionalFee, - ]; -}; +import { checkFeeIsCorrect } from './utils/feeForTesting'; describe('./src/vms/pvm/etna-builder/builder.test.ts', () => { const nodeId = 'NodeID-2m38qc95mhHXtrhjyGbe7r2NhniqHHJRB'; diff --git a/src/vms/pvm/etna-builder/utils/feeForTesting.ts b/src/vms/pvm/etna-builder/utils/feeForTesting.ts new file mode 100644 index 000000000..7311c2be5 --- /dev/null +++ b/src/vms/pvm/etna-builder/utils/feeForTesting.ts @@ -0,0 +1,120 @@ +// This file is being used in test files to help figure out the fees. + +import type { + TransferableInput, + TransferableOutput, +} from '../../../../serializable'; +import type { UnsignedTx } from '../../../common'; +import { calculateFee } from '../../txs/fee/calculator'; +import type { FeeState } from '../../models'; +import { testContext } from '../../../../fixtures/context'; +import { testAvaxAssetID } from '../../../../fixtures/transactions'; +const addTransferableAmounts = ( + transferableItems: + | readonly TransferableOutput[] + | readonly TransferableInput[], +): Map => { + const amounts = new Map(); + + for (const transferable of transferableItems) { + const assetId = transferable.getAssetId(); + + amounts.set(assetId, (amounts.get(assetId) ?? 0n) + transferable.amount()); + } + + return amounts; +}; + +const addAmounts = (...amounts: Map[]): Map => { + const amount = new Map(); + + for (const m of amounts) { + for (const [assetID, value] of m) { + amount.set(assetID, (amount.get(assetID) ?? 0n) + value); + } + } + + return amount; +}; +/** + * Given a bigint, returns a human-readable string of the value. + * + * @example + * ```ts + * formatBigIntToHumanReadable(123456789n); // '123_456_789n' + * formatBigIntToHumanReadable(1234567890n); // '1_234_567_890n' + * ``` + */ +const formatBigIntToHumanReadable = (value: bigint): string => { + const bigIntStr = value.toString(); + + return `${bigIntStr.replace(/\B(?=(\d{3})+(?!\d))/g, '_')}n`; +}; + +/** + * Calculates the required fee for the unsigned transaction + * and verifies that the burned amount is exactly the required fee. + */ +export const checkFeeIsCorrect = ({ + unsignedTx, + inputs, + outputs, + feeState, + additionalInputs = [], + additionalOutputs = [], + additionalFee = 0n, +}: { + unsignedTx: UnsignedTx; + inputs: readonly TransferableInput[]; + outputs: readonly TransferableOutput[]; + feeState: FeeState; + additionalInputs?: readonly TransferableInput[]; + additionalOutputs?: readonly TransferableOutput[]; + additionalFee?: bigint; +}): [ + amountConsumed: Record, + expectedAmountConsumed: Record, + expectedFee: bigint, +] => { + const amountConsumed = addTransferableAmounts([ + ...inputs, + ...additionalInputs, + ]); + const amountProduced = addTransferableAmounts([ + ...outputs, + ...additionalOutputs, + ]); + + const expectedFee = calculateFee( + unsignedTx.getTx(), + testContext.platformFeeConfig.weights, + feeState.price, + ); + + const expectedAmountBurned = addAmounts( + new Map([[testAvaxAssetID.toString(), expectedFee + additionalFee]]), + ); + + const expectedAmountConsumed = addAmounts( + amountProduced, + expectedAmountBurned, + ); + + // Convert each map into a object with a stringified bigint value. + const safeExpectedAmountConsumed = Object.fromEntries( + [...expectedAmountConsumed].map(([k, v]) => [ + k, + formatBigIntToHumanReadable(v), + ]), + ); + + const safeAmountConsumed = Object.fromEntries( + [...amountConsumed].map(([k, v]) => [k, formatBigIntToHumanReadable(v)]), + ); + + return [ + safeAmountConsumed, + safeExpectedAmountConsumed, + expectedFee + additionalFee, + ]; +}; From c1f0913f2962dc4eaff03932a1cec9db233654e1 Mon Sep 17 00:00:00 2001 From: Fumiko Date: Mon, 3 Feb 2025 09:02:04 -0700 Subject: [PATCH 4/8] fix: (types) fixing context type --- src/vms/context/context.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vms/context/context.ts b/src/vms/context/context.ts index 2b1c82402..c0a71c272 100644 --- a/src/vms/context/context.ts +++ b/src/vms/context/context.ts @@ -17,8 +17,7 @@ export const getContextFromURI = async ( assetDescription, ); const info = new InfoApi(baseURL); - const { txFee: baseTxFee, createAssetTxFee } = await xChainApi.getTxFee(); - + const { txFee, createAssetTxFee } = await xChainApi.getTxFee(); const feeState = await pChainApi.getFeeState(); const { blockchainID: xBlockchainID } = await info.getBlockchainId('X'); @@ -35,8 +34,8 @@ export const getContextFromURI = async ( pBlockchainID, cBlockchainID, avaxAssetID, - baseTxFee, - createAssetTxFee, + baseTxFee: BigInt(txFee), + createAssetTxFee: BigInt(createAssetTxFee), networkID, hrp: getHRP(networkID), platformFeeConfig, From 1408603fc8c11103b30a716375e4e503193a4ce3 Mon Sep 17 00:00:00 2001 From: Fumiko Date: Mon, 3 Feb 2025 09:39:46 -0700 Subject: [PATCH 5/8] fix: (context) remove feeState --- src/fixtures/context.ts | 6 ----- src/utils/getBurnedAmountByTx.test.ts | 24 +++++++++---------- .../validateStaticBurnedAmount.test.ts | 11 +++++---- src/vms/avm/builder.test.ts | 15 ++++++------ src/vms/context/context.ts | 2 -- src/vms/context/model.ts | 3 +-- 6 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/fixtures/context.ts b/src/fixtures/context.ts index ebe8fc62d..23c7d3e17 100644 --- a/src/fixtures/context.ts +++ b/src/fixtures/context.ts @@ -23,10 +23,4 @@ export const testContext: Context = { minPrice: 1n, excessConversionConstant: 5_000n, }, - feeState: { - capacity: 1000000n, - excess: 100000n, - price: 1n, - timestamp: '2024-12-16T17:19:07Z', - }, }; diff --git a/src/utils/getBurnedAmountByTx.test.ts b/src/utils/getBurnedAmountByTx.test.ts index 252a911df..20ae88fab 100644 --- a/src/utils/getBurnedAmountByTx.test.ts +++ b/src/utils/getBurnedAmountByTx.test.ts @@ -305,7 +305,7 @@ describe('getBurnedAmountByTx', () => { fromAddressesBytes: [testAddress1], utxos: [utxo1, utxo2], outputs: [output], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ); @@ -317,7 +317,7 @@ describe('getBurnedAmountByTx', () => { unsignedTx, inputs, outputs, - feeState: testContext.feeState, + feeState: feeState(), }); expect(amounts.size).toEqual(1); @@ -336,7 +336,7 @@ describe('getBurnedAmountByTx', () => { fromAddressesBytes: [testAddress1], utxos: [utxo], outputs: [output1, output2, output3], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ); @@ -348,7 +348,7 @@ describe('getBurnedAmountByTx', () => { unsignedTx, inputs, outputs, - feeState: testContext.feeState, + feeState: feeState(), }); expect(amounts.size).toEqual(1); @@ -368,7 +368,7 @@ describe('getBurnedAmountByTx', () => { fromAddressesBytes: [testAddress1], utxos: [utxo1, utxo2], outputs: [output1, output2, output3], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ); @@ -380,7 +380,7 @@ describe('getBurnedAmountByTx', () => { unsignedTx, inputs, outputs, - feeState: testContext.feeState, + feeState: feeState(), }); expect(amounts.size).toEqual(1); @@ -399,7 +399,7 @@ describe('getBurnedAmountByTx', () => { utxos: [utxo1, utxo2], toAddressesBytes: [testAddress2], fromAddressesBytes: [testAddress1], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ); @@ -411,7 +411,7 @@ describe('getBurnedAmountByTx', () => { unsignedTx, inputs, outputs, - feeState: testContext.feeState, + feeState: feeState(), }); expect(amounts.size).toEqual(1); expect(amounts.get(testContext.avaxAssetID)).toEqual(expectedFee); @@ -428,7 +428,7 @@ describe('getBurnedAmountByTx', () => { utxos: [utxo1, utxo2], fromAddressesBytes: [testAddress1], subnetOwners: [testAddress1], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ); @@ -440,7 +440,7 @@ describe('getBurnedAmountByTx', () => { unsignedTx, inputs, outputs, - feeState: testContext.feeState, + feeState: feeState(), }); expect(amounts.size).toEqual(1); expect(amounts.get(testContext.avaxAssetID)).toEqual(expectedFee); @@ -456,7 +456,7 @@ describe('getBurnedAmountByTx', () => { const unsignedTx = newAddSubnetValidatorTx( { end: 1n, - feeState: testContext.feeState, + feeState: feeState(), fromAddressesBytes: [testAddress1], nodeId: nodeId().toString(), start: 0n, @@ -474,7 +474,7 @@ describe('getBurnedAmountByTx', () => { unsignedTx, inputs, outputs, - feeState: testContext.feeState, + feeState: feeState(), }); expect(amounts.size).toEqual(1); expect(amounts.get(testContext.avaxAssetID)).toEqual(expectedFee); diff --git a/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts b/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts index 4f5f71057..3a30cbc52 100644 --- a/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts +++ b/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts @@ -23,6 +23,7 @@ import { TransferableOutput } from '../../serializable'; import { nodeId } from '../../fixtures/common'; import { testSubnetId } from '../../fixtures/transactions'; import { validateStaticBurnedAmount } from './validateStaticBurnedAmount'; +import { feeState } from '../../fixtures/pvm'; const utxoMock = new Utxo( utxoId(), @@ -86,7 +87,7 @@ describe('validateStaticBurnedAmount', () => { fromAddressesBytes: [testAddress1], utxos: [utxoMock], outputs: [outputMock], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ), @@ -100,7 +101,7 @@ describe('validateStaticBurnedAmount', () => { fromAddressesBytes: [testAddress1], utxos: [utxoMock], outputs: [outputMock], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ), @@ -114,7 +115,7 @@ describe('validateStaticBurnedAmount', () => { utxos: [utxoMock], toAddressesBytes: [testAddress2], fromAddressesBytes: [testAddress1], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ), @@ -129,7 +130,7 @@ describe('validateStaticBurnedAmount', () => { nodeId: nodeId().toString(), subnetId: Id.fromHex(testSubnetId).toString(), subnetAuth: [0], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ), @@ -144,7 +145,7 @@ describe('validateStaticBurnedAmount', () => { subnetId: Id.fromHex(testSubnetId).toString(), subnetAuth: [0, 2], subnetOwners: [testAddress2], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ), diff --git a/src/vms/avm/builder.test.ts b/src/vms/avm/builder.test.ts index a50d11002..08bb030c3 100644 --- a/src/vms/avm/builder.test.ts +++ b/src/vms/avm/builder.test.ts @@ -25,6 +25,7 @@ import { hexToBuffer } from '../../utils'; import { newExportTx, newImportTx } from '../pvm'; import { newBaseTx } from './builder'; import { checkFeeIsCorrect } from '../pvm/etna-builder/utils/feeForTesting'; +import { feeState } from '../../fixtures/pvm'; describe('AVMBuilder', () => { let utxos: Utxo[]; @@ -39,7 +40,7 @@ describe('AVMBuilder', () => { utxos, toAddressesBytes: [toAddress], fromAddressesBytes: [testOwnerXAddress.toBytes()], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ); @@ -69,7 +70,7 @@ describe('AVMBuilder', () => { utxos, toAddressesBytes: [toAddress], fromAddressesBytes: [testOwnerXAddress.toBytes()], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ); @@ -80,7 +81,7 @@ describe('AVMBuilder', () => { unsignedTx, inputs, outputs, - feeState: testContext.feeState, + feeState: feeState(), }); expect(importTx.ins).toHaveLength(1); @@ -111,7 +112,7 @@ describe('AVMBuilder', () => { utxos, toAddressesBytes: [toAddress], fromAddressesBytes: [testOwnerXAddress.toBytes()], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ), @@ -131,7 +132,7 @@ describe('AVMBuilder', () => { fromAddressesBytes: [testOwnerXAddress.toBytes()], utxos, outputs: [tnsOut], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ); @@ -141,7 +142,7 @@ describe('AVMBuilder', () => { unsignedTx: tx, inputs, outputs, - feeState: testContext.feeState, + feeState: feeState(), }); const expectedAmountConsumedOriginal = expectedAmountConsumed[ testContext.avaxAssetID @@ -187,7 +188,7 @@ describe('AVMBuilder', () => { fromAddressesBytes: [testOwnerXAddress.toBytes()], utxos, outputs: [tnsOut], - feeState: testContext.feeState, + feeState: feeState(), }, testContext, ), diff --git a/src/vms/context/context.ts b/src/vms/context/context.ts index c0a71c272..81be01d70 100644 --- a/src/vms/context/context.ts +++ b/src/vms/context/context.ts @@ -18,7 +18,6 @@ export const getContextFromURI = async ( ); const info = new InfoApi(baseURL); const { txFee, createAssetTxFee } = await xChainApi.getTxFee(); - const feeState = await pChainApi.getFeeState(); const { blockchainID: xBlockchainID } = await info.getBlockchainId('X'); const { blockchainID: pBlockchainID } = await info.getBlockchainId('P'); @@ -39,6 +38,5 @@ export const getContextFromURI = async ( networkID, hrp: getHRP(networkID), platformFeeConfig, - feeState, }); }; diff --git a/src/vms/context/model.ts b/src/vms/context/model.ts index 7b935f102..56b3ddeac 100644 --- a/src/vms/context/model.ts +++ b/src/vms/context/model.ts @@ -1,4 +1,4 @@ -import type { FeeConfig, FeeState } from '../pvm'; +import type { FeeConfig } from '../pvm'; export type Context = { readonly networkID: number; @@ -12,5 +12,4 @@ export type Context = { // Post Etna readonly platformFeeConfig: FeeConfig; - readonly feeState: FeeState; }; From 4a954a2af4d9ae559376dad53f27822f57dd6074 Mon Sep 17 00:00:00 2001 From: Fumiko Date: Mon, 3 Feb 2025 10:25:32 -0700 Subject: [PATCH 6/8] fix: (test) remove pvm reference from avm builder test --- src/vms/avm/builder.test.ts | 96 ++++++++++--------------------------- 1 file changed, 25 insertions(+), 71 deletions(-) diff --git a/src/vms/avm/builder.test.ts b/src/vms/avm/builder.test.ts index 08bb030c3..4faa7e164 100644 --- a/src/vms/avm/builder.test.ts +++ b/src/vms/avm/builder.test.ts @@ -22,10 +22,7 @@ import { } from '../../serializable/fxs/secp256k1'; import { BigIntPr, Int } from '../../serializable/primitives'; import { hexToBuffer } from '../../utils'; -import { newExportTx, newImportTx } from '../pvm'; -import { newBaseTx } from './builder'; -import { checkFeeIsCorrect } from '../pvm/etna-builder/utils/feeForTesting'; -import { feeState } from '../../fixtures/pvm'; +import { newBaseTx, newExportTx, newImportTx } from './builder'; describe('AVMBuilder', () => { let utxos: Utxo[]; @@ -35,14 +32,11 @@ describe('AVMBuilder', () => { it('importTx', async () => { const toAddress = hexToBuffer('0x5432112345123451234512'); const tx = newImportTx( - { - sourceChainId: testContext.cBlockchainID, - utxos, - toAddressesBytes: [toAddress], - fromAddressesBytes: [testOwnerXAddress.toBytes()], - feeState: feeState(), - }, testContext, + testContext.cBlockchainID, + utxos, + [toAddress], + [testOwnerXAddress.toBytes()], ); const importTx = tx.getTx() as ImportTx; @@ -64,39 +58,21 @@ describe('AVMBuilder', () => { ), ); - const unsignedTx = newImportTx( - { - sourceChainId: testContext.cBlockchainID, - utxos, - toAddressesBytes: [toAddress], - fromAddressesBytes: [testOwnerXAddress.toBytes()], - feeState: feeState(), - }, + const tx = newImportTx( testContext, + testContext.cBlockchainID, + utxos, + [toAddress], + [testOwnerXAddress.toBytes()], ); - const importTx = unsignedTx.getTx() as ImportTx; - const { inputs, outputs } = importTx.baseTx; - const [, expectedAmountConsumed, expectedFee] = checkFeeIsCorrect({ - unsignedTx, - inputs, - outputs, - feeState: feeState(), - }); + const importTx = tx.getTx() as ImportTx; expect(importTx.ins).toHaveLength(1); expect(importTx.ins[0].assetId).toEqual(testAvaxAssetID); expect(Number(importTx.ins[0].amount())).toEqual(50 * 1e5); - - const expectedAmountConsumedOriginal = expectedAmountConsumed[ - testContext.avaxAssetID - ] - .slice(0, -1) //remove n - .replaceAll('_', ''); - const expectedAmountConsumedBigInt = BigInt(expectedAmountConsumedOriginal); - expect((importTx.baseTx.outputs as TransferableOutput[])[0].amount()).toBe( - expectedAmountConsumedBigInt - expectedFee, + BigInt(40 * 1e5), ); expect(importTx.ins[0].utxoID.ID()).toEqual(utxos[2].ID()); }); @@ -107,14 +83,11 @@ describe('AVMBuilder', () => { expect(() => newImportTx( - { - sourceChainId: testContext.cBlockchainID, - utxos, - toAddressesBytes: [toAddress], - fromAddressesBytes: [testOwnerXAddress.toBytes()], - feeState: feeState(), - }, testContext, + testContext.cBlockchainID, + utxos, + [toAddress], + [testOwnerXAddress.toBytes()], ), ).toThrow(); }); @@ -127,29 +100,13 @@ describe('AVMBuilder', () => { [toAddress], ); const tx = newExportTx( - { - destinationChainId: testContext.cBlockchainID, - fromAddressesBytes: [testOwnerXAddress.toBytes()], - utxos, - outputs: [tnsOut], - feeState: feeState(), - }, testContext, + testContext.cBlockchainID, + [testOwnerXAddress.toBytes()], + utxos, + [tnsOut], ); const exportTx = tx.getTx() as ExportTx; - const { inputs, outputs } = exportTx.baseTx; - const [, expectedAmountConsumed, expectedFee] = checkFeeIsCorrect({ - unsignedTx: tx, - inputs, - outputs, - feeState: feeState(), - }); - const expectedAmountConsumedOriginal = expectedAmountConsumed[ - testContext.avaxAssetID - ] - .slice(0, -1) //remove n - .replaceAll('_', ''); - const expectedAmountConsumedBigInt = BigInt(expectedAmountConsumedOriginal); expect(exportTx.outs as TransferableOutput[]).toEqual([tnsOut]); expect(exportTx.baseTx.inputs as TransferableInput[]).toEqual([ new TransferableInput( @@ -166,7 +123,7 @@ describe('AVMBuilder', () => { new TransferableOutput( testAvaxAssetID, new TransferOutput( - new BigIntPr(expectedAmountConsumedBigInt - expectedFee), + new BigIntPr(44999000000n), OutputOwners.fromNative([testOwnerXAddress.toBytes()]), ), ), @@ -183,14 +140,11 @@ describe('AVMBuilder', () => { utxos.pop(); expect(() => newExportTx( - { - destinationChainId: testContext.cBlockchainID, - fromAddressesBytes: [testOwnerXAddress.toBytes()], - utxos, - outputs: [tnsOut], - feeState: feeState(), - }, testContext, + testContext.cBlockchainID, + [testOwnerXAddress.toBytes()], + utxos, + [tnsOut], ), ).toThrow(); }); From dfc52f9671df141a682fec5f79a1e8c78f75b2cb Mon Sep 17 00:00:00 2001 From: Fumiko Date: Mon, 3 Feb 2025 13:39:13 -0700 Subject: [PATCH 7/8] fix: (burned Amount) clean up --- .../validateBurnedAmount.ts | 17 +--- .../validateStaticBurnedAmount.test.ts | 81 ------------------- .../validateStaticBurnedAmount.ts | 20 +---- 3 files changed, 5 insertions(+), 113 deletions(-) diff --git a/src/utils/validateBurnedAmount/validateBurnedAmount.ts b/src/utils/validateBurnedAmount/validateBurnedAmount.ts index d8f3c60ff..b316098d9 100644 --- a/src/utils/validateBurnedAmount/validateBurnedAmount.ts +++ b/src/utils/validateBurnedAmount/validateBurnedAmount.ts @@ -5,8 +5,6 @@ import { isImportExportTx as isEvmImportExportTx } from '../../serializable/evm' import { getBurnedAmountByTx } from '../getBurnedAmountByTx'; import type { AvaxTx } from '../../serializable/avax'; import { validateDynamicBurnedAmount } from './validateDynamicBurnedAmount'; -import type { GetUpgradesInfoResponse } from '../../info/model'; -import { isEtnaEnabled } from '../isEtnaEnabled'; import { validateStaticBurnedAmount } from './validateStaticBurnedAmount'; import { costCorethTx } from '../costs'; import { calculateFee } from '../../vms/pvm/txs/fee/calculator'; @@ -34,13 +32,9 @@ const _getBurnedAmount = (tx: Transaction, context: Context) => { return burnedAmounts.get(context.avaxAssetID) ?? 0n; }; -// Check supported pvm transactions for Etna -// Todo: add isAvmBaseTx, isAvmExportTx and isAvmImportTx when avm dynamic fee is implemented -const isEtnaSupported = (tx: Transaction) => { +// Todo: create isAvmTx for isAvmBaseTx, isAvmExportTx and isAvmImportTx when avm dynamic fee is implemented +const isPvmTx = (tx: Transaction) => { return ( - // isAvmBaseTx(tx) || // not implemented - // isAvmExportTx(tx) || // not implemented - // isAvmImportTx(tx) || // not implemented isPvmBaseTx(tx) || isPvmExportTx(tx) || isPvmImportTx(tx) || @@ -74,14 +68,12 @@ const isEtnaSupported = (tx: Transaction) => { export const validateBurnedAmount = ({ unsignedTx, context, - upgradesInfo, burnedAmount, baseFee, feeTolerance, }: { unsignedTx: UnsignedTx; context: Context; - upgradesInfo?: GetUpgradesInfoResponse; burnedAmount?: bigint; baseFee: bigint; feeTolerance: number; @@ -89,10 +81,7 @@ export const validateBurnedAmount = ({ const tx = unsignedTx.getTx(); const burned = burnedAmount ?? _getBurnedAmount(tx, context); - if ( - isEvmImportExportTx(tx) || - (upgradesInfo && isEtnaEnabled(upgradesInfo) && isEtnaSupported(tx)) - ) { + if (isEvmImportExportTx(tx) || isPvmTx(tx)) { const feeAmount = isEvmImportExportTx(tx) ? baseFee * costCorethTx(unsignedTx) : calculateFee(tx, context.platformFeeConfig.weights, baseFee); diff --git a/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts b/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts index 3a30cbc52..ea11b8e60 100644 --- a/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts +++ b/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts @@ -12,18 +12,8 @@ import { newExportTx as avmExportTx, newImportTx as avmImportTx, } from '../../vms/avm'; -import { - newBaseTx as pvmBaseTx, - newExportTx as pvmExportTx, - newImportTx as pvmImportTx, - newRemoveSubnetValidatorTx, - newTransferSubnetOwnershipTx, -} from '../../vms/pvm'; import { TransferableOutput } from '../../serializable'; -import { nodeId } from '../../fixtures/common'; -import { testSubnetId } from '../../fixtures/transactions'; import { validateStaticBurnedAmount } from './validateStaticBurnedAmount'; -import { feeState } from '../../fixtures/pvm'; const utxoMock = new Utxo( utxoId(), @@ -80,77 +70,6 @@ describe('validateStaticBurnedAmount', () => { ), correctBurnedAmount: testContext.baseTxFee, }, - { - name: 'base tx on P', - unsignedTx: pvmBaseTx( - { - fromAddressesBytes: [testAddress1], - utxos: [utxoMock], - outputs: [outputMock], - feeState: feeState(), - }, - testContext, - ), - correctBurnedAmount: testContext.baseTxFee, - }, - { - name: 'export from P', - unsignedTx: pvmExportTx( - { - destinationChainId: 'C', - fromAddressesBytes: [testAddress1], - utxos: [utxoMock], - outputs: [outputMock], - feeState: feeState(), - }, - testContext, - ), - correctBurnedAmount: testContext.baseTxFee, - }, - { - name: 'import to P', - unsignedTx: pvmImportTx( - { - sourceChainId: 'C', - utxos: [utxoMock], - toAddressesBytes: [testAddress2], - fromAddressesBytes: [testAddress1], - feeState: feeState(), - }, - testContext, - ), - correctBurnedAmount: testContext.baseTxFee, - }, - { - name: 'remove subnet validator', - unsignedTx: newRemoveSubnetValidatorTx( - { - utxos: [utxoMock], - fromAddressesBytes: [testAddress1], - nodeId: nodeId().toString(), - subnetId: Id.fromHex(testSubnetId).toString(), - subnetAuth: [0], - feeState: feeState(), - }, - testContext, - ), - correctBurnedAmount: testContext.baseTxFee, - }, - { - name: 'transfer subnet ownership', - unsignedTx: newTransferSubnetOwnershipTx( - { - utxos: [utxoMock], - fromAddressesBytes: [testAddress1], - subnetId: Id.fromHex(testSubnetId).toString(), - subnetAuth: [0, 2], - subnetOwners: [testAddress2], - feeState: feeState(), - }, - testContext, - ), - correctBurnedAmount: testContext.baseTxFee, - }, ]; describe.each(testData)('$name', ({ unsignedTx, correctBurnedAmount }) => { diff --git a/src/utils/validateBurnedAmount/validateStaticBurnedAmount.ts b/src/utils/validateBurnedAmount/validateStaticBurnedAmount.ts index 8dad548e9..86fa9d2f0 100644 --- a/src/utils/validateBurnedAmount/validateStaticBurnedAmount.ts +++ b/src/utils/validateBurnedAmount/validateStaticBurnedAmount.ts @@ -1,11 +1,4 @@ import type { Context } from '../../vms/context/model'; -import { - isPvmBaseTx, - isExportTx as isPvmExportTx, - isImportTx as isPvmImportTx, - isRemoveSubnetValidatorTx, - isTransferSubnetOwnershipTx, -} from '../../serializable/pvm'; import type { UnsignedTx } from '../../vms/common'; import { isAvmBaseTx, @@ -14,7 +7,7 @@ import { } from '../../serializable/avm'; /** - * Validate static burned amount for avalanche x/p transactions + * Validate static burned amount for avalanche x transactions * * @param unsignedTx: unsigned transaction * @param context @@ -33,16 +26,7 @@ export const validateStaticBurnedAmount = ({ }): { isValid: boolean; txFee: bigint } => { const tx = unsignedTx.getTx(); - if ( - isAvmBaseTx(tx) || - isPvmBaseTx(tx) || - isAvmExportTx(tx) || - isAvmImportTx(tx) || - isPvmExportTx(tx) || - isPvmImportTx(tx) || - isRemoveSubnetValidatorTx(tx) || - isTransferSubnetOwnershipTx(tx) - ) { + if (isAvmBaseTx(tx) || isAvmExportTx(tx) || isAvmImportTx(tx)) { return validate(burnedAmount, context.baseTxFee); } From 6ae1258cfdd9508d20f0569db70a05498e3cab26 Mon Sep 17 00:00:00 2001 From: Fumiko Date: Tue, 4 Feb 2025 10:00:03 -0700 Subject: [PATCH 8/8] fix: (example and avm) post code review update --- examples/p-chain/base.ts | 42 ++++++---- .../p-chain/{etna => }/convertSubnetToL1.ts | 4 +- examples/p-chain/{etna => }/createChain.ts | 6 +- examples/p-chain/{etna => }/createSubnet.ts | 4 +- examples/p-chain/delegate.ts | 55 +++++++------ examples/p-chain/etna/base.ts | 41 ---------- examples/p-chain/etna/delegate.ts | 49 ------------ examples/p-chain/etna/export.ts | 42 ---------- examples/p-chain/etna/import.ts | 35 --------- examples/p-chain/etna/validate.ts | 65 ---------------- examples/p-chain/export.ts | 44 ++++++----- examples/p-chain/import.ts | 29 ++++--- .../p-chain/{etna => }/increaseBalanceTx.ts | 4 +- .../{etna => }/utils/addSignatureToAllCred.ts | 2 +- .../p-chain/{etna => }/utils/etna-helper.ts | 4 +- .../{etna => }/utils/random-node-id.ts | 0 examples/p-chain/validate.ts | 77 ++++++++++--------- package.json | 6 +- src/vms/avm/api.ts | 11 ++- src/vms/avm/models.ts | 5 ++ src/vms/context/context.ts | 4 +- src/vms/pvm/index.ts | 2 +- 22 files changed, 168 insertions(+), 363 deletions(-) rename examples/p-chain/{etna => }/convertSubnetToL1.ts (96%) rename examples/p-chain/{etna => }/createChain.ts (89%) rename examples/p-chain/{etna => }/createSubnet.ts (87%) delete mode 100644 examples/p-chain/etna/base.ts delete mode 100644 examples/p-chain/etna/delegate.ts delete mode 100644 examples/p-chain/etna/export.ts delete mode 100644 examples/p-chain/etna/import.ts delete mode 100644 examples/p-chain/etna/validate.ts rename examples/p-chain/{etna => }/increaseBalanceTx.ts (88%) rename examples/p-chain/{etna => }/utils/addSignatureToAllCred.ts (87%) rename examples/p-chain/{etna => }/utils/etna-helper.ts (85%) rename examples/p-chain/{etna => }/utils/random-node-id.ts (100%) diff --git a/examples/p-chain/base.ts b/examples/p-chain/base.ts index 577aeb498..19a99ce1d 100644 --- a/examples/p-chain/base.ts +++ b/examples/p-chain/base.ts @@ -1,29 +1,41 @@ -import { addTxSignatures } from '../../src/signer'; -import { TransferableOutput } from '../../src/serializable/avax'; -import { bech32ToBytes, hexToBuffer } from '../../src/utils'; -import { getContextFromURI } from '../../src/vms/context'; -import { newBaseTx } from '../../src/vms/pvm'; -import { pvmapi } from '../chain_apis'; +import { TransferableOutput, addTxSignatures, pvm, utils } from '../../src'; import { getEnvVars } from '../utils/getEnvVars'; +import { setupEtnaExample } from './utils/etna-helper'; + +/** + * The amount of AVAX to send to self. + */ +const SEND_AVAX_AMOUNT: number = 0.001; const main = async () => { const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY } = getEnvVars(); - const { utxos } = await pvmapi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] }); - const context = await getContextFromURI(AVAX_PUBLIC_URL); + const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL); + + const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] }); - const tx = newBaseTx(context, [bech32ToBytes(P_CHAIN_ADDRESS)], utxos, [ - TransferableOutput.fromNative(context.avaxAssetID, BigInt(0.1 * 1e9), [ - bech32ToBytes(P_CHAIN_ADDRESS), - ]), - ]); + const tx = pvm.newBaseTx( + { + feeState, + fromAddressesBytes: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], + outputs: [ + TransferableOutput.fromNative( + context.avaxAssetID, + BigInt(SEND_AVAX_AMOUNT * 1e9), + [utils.bech32ToBytes(P_CHAIN_ADDRESS)], + ), + ], + utxos, + }, + context, + ); await addTxSignatures({ unsignedTx: tx, - privateKeys: [hexToBuffer(PRIVATE_KEY)], + privateKeys: [utils.hexToBuffer(PRIVATE_KEY)], }); - return pvmapi.issueSignedTx(tx.getSignedTx()); + return pvmApi.issueSignedTx(tx.getSignedTx()); }; main().then(console.log); diff --git a/examples/p-chain/etna/convertSubnetToL1.ts b/examples/p-chain/convertSubnetToL1.ts similarity index 96% rename from examples/p-chain/etna/convertSubnetToL1.ts rename to examples/p-chain/convertSubnetToL1.ts index 0d51cf4b1..cbf1a606a 100644 --- a/examples/p-chain/etna/convertSubnetToL1.ts +++ b/examples/p-chain/convertSubnetToL1.ts @@ -5,9 +5,9 @@ import { pvm, pvmSerial, utils, -} from '../../../src'; +} from '../../src'; import { setupEtnaExample } from './utils/etna-helper'; -import { getEnvVars } from '../../utils/getEnvVars'; +import { getEnvVars } from '../utils/getEnvVars'; const AMOUNT_TO_VALIDATE_AVAX: number = 1; const BALANCE_AVAX: number = 1; diff --git a/examples/p-chain/etna/createChain.ts b/examples/p-chain/createChain.ts similarity index 89% rename from examples/p-chain/etna/createChain.ts rename to examples/p-chain/createChain.ts index e3f0c87c3..1faca3149 100644 --- a/examples/p-chain/etna/createChain.ts +++ b/examples/p-chain/createChain.ts @@ -1,7 +1,7 @@ -import { pvm, utils } from '../../../src'; +import { pvm, utils } from '../../src'; import { setupEtnaExample } from './utils/etna-helper'; -import { testGenesisData } from '../../../src/fixtures/transactions'; -import { getEnvVars } from '../../utils/getEnvVars'; +import { testGenesisData } from '../../src/fixtures/transactions'; +import { getEnvVars } from '../utils/getEnvVars'; import { addSigToAllCreds } from './utils/addSignatureToAllCred'; /** diff --git a/examples/p-chain/etna/createSubnet.ts b/examples/p-chain/createSubnet.ts similarity index 87% rename from examples/p-chain/etna/createSubnet.ts rename to examples/p-chain/createSubnet.ts index 0d93114a0..6b92346c4 100644 --- a/examples/p-chain/etna/createSubnet.ts +++ b/examples/p-chain/createSubnet.ts @@ -1,5 +1,5 @@ -import { addTxSignatures, pvm, utils } from '../../../src'; -import { getEnvVars } from '../../utils/getEnvVars'; +import { addTxSignatures, pvm, utils } from '../../src'; +import { getEnvVars } from '../utils/getEnvVars'; import { setupEtnaExample } from './utils/etna-helper'; const createSubnetTxExample = async () => { diff --git a/examples/p-chain/delegate.ts b/examples/p-chain/delegate.ts index c68cdbc18..046612165 100644 --- a/examples/p-chain/delegate.ts +++ b/examples/p-chain/delegate.ts @@ -1,42 +1,49 @@ -import { PrimaryNetworkID } from '../../src/constants/networkIDs'; -import { addTxSignatures } from '../../src/signer'; -import { bech32ToBytes, hexToBuffer } from '../../src/utils'; -import { getContextFromURI } from '../../src/vms/context'; -import { PVMApi, newAddPermissionlessDelegatorTx } from '../../src/vms/pvm'; -import { pvmapi } from '../chain_apis'; +import { addTxSignatures, networkIDs, pvm, utils } from '../../src'; import { getEnvVars } from '../utils/getEnvVars'; +import { setupEtnaExample } from './utils/etna-helper'; + +const AMOUNT_TO_DELEGATE_AVAX: number = 1; +const DAYS_TO_DELEGATE: number = 14; const main = async () => { const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY } = getEnvVars(); - const { utxos } = await pvmapi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] }); - const context = await getContextFromURI(AVAX_PUBLIC_URL); - const startTime = await new PVMApi().getTimestamp(); + const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL); + + const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] }); + + const startTime = await pvmApi.getTimestamp(); const startDate = new Date(startTime.timestamp); - const start = BigInt(startDate.getTime() / 1000); + const start: bigint = BigInt(startDate.getTime() / 1_000); + const endTime = new Date(startTime.timestamp); - endTime.setDate(endTime.getDate() + 21); - const end = BigInt(endTime.getTime() / 1000); - const nodeID = 'NodeID-HKLp5269LH8DcrLvHPc2PHjGczBQD3td4'; + endTime.setDate(endTime.getDate() + DAYS_TO_DELEGATE); + const end: bigint = BigInt(endTime.getTime() / 1_000); + + // TODO: Get this from an argument. + const nodeId = 'NodeID-MqgFXT8JhorbEW2LpTDGePBBhv55SSp3M'; - const tx = newAddPermissionlessDelegatorTx( + const tx = pvm.newAddPermissionlessDelegatorTx( + { + end, + feeState, + fromAddressesBytes: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], + nodeId, + rewardAddresses: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], + start, + subnetId: networkIDs.PrimaryNetworkID.toString(), + utxos, + weight: BigInt(AMOUNT_TO_DELEGATE_AVAX * 1e9), + }, context, - utxos, - [bech32ToBytes(P_CHAIN_ADDRESS)], - nodeID, - PrimaryNetworkID.toString(), - start, - end, - BigInt(1e9), - [bech32ToBytes(P_CHAIN_ADDRESS)], ); await addTxSignatures({ unsignedTx: tx, - privateKeys: [hexToBuffer(PRIVATE_KEY)], + privateKeys: [utils.hexToBuffer(PRIVATE_KEY)], }); - return pvmapi.issueSignedTx(tx.getSignedTx()); + return pvmApi.issueSignedTx(tx.getSignedTx()); }; main().then(console.log); diff --git a/examples/p-chain/etna/base.ts b/examples/p-chain/etna/base.ts deleted file mode 100644 index 20bae3c84..000000000 --- a/examples/p-chain/etna/base.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { TransferableOutput, addTxSignatures, pvm, utils } from '../../../src'; -import { getEnvVars } from '../../utils/getEnvVars'; -import { setupEtnaExample } from './utils/etna-helper'; - -/** - * The amount of AVAX to send to self. - */ -const SEND_AVAX_AMOUNT: number = 0.001; - -const main = async () => { - const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY } = getEnvVars(); - - const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL); - - const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] }); - - const tx = pvm.newBaseTx( - { - feeState, - fromAddressesBytes: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], - outputs: [ - TransferableOutput.fromNative( - context.avaxAssetID, - BigInt(SEND_AVAX_AMOUNT * 1e9), - [utils.bech32ToBytes(P_CHAIN_ADDRESS)], - ), - ], - utxos, - }, - context, - ); - - await addTxSignatures({ - unsignedTx: tx, - privateKeys: [utils.hexToBuffer(PRIVATE_KEY)], - }); - - return pvmApi.issueSignedTx(tx.getSignedTx()); -}; - -main().then(console.log); diff --git a/examples/p-chain/etna/delegate.ts b/examples/p-chain/etna/delegate.ts deleted file mode 100644 index 8190d5e48..000000000 --- a/examples/p-chain/etna/delegate.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { addTxSignatures, networkIDs, pvm, utils } from '../../../src'; -import { getEnvVars } from '../../utils/getEnvVars'; -import { setupEtnaExample } from './utils/etna-helper'; - -const AMOUNT_TO_DELEGATE_AVAX: number = 1; -const DAYS_TO_DELEGATE: number = 14; - -const main = async () => { - const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY } = getEnvVars(); - - const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL); - - const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] }); - - const startTime = await pvmApi.getTimestamp(); - const startDate = new Date(startTime.timestamp); - const start: bigint = BigInt(startDate.getTime() / 1_000); - - const endTime = new Date(startTime.timestamp); - endTime.setDate(endTime.getDate() + DAYS_TO_DELEGATE); - const end: bigint = BigInt(endTime.getTime() / 1_000); - - // TODO: Get this from an argument. - const nodeId = 'NodeID-MqgFXT8JhorbEW2LpTDGePBBhv55SSp3M'; - - const tx = pvm.newAddPermissionlessDelegatorTx( - { - end, - feeState, - fromAddressesBytes: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], - nodeId, - rewardAddresses: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], - start, - subnetId: networkIDs.PrimaryNetworkID.toString(), - utxos, - weight: BigInt(AMOUNT_TO_DELEGATE_AVAX * 1e9), - }, - context, - ); - - await addTxSignatures({ - unsignedTx: tx, - privateKeys: [utils.hexToBuffer(PRIVATE_KEY)], - }); - - return pvmApi.issueSignedTx(tx.getSignedTx()); -}; - -main().then(console.log); diff --git a/examples/p-chain/etna/export.ts b/examples/p-chain/etna/export.ts deleted file mode 100644 index cbdf5dc79..000000000 --- a/examples/p-chain/etna/export.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { TransferableOutput, addTxSignatures, pvm, utils } from '../../../src'; -import { getEnvVars } from '../../utils/getEnvVars'; -import { setupEtnaExample } from './utils/etna-helper'; - -const AMOUNT_TO_EXPORT_AVAX: number = 0.001; - -const main = async () => { - const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY, X_CHAIN_ADDRESS } = - getEnvVars(); - - const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL); - - const { utxos } = await pvmApi.getUTXOs({ - addresses: [P_CHAIN_ADDRESS], - }); - - const exportTx = pvm.newExportTx( - { - destinationChainId: context.xBlockchainID, - feeState, - fromAddressesBytes: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], - outputs: [ - TransferableOutput.fromNative( - context.avaxAssetID, - BigInt(AMOUNT_TO_EXPORT_AVAX * 1e9), - [utils.bech32ToBytes(X_CHAIN_ADDRESS)], - ), - ], - utxos, - }, - context, - ); - - await addTxSignatures({ - unsignedTx: exportTx, - privateKeys: [utils.hexToBuffer(PRIVATE_KEY)], - }); - - return pvmApi.issueSignedTx(exportTx.getSignedTx()); -}; - -main().then(console.log); diff --git a/examples/p-chain/etna/import.ts b/examples/p-chain/etna/import.ts deleted file mode 100644 index 8bb9696c5..000000000 --- a/examples/p-chain/etna/import.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { addTxSignatures, pvm, utils } from '../../../src'; -import { getEnvVars } from '../../utils/getEnvVars'; -import { setupEtnaExample } from './utils/etna-helper'; - -const main = async () => { - const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY, X_CHAIN_ADDRESS } = - getEnvVars(); - - const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL); - - const { utxos } = await pvmApi.getUTXOs({ - sourceChain: 'X', - addresses: [P_CHAIN_ADDRESS], - }); - - const importTx = pvm.newImportTx( - { - feeState, - fromAddressesBytes: [utils.bech32ToBytes(X_CHAIN_ADDRESS)], - sourceChainId: context.xBlockchainID, - toAddressesBytes: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], - utxos, - }, - context, - ); - - await addTxSignatures({ - unsignedTx: importTx, - privateKeys: [utils.hexToBuffer(PRIVATE_KEY)], - }); - - return pvmApi.issueSignedTx(importTx.getSignedTx()); -}; - -main().then(console.log); diff --git a/examples/p-chain/etna/validate.ts b/examples/p-chain/etna/validate.ts deleted file mode 100644 index bf6d4f741..000000000 --- a/examples/p-chain/etna/validate.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { addTxSignatures, networkIDs, pvm, utils } from '../../../src'; -import { getEnvVars } from '../../utils/getEnvVars'; -import { setupEtnaExample } from './utils/etna-helper'; -import { getRandomNodeId } from './utils/random-node-id'; - -const AMOUNT_TO_VALIDATE_AVAX: number = 1; -const DAYS_TO_VALIDATE: number = 21; - -const nodeId = getRandomNodeId(); - -const main = async () => { - const { - AVAX_PUBLIC_URL, - P_CHAIN_ADDRESS, - PRIVATE_KEY, - BLS_PUBLIC_KEY, - BLS_SIGNATURE, - } = getEnvVars(); - - const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL); - - const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] }); - - const startTime = await pvmApi.getTimestamp(); - const startDate = new Date(startTime.timestamp); - const start: bigint = BigInt(startDate.getTime() / 1_000); - - const endTime = new Date(startTime.timestamp); - endTime.setDate(endTime.getDate() + DAYS_TO_VALIDATE); - const end: bigint = BigInt(endTime.getTime() / 1_000); - - const publicKey = utils.hexToBuffer(BLS_PUBLIC_KEY); - - const signature = utils.hexToBuffer(BLS_SIGNATURE); - - const tx = pvm.newAddPermissionlessValidatorTx( - { - end, - delegatorRewardsOwner: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], - feeState, - fromAddressesBytes: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], - nodeId, - publicKey, - rewardAddresses: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], - shares: 20 * 1e4, - signature, - start, - subnetId: networkIDs.PrimaryNetworkID.toString(), - utxos, - weight: BigInt(AMOUNT_TO_VALIDATE_AVAX * 1e9), - }, - context, - ); - - await addTxSignatures({ - unsignedTx: tx, - privateKeys: [utils.hexToBuffer(PRIVATE_KEY)], - }); - - return pvmApi.issueSignedTx(tx.getSignedTx()); -}; - -main() - .then(console.log) - .then(() => console.log('Validate node ID:', nodeId)); diff --git a/examples/p-chain/export.ts b/examples/p-chain/export.ts index 07c37f00b..c1d6aaa8e 100644 --- a/examples/p-chain/export.ts +++ b/examples/p-chain/export.ts @@ -1,40 +1,42 @@ -import { TransferableOutput } from '../../src/serializable/avax'; -import { addTxSignatures } from '../../src/signer'; -import { bech32ToBytes, hexToBuffer } from '../../src/utils'; -import { getContextFromURI } from '../../src/vms/context'; -import { newExportTx } from '../../src/vms/pvm'; -import { pvmapi } from '../chain_apis'; -import { getChainIdFromContext } from '../utils/getChainIdFromContext'; +import { TransferableOutput, addTxSignatures, pvm, utils } from '../../src'; import { getEnvVars } from '../utils/getEnvVars'; +import { setupEtnaExample } from './utils/etna-helper'; + +const AMOUNT_TO_EXPORT_AVAX: number = 0.001; const main = async () => { const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY, X_CHAIN_ADDRESS } = getEnvVars(); - const context = await getContextFromURI(AVAX_PUBLIC_URL); + const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL); - const { utxos } = await pvmapi.getUTXOs({ + const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS], }); - const tx = newExportTx( + const exportTx = pvm.newExportTx( + { + destinationChainId: context.xBlockchainID, + feeState, + fromAddressesBytes: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], + outputs: [ + TransferableOutput.fromNative( + context.avaxAssetID, + BigInt(AMOUNT_TO_EXPORT_AVAX * 1e9), + [utils.bech32ToBytes(X_CHAIN_ADDRESS)], + ), + ], + utxos, + }, context, - getChainIdFromContext('X', context), - [bech32ToBytes(P_CHAIN_ADDRESS)], - utxos, - [ - TransferableOutput.fromNative(context.avaxAssetID, BigInt(0.1 * 1e9), [ - bech32ToBytes(X_CHAIN_ADDRESS), - ]), - ], ); await addTxSignatures({ - unsignedTx: tx, - privateKeys: [hexToBuffer(PRIVATE_KEY)], + unsignedTx: exportTx, + privateKeys: [utils.hexToBuffer(PRIVATE_KEY)], }); - return pvmapi.issueSignedTx(tx.getSignedTx()); + return pvmApi.issueSignedTx(exportTx.getSignedTx()); }; main().then(console.log); diff --git a/examples/p-chain/import.ts b/examples/p-chain/import.ts index 1795639d1..349684d60 100644 --- a/examples/p-chain/import.ts +++ b/examples/p-chain/import.ts @@ -1,36 +1,35 @@ -import { addTxSignatures } from '../../src/signer'; -import { bech32ToBytes, hexToBuffer } from '../../src/utils'; -import { getContextFromURI } from '../../src/vms/context'; -import { newImportTx } from '../../src/vms/pvm'; -import { pvmapi } from '../chain_apis'; -import { getChainIdFromContext } from '../utils/getChainIdFromContext'; +import { addTxSignatures, pvm, utils } from '../../src'; import { getEnvVars } from '../utils/getEnvVars'; +import { setupEtnaExample } from './utils/etna-helper'; const main = async () => { const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY, X_CHAIN_ADDRESS } = getEnvVars(); - const context = await getContextFromURI(AVAX_PUBLIC_URL); + const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL); - const { utxos } = await pvmapi.getUTXOs({ + const { utxos } = await pvmApi.getUTXOs({ sourceChain: 'X', addresses: [P_CHAIN_ADDRESS], }); - const importTx = newImportTx( + const importTx = pvm.newImportTx( + { + feeState, + fromAddressesBytes: [utils.bech32ToBytes(X_CHAIN_ADDRESS)], + sourceChainId: context.xBlockchainID, + toAddressesBytes: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], + utxos, + }, context, - getChainIdFromContext('X', context), - utxos, - [bech32ToBytes(P_CHAIN_ADDRESS)], - [bech32ToBytes(X_CHAIN_ADDRESS)], ); await addTxSignatures({ unsignedTx: importTx, - privateKeys: [hexToBuffer(PRIVATE_KEY)], + privateKeys: [utils.hexToBuffer(PRIVATE_KEY)], }); - return pvmapi.issueSignedTx(importTx.getSignedTx()); + return pvmApi.issueSignedTx(importTx.getSignedTx()); }; main().then(console.log); diff --git a/examples/p-chain/etna/increaseBalanceTx.ts b/examples/p-chain/increaseBalanceTx.ts similarity index 88% rename from examples/p-chain/etna/increaseBalanceTx.ts rename to examples/p-chain/increaseBalanceTx.ts index 9eb6e2dda..937f8f071 100644 --- a/examples/p-chain/etna/increaseBalanceTx.ts +++ b/examples/p-chain/increaseBalanceTx.ts @@ -1,6 +1,6 @@ -import { addTxSignatures, pvm, utils } from '../../../src'; +import { addTxSignatures, pvm, utils } from '../../src'; import { setupEtnaExample } from './utils/etna-helper'; -import { getEnvVars } from '../../utils/getEnvVars'; +import { getEnvVars } from '../utils/getEnvVars'; const BALANCE_AVAX: number = 1; const VALIDATION_ID: string = ''; diff --git a/examples/p-chain/etna/utils/addSignatureToAllCred.ts b/examples/p-chain/utils/addSignatureToAllCred.ts similarity index 87% rename from examples/p-chain/etna/utils/addSignatureToAllCred.ts rename to examples/p-chain/utils/addSignatureToAllCred.ts index d1ed72763..b37a8074d 100644 --- a/examples/p-chain/etna/utils/addSignatureToAllCred.ts +++ b/examples/p-chain/utils/addSignatureToAllCred.ts @@ -1,4 +1,4 @@ -import { secp256k1, type UnsignedTx } from '../../../../src'; +import { secp256k1, type UnsignedTx } from '../../../src'; export const addSigToAllCreds = async ( unsignedTx: UnsignedTx, diff --git a/examples/p-chain/etna/utils/etna-helper.ts b/examples/p-chain/utils/etna-helper.ts similarity index 85% rename from examples/p-chain/etna/utils/etna-helper.ts rename to examples/p-chain/utils/etna-helper.ts index 933a56464..63f8385f2 100644 --- a/examples/p-chain/etna/utils/etna-helper.ts +++ b/examples/p-chain/utils/etna-helper.ts @@ -1,5 +1,5 @@ -import { Context, info, pvm } from '../../../../src'; -import type { FeeState } from '../../../../src/vms/pvm'; +import { Context, info, pvm } from '../../../src'; +import type { FeeState } from '../../../src/vms/pvm'; export const setupEtnaExample = async ( uri: string, diff --git a/examples/p-chain/etna/utils/random-node-id.ts b/examples/p-chain/utils/random-node-id.ts similarity index 100% rename from examples/p-chain/etna/utils/random-node-id.ts rename to examples/p-chain/utils/random-node-id.ts diff --git a/examples/p-chain/validate.ts b/examples/p-chain/validate.ts index 29ca3f8c1..fb241bd9c 100644 --- a/examples/p-chain/validate.ts +++ b/examples/p-chain/validate.ts @@ -1,10 +1,12 @@ -import { PrimaryNetworkID } from '../../src/constants/networkIDs'; -import { addTxSignatures } from '../../src/signer'; -import { bech32ToBytes, hexToBuffer } from '../../src/utils'; -import { getContextFromURI } from '../../src/vms/context'; -import { PVMApi, newAddPermissionlessValidatorTx } from '../../src/vms/pvm'; -import { pvmapi } from '../chain_apis'; +import { addTxSignatures, networkIDs, pvm, utils } from '../../src'; import { getEnvVars } from '../utils/getEnvVars'; +import { setupEtnaExample } from './utils/etna-helper'; +import { getRandomNodeId } from './utils/random-node-id'; + +const AMOUNT_TO_VALIDATE_AVAX: number = 1; +const DAYS_TO_VALIDATE: number = 21; + +const nodeId = getRandomNodeId(); const main = async () => { const { @@ -13,46 +15,51 @@ const main = async () => { PRIVATE_KEY, BLS_PUBLIC_KEY, BLS_SIGNATURE, - NODE_ID, } = getEnvVars(); - const { utxos } = await pvmapi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] }); - const context = await getContextFromURI(AVAX_PUBLIC_URL); - const startTime = await new PVMApi().getTimestamp(); + const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL); + + const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] }); + + const startTime = await pvmApi.getTimestamp(); const startDate = new Date(startTime.timestamp); - const start = BigInt(startDate.getTime() / 1000); + const start: bigint = BigInt(startDate.getTime() / 1_000); + const endTime = new Date(startTime.timestamp); - endTime.setDate(endTime.getDate() + 21); - const end = BigInt(endTime.getTime() / 1000); - const nodeID = NODE_ID; - const blsPublicKey = hexToBuffer(BLS_PUBLIC_KEY); - const blsSignature = hexToBuffer(BLS_SIGNATURE); + endTime.setDate(endTime.getDate() + DAYS_TO_VALIDATE); + const end: bigint = BigInt(endTime.getTime() / 1_000); + + const publicKey = utils.hexToBuffer(BLS_PUBLIC_KEY); + + const signature = utils.hexToBuffer(BLS_SIGNATURE); - const tx = newAddPermissionlessValidatorTx( + const tx = pvm.newAddPermissionlessValidatorTx( + { + end, + delegatorRewardsOwner: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], + feeState, + fromAddressesBytes: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], + nodeId, + publicKey, + rewardAddresses: [utils.bech32ToBytes(P_CHAIN_ADDRESS)], + shares: 20 * 1e4, + signature, + start, + subnetId: networkIDs.PrimaryNetworkID.toString(), + utxos, + weight: BigInt(AMOUNT_TO_VALIDATE_AVAX * 1e9), + }, context, - utxos, - [bech32ToBytes(P_CHAIN_ADDRESS)], - nodeID, - PrimaryNetworkID.toString(), - start, - end, - BigInt(1e9), - [bech32ToBytes(P_CHAIN_ADDRESS)], - [bech32ToBytes(P_CHAIN_ADDRESS)], - 1e4 * 20, - undefined, - 1, - 0n, - blsPublicKey, - blsSignature, ); await addTxSignatures({ unsignedTx: tx, - privateKeys: [hexToBuffer(PRIVATE_KEY)], + privateKeys: [utils.hexToBuffer(PRIVATE_KEY)], }); - return pvmapi.issueSignedTx(tx.getSignedTx()); + return pvmApi.issueSignedTx(tx.getSignedTx()); }; -main().then(console.log); +main() + .then(console.log) + .then(() => console.log('Validate node ID:', nodeId)); diff --git a/package.json b/package.json index ca2091ca4..09c23a6f7 100644 --- a/package.json +++ b/package.json @@ -30,9 +30,9 @@ "typecheck": "tsc --skipLibCheck --noEmit", "example": "NODE_OPTIONS='--loader ts-node/esm' ts-node examples/c-chain/export.ts", "example:generateKeys": "NODE_OPTIONS='--loader ts-node/esm' ts-node examples/generate-keys.ts", - "example:createSubnetTx": "NODE_OPTIONS='--loader ts-node/esm' ts-node examples/p-chain/etna/createSubnet.ts", - "example:createChainTx": "NODE_OPTIONS='--loader ts-node/esm' ts-node examples/p-chain/etna/createChain.ts", - "example:convertSubnetTx": "NODE_OPTIONS='--loader ts-node/esm' ts-node examples/p-chain/etna/convertSubnet.ts", + "example:createSubnetTx": "NODE_OPTIONS='--loader ts-node/esm' ts-node examples/p-chain/createSubnet.ts", + "example:createChainTx": "NODE_OPTIONS='--loader ts-node/esm' ts-node examples/p-chain/createChain.ts", + "example:convertSubnetTx": "NODE_OPTIONS='--loader ts-node/esm' ts-node examples/p-chain/convertSubnet.ts", "prepare": "husky install" }, "dependencies": { diff --git a/src/vms/avm/api.ts b/src/vms/avm/api.ts index 1bfcda6b4..238287321 100644 --- a/src/vms/avm/api.ts +++ b/src/vms/avm/api.ts @@ -6,6 +6,7 @@ import type { GetAllBalancesParams, GetAllBalancesResponse, GetTxFeeResponse, + TxFee, } from './models'; export class AVMApi extends AvaxApi { @@ -35,7 +36,11 @@ export class AVMApi extends AvaxApi { ); } - getTxFee(): Promise { - return this.callRpc('getTxFee'); - } + getTxFee = async (): Promise => { + const txFee = await this.callRpc('getTxFee'); + return { + txFee: BigInt(txFee.txFee), + createAssetTxFee: BigInt(txFee.createAssetTxFee), + }; + }; } diff --git a/src/vms/avm/models.ts b/src/vms/avm/models.ts index affefd5af..c7f5e7d79 100644 --- a/src/vms/avm/models.ts +++ b/src/vms/avm/models.ts @@ -40,3 +40,8 @@ export interface GetTxFeeResponse { txFee: string; createAssetTxFee: string; } + +export interface TxFee { + txFee: bigint; + createAssetTxFee: bigint; +} diff --git a/src/vms/context/context.ts b/src/vms/context/context.ts index 81be01d70..b9e1b58c6 100644 --- a/src/vms/context/context.ts +++ b/src/vms/context/context.ts @@ -33,8 +33,8 @@ export const getContextFromURI = async ( pBlockchainID, cBlockchainID, avaxAssetID, - baseTxFee: BigInt(txFee), - createAssetTxFee: BigInt(createAssetTxFee), + baseTxFee: txFee, + createAssetTxFee: createAssetTxFee, networkID, hrp: getHRP(networkID), platformFeeConfig, diff --git a/src/vms/pvm/index.ts b/src/vms/pvm/index.ts index bb14edd96..8682a487f 100644 --- a/src/vms/pvm/index.ts +++ b/src/vms/pvm/index.ts @@ -4,6 +4,6 @@ export * from './api'; export * from './txs/fee'; /** - * @deprecated Exposed Etna builder functions under `e` namespace + * @deprecated PVM builder functions aliased under "e" are deprecated. Please use the builder functions on the root pvm export. Ex: pvm.e.newBaseTx -> pvm.newBaseTx. */ export * as e from './etna-builder';