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 94% rename from examples/p-chain/etna/convertSubnetToL1.ts rename to examples/p-chain/convertSubnetToL1.ts index c1d61eb03..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; @@ -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/createChain.ts similarity index 87% rename from examples/p-chain/etna/createChain.ts rename to examples/p-chain/createChain.ts index a0164888d..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'; /** @@ -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/createSubnet.ts similarity index 82% rename from examples/p-chain/etna/createSubnet.ts rename to examples/p-chain/createSubnet.ts index c1fe5b9a5..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 () => { @@ -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/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 a0a5989b2..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.e.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 eb330b632..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.e.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 2c5fb6523..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.e.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 b7329edd0..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.e.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 041e0e6b7..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.e.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 82% rename from examples/p-chain/etna/increaseBalanceTx.ts rename to examples/p-chain/increaseBalanceTx.ts index bf0fc58de..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 = ''; @@ -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/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/fixtures/context.ts b/src/fixtures/context.ts index 362805fb2..23c7d3e17 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: { 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..20ae88fab 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, @@ -42,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, @@ -59,26 +62,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, @@ -312,348 +295,189 @@ 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], + utxos: [utxo1, utxo2], + outputs: [output], + feeState: feeState(), + }, testContext, - 'C', - [testAddress1], - [utxo1, utxo2], - [output], - ).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: 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], + utxos: [utxo], + outputs: [output1, output2, output3], + feeState: feeState(), + }, testContext, - 'C', - [testAddress1], - [utxo], - [output1, output2, output3], - ).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: 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( - testContext, - 'C', - [testAddress1], - [utxo1, utxo2], - [output1, output2, output3], - ).getTx() as AvaxTx; - - const amounts = getBurnedAmountByTx(tx, testContext); - expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.baseTxFee, - ); - }); - }); - - 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 = pvmExportTx( + { + destinationChainId: 'C', + fromAddressesBytes: [testAddress1], + utxos: [utxo1, utxo2], + outputs: [output1, output2, output3], + feeState: feeState(), + }, testContext, - 'C', - [utxo1, utxo2], - [testAddress2], - [testAddress1], - ).getTx() as AvaxTx; - - const amounts = getBurnedAmountByTx(tx, testContext); - expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.baseTxFee, ); - }); - }); - - 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 tx = unsignedTx.getTx() as ExportTx; + const { inputs, outputs } = tx.baseTx; 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 [, , expectedFee] = checkFeeIsCorrect({ + unsignedTx, + inputs, + outputs, + feeState: feeState(), + }); - const amounts = getBurnedAmountByTx(tx, testContext); expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.addPrimaryNetworkValidatorFee, - ); + expect(amounts.get(testContext.avaxAssetID)).toEqual(expectedFee); }); }); - 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; + describe('import tx', () => { + it('calculates the burned amount of import tx correctly', () => { const utxo1 = getUtxoMock(testUTXOID1, 50000000n); const utxo2 = getUtxoMock(testUTXOID2, 60000000n); - const tx = newAddDelegatorTx( + const unsignedTx = pvmImportTx( + { + sourceChainId: 'C', + utxos: [utxo1, utxo2], + toAddressesBytes: [testAddress2], + fromAddressesBytes: [testAddress1], + feeState: feeState(), + }, 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 tx = unsignedTx.getTx() as ImportTx; + const { inputs, outputs } = tx.baseTx; const amounts = getBurnedAmountByTx(tx, testContext); + const [, , expectedFee] = checkFeeIsCorrect({ + unsignedTx, + inputs, + outputs, + feeState: feeState(), + }); expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.addPrimaryNetworkDelegatorFee, - ); + 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], + subnetOwners: [testAddress1], + feeState: 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 tx = unsignedTx.getTx() as CreateSubnetTx; + const { inputs, outputs } = tx.baseTx; const amounts = getBurnedAmountByTx(tx, testContext); + const [, , expectedFee] = checkFeeIsCorrect({ + unsignedTx, + inputs, + outputs, + feeState: feeState(), + }); expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.createBlockchainTxFee, - ); + 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: 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 tx = unsignedTx.getTx() as AddSubnetValidatorTx; + const { inputs, outputs } = tx.baseTx; const amounts = getBurnedAmountByTx(tx, testContext); + const [, , expectedFee] = checkFeeIsCorrect({ + unsignedTx, + inputs, + outputs, + feeState: feeState(), + }); expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual( - testContext.addSubnetValidatorFee, - ); + expect(amounts.get(testContext.avaxAssetID)).toEqual(expectedFee); }); }); 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 5efb0b54b..ea11b8e60 100644 --- a/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts +++ b/src/utils/validateBurnedAmount/validateStaticBurnedAmount.test.ts @@ -12,29 +12,7 @@ import { newExportTx as avmExportTx, newImportTx as avmImportTx, } from '../../vms/avm'; -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( @@ -92,235 +70,6 @@ describe('validateStaticBurnedAmount', () => { ), correctBurnedAmount: testContext.baseTxFee, }, - { - name: 'base tx on P', - unsignedTx: pvmBaseTx( - testContext, - [testAddress1], - [utxoMock], - [outputMock], - ), - correctBurnedAmount: testContext.baseTxFee, - }, - { - name: 'export from P', - unsignedTx: pvmExportTx( - testContext, - 'C', - [testAddress1], - [utxoMock], - [outputMock], - ), - correctBurnedAmount: testContext.baseTxFee, - }, - { - name: 'import to P', - unsignedTx: pvmImportTx( - 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( - 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( - testContext, - [utxoMock], - [testAddress1], - Id.fromHex(testSubnetId).toString(), - [0, 2], - [testAddress2], - ), - correctBurnedAmount: testContext.baseTxFee, - }, ]; describe.each(testData)('$name', ({ unsignedTx, correctBurnedAmount }) => { diff --git a/src/utils/validateBurnedAmount/validateStaticBurnedAmount.ts b/src/utils/validateBurnedAmount/validateStaticBurnedAmount.ts index ce272ed86..86fa9d2f0 100644 --- a/src/utils/validateBurnedAmount/validateStaticBurnedAmount.ts +++ b/src/utils/validateBurnedAmount/validateStaticBurnedAmount.ts @@ -1,29 +1,13 @@ 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 { isAvmBaseTx, isExportTx as isAvmExportTx, isImportTx as isAvmImportTx, } from '../../serializable/avm'; -import { PrimaryNetworkID } from '../../constants/networkIDs'; /** - * Validate static burned amount for avalanche x/p transactions + * Validate static burned amount for avalanche x transactions * * @param unsignedTx: unsigned transaction * @param context @@ -42,67 +26,11 @@ 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) || - isAvmExportTx(tx) || - isAvmImportTx(tx) || - isPvmExportTx(tx) || - isPvmImportTx(tx) || - isRemoveSubnetValidatorTx(tx) || - isTransferSubnetOwnershipTx(tx) - ) { + if (isAvmBaseTx(tx) || isAvmExportTx(tx) || isAvmImportTx(tx)) { 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..238287321 100644 --- a/src/vms/avm/api.ts +++ b/src/vms/avm/api.ts @@ -5,6 +5,8 @@ import type { BuildGenesisResponse, GetAllBalancesParams, GetAllBalancesResponse, + GetTxFeeResponse, + TxFee, } from './models'; export class AVMApi extends AvaxApi { @@ -33,4 +35,12 @@ export class AVMApi extends AvaxApi { getAllBalancesParams, ); } + + getTxFee = async (): Promise => { + const txFee = await this.callRpc('getTxFee'); + return { + txFee: BigInt(txFee.txFee), + createAssetTxFee: BigInt(txFee.createAssetTxFee), + }; + }; } diff --git a/src/vms/avm/builder.test.ts b/src/vms/avm/builder.test.ts index 1c78ef9ce..4faa7e164 100644 --- a/src/vms/avm/builder.test.ts +++ b/src/vms/avm/builder.test.ts @@ -22,8 +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 { newBaseTx, newExportTx, newImportTx } from './builder'; describe('AVMBuilder', () => { let utxos: Utxo[]; diff --git a/src/vms/avm/models.ts b/src/vms/avm/models.ts index 888397c24..c7f5e7d79 100644 --- a/src/vms/avm/models.ts +++ b/src/vms/avm/models.ts @@ -35,3 +35,13 @@ export interface GetAddressTxsResponse { txIDs: string[]; cursor: bigint; } + +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 5403a31f4..b9e1b58c6 100644 --- a/src/vms/context/context.ts +++ b/src/vms/context/context.ts @@ -17,17 +17,8 @@ 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, createAssetTxFee } = await xChainApi.getTxFee(); + const { blockchainID: xBlockchainID } = await info.getBlockchainId('X'); const { blockchainID: pBlockchainID } = await info.getBlockchainId('P'); const { blockchainID: cBlockchainID } = await info.getBlockchainId('C'); @@ -42,15 +33,8 @@ export const getContextFromURI = async ( pBlockchainID, cBlockchainID, avaxAssetID, - baseTxFee, - createAssetTxFee, - createSubnetTxFee, - transformSubnetTxFee, - createBlockchainTxFee, - addPrimaryNetworkValidatorFee, - addPrimaryNetworkDelegatorFee, - addSubnetValidatorFee, - addSubnetDelegatorFee, + baseTxFee: txFee, + createAssetTxFee: createAssetTxFee, networkID, hrp: getHRP(networkID), platformFeeConfig, diff --git a/src/vms/context/model.ts b/src/vms/context/model.ts index bb807ff7a..56b3ddeac 100644 --- a/src/vms/context/model.ts +++ b/src/vms/context/model.ts @@ -9,13 +9,6 @@ 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; 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/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, + ]; +}; diff --git a/src/vms/pvm/index.ts b/src/vms/pvm/index.ts index 73091b284..8682a487f 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 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';