Skip to content

Commit 683b56f

Browse files
authored
feat: implement IncreaseBalanceTx
Implement IncreaseBalanceTx (#888) * feat: add test transactions and complexity constants * feat: add IncreaseBalanceTx * chore: update ts-jest and jest config * fix: circular dependencies Closes #873. * test: add newIncreaseBalanceTx builder tests * docs: add example for increaseBalanceTx * chore: adjust example envs
1 parent 0097d86 commit 683b56f

31 files changed

+737
-215
lines changed

examples/p-chain/etna/convertSubnet.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ const BALANCE_AVAX: number = 1;
2020
* @param subnetId the ID of the subnet that is created via `createSubnetTx`.
2121
* @returns The resulting transaction's ID.
2222
*/
23-
const convertSubnetTxExmaple = async () => {
23+
const convertSubnetTxExample = async () => {
2424
const {
2525
AVAX_PUBLIC_URL,
2626
P_CHAIN_ADDRESS,
2727
PRIVATE_KEY,
2828
NODE_ID,
2929
BLS_PUBLIC_KEY,
3030
BLS_SIGNATURE,
31-
} = getEnvVars();
31+
} = getEnvVars(['BLS_PUBLIC_KEY', 'BLS_SIGNATURE', 'NODE_ID']);
3232
const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL);
3333

3434
const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] });
@@ -74,4 +74,4 @@ const convertSubnetTxExmaple = async () => {
7474
return pvmApi.issueSignedTx(tx.getSignedTx());
7575
};
7676

77-
convertSubnetTxExmaple().then(console.log);
77+
convertSubnetTxExample().then(console.log);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { addTxSignatures, pvm, utils } from '../../../src';
2+
import { setupEtnaExample } from './utils/etna-helper';
3+
import { getEnvVars } from '../../utils/getEnvVars';
4+
5+
const BALANCE_AVAX: number = 1;
6+
const VALIDATION_ID: string = '';
7+
8+
const increaseBalanceTx = async () => {
9+
const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY } = getEnvVars();
10+
const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL);
11+
12+
const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] });
13+
14+
const testPAddr = utils.bech32ToBytes(P_CHAIN_ADDRESS);
15+
16+
const unsignedTx = pvm.e.newIncreaseBalanceTx(
17+
{
18+
balance: BigInt(BALANCE_AVAX * 1e9),
19+
feeState,
20+
fromAddressesBytes: [testPAddr],
21+
utxos,
22+
validationId: VALIDATION_ID,
23+
},
24+
context,
25+
);
26+
27+
await addTxSignatures({
28+
unsignedTx,
29+
privateKeys: [utils.hexToBuffer(PRIVATE_KEY)],
30+
});
31+
32+
return pvmApi.issueSignedTx(unsignedTx.getSignedTx());
33+
};
34+
35+
await increaseBalanceTx().then(console.log);

examples/utils/getEnvVars.ts

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,58 @@ const P_CHAIN_ADDRESS = process.env['P_CHAIN_ADDRESS'];
33
const PRIVATE_KEY = process.env['PRIVATE_KEY'];
44
const X_CHAIN_ADDRESS = process.env['X_CHAIN_ADDRESS'];
55
const C_CHAIN_ADDRESS = process.env['C_CHAIN_ADDRESS'];
6-
const NODE_ID = process.env['NODE_ID'];
7-
const BLS_PUBLIC_KEY = process.env['BLS_PUBLIC_KEY'];
8-
const BLS_SIGNATURE = process.env['BLS_SIGNATURE'];
96
const CORETH_ADDRESS = process.env['CORETH_ADDRESS'];
107

11-
export const getEnvVars = () => {
8+
type PrimaryEnvKeys =
9+
| 'AVAX_PUBLIC_URL'
10+
| 'P_CHAIN_ADDRESS'
11+
| 'PRIVATE_KEY'
12+
| 'X_CHAIN_ADDRESS'
13+
| 'C_CHAIN_ADDRESS'
14+
| 'CORETH_ADDRESS';
15+
16+
type ExampleEnvs<T extends string> = Record<PrimaryEnvKeys | T, string>;
17+
18+
export const getEnvVars = <T extends string>(
19+
additionalEnvsKeys: T[] = [],
20+
): ExampleEnvs<T> => {
1221
if (
1322
!(
1423
AVAX_PUBLIC_URL &&
1524
P_CHAIN_ADDRESS &&
1625
PRIVATE_KEY &&
1726
X_CHAIN_ADDRESS &&
18-
NODE_ID &&
19-
BLS_PUBLIC_KEY &&
20-
BLS_SIGNATURE &&
2127
C_CHAIN_ADDRESS &&
2228
CORETH_ADDRESS
2329
)
2430
) {
25-
throw new Error('Missing environment variable(s).');
31+
throw new Error(
32+
'Missing required environment variable(s). Please check your .env file.',
33+
);
2634
}
2735

28-
return {
36+
const additionalEnvs = additionalEnvsKeys.reduce((acc, key) => {
37+
const env = process.env[key];
38+
39+
if (!env) {
40+
throw new Error(`Missing environment variable: ${key}`);
41+
}
42+
43+
return {
44+
...acc,
45+
[key]: env,
46+
};
47+
}, {} as Record<T, string>);
48+
49+
const envs: ExampleEnvs<T> = {
50+
...additionalEnvs,
2951
AVAX_PUBLIC_URL,
3052
P_CHAIN_ADDRESS,
3153
PRIVATE_KEY,
3254
X_CHAIN_ADDRESS,
33-
NODE_ID,
34-
BLS_PUBLIC_KEY,
35-
BLS_SIGNATURE,
3655
C_CHAIN_ADDRESS,
3756
CORETH_ADDRESS,
3857
};
58+
59+
return envs;
3960
};

jest.config.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1-
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2-
module.exports = {
1+
import { createDefaultEsmPreset, type JestConfigWithTsJest } from 'ts-jest';
2+
3+
const preset = createDefaultEsmPreset();
4+
5+
const jestConfig: JestConfigWithTsJest = {
6+
...preset,
37
moduleFileExtensions: ['js', 'json', 'ts'],
48
rootDir: 'src',
5-
transform: {
6-
'^.+\\.tsx?$': [
7-
'ts-jest',
8-
{
9-
useESM: true,
10-
},
11-
],
12-
},
139
collectCoverageFrom: ['**/*.(t|j)s'],
1410
coverageDirectory: '../coverage',
1511
testEnvironment: 'node',
1612
coverageProvider: 'v8',
17-
extensionsToTreatAsEsm: ['.ts'],
1813
// Experimental to fix issues with BigInt serialization
1914
// See: https://jestjs.io/docs/configuration#workerthreads
15+
// @ts-expect-error - workerThreads is not in the type definition
2016
workerThreads: true,
2117
};
18+
19+
export default jestConfig;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
"rollup": "4.9.6",
7575
"rollup-plugin-filesize": "10.0.0",
7676
"semantic-release": "21.0.1",
77-
"ts-jest": "29.1.2",
77+
"ts-jest": "29.2.5",
7878
"ts-node": "10.9.2",
7979
"typescript": "5.3.3"
8080
},

src/fixtures/pvm.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
RemoveSubnetValidatorTx,
2020
TransferSubnetOwnershipTx,
2121
TransformSubnetTx,
22+
IncreaseBalanceTx,
2223
} from '../serializable/pvm';
2324
import {
2425
baseTx,
@@ -346,6 +347,12 @@ export const convertSubnetTxBytes = () =>
346347
inputBytes(),
347348
);
348349

350+
export const increaseBalanceTx = () =>
351+
new IncreaseBalanceTx(baseTx(), id(), bigIntPr());
352+
353+
export const increaseBalanceTxBytes = () =>
354+
concatBytes(baseTxbytes(), idBytes(), bigIntPrBytes());
355+
349356
export const pChainOwner = () => new PChainOwner(int(), addresses()());
350357

351358
export const pChainOwnerBytes = () =>

src/serializable/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export enum TypeSymbols {
8282

8383
ConvertSubnetTx = 'pvm.ConvertSubnetTx',
8484
ConvertSubnetValidator = 'pvm.ConvertSubnetValidator',
85+
IncreaseBalanceTx = 'pvm.IncreaseBalanceTx',
8586

8687
PChainOwner = 'pvm.PChainOwner',
8788

src/serializable/fxs/pvm/convertSubnetValidator.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { testPVMCodec } from '../../../fixtures/codec';
12
import {
23
convertSubnetValidator,
34
convertSubnetValidatorBytes,
@@ -10,4 +11,5 @@ testSerialization(
1011
ConvertSubnetValidator,
1112
convertSubnetValidator,
1213
convertSubnetValidatorBytes,
14+
testPVMCodec,
1315
);

src/serializable/fxs/pvm/convertSubnetValidator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Codec } from '../../codec';
33
import { serializable } from '../../common/types';
44
import { BigIntPr, Bytes } from '../../primitives';
55
import { TypeSymbols } from '../../constants';
6-
import { ProofOfPossession } from '../../pvm';
6+
import { ProofOfPossession } from '../../pvm/proofOfPossession';
77
import { NodeId } from '../common';
88
import { PChainOwner } from './pChainOwner';
99

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1+
import { testPVMCodec } from '../../../fixtures/codec';
12
import { pChainOwner, pChainOwnerBytes } from '../../../fixtures/pvm';
23
import { testSerialization } from '../../../fixtures/utils/serializable';
34
import { PChainOwner } from './pChainOwner';
45

5-
testSerialization('PChainOwner', PChainOwner, pChainOwner, pChainOwnerBytes);
6+
testSerialization(
7+
'PChainOwner',
8+
PChainOwner,
9+
pChainOwner,
10+
pChainOwnerBytes,
11+
testPVMCodec,
12+
);

src/serializable/pvm/codec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { TransferSubnetOwnershipTx } from './transferSubnetOwnershipTx';
1919
import { TransformSubnetTx } from './transformSubnetTx';
2020
import { BaseTx } from './baseTx';
2121
import { ConvertSubnetTx } from './convertSubnetTx';
22+
import { IncreaseBalanceTx } from './increaseBalanceTx';
2223

2324
/**
2425
* @see https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/txs/codec.go#L35
@@ -60,6 +61,10 @@ export const codec = new Codec([
6061
BaseTx, // 34
6162

6263
ConvertSubnetTx, // 35
64+
// Replace these with the actual txs when they are implemented
65+
...new Array(2), // 36-37 RegisterSubnetValidatorTx, SetSubnetValidatorWeightTx
66+
IncreaseBalanceTx, // 38
67+
// DisableSubnetValidatorTx, // 39
6368
]);
6469

6570
let manager: Manager;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { testPVMCodec } from '../../fixtures/codec';
2+
import { increaseBalanceTx, increaseBalanceTxBytes } from '../../fixtures/pvm';
3+
import { testSerialization } from '../../fixtures/utils/serializable';
4+
import { IncreaseBalanceTx } from './increaseBalanceTx';
5+
6+
testSerialization(
7+
'IncreaseBalanceTx',
8+
IncreaseBalanceTx,
9+
increaseBalanceTx,
10+
increaseBalanceTxBytes,
11+
testPVMCodec,
12+
);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { pack, unpack } from '../../utils/struct';
2+
import { BaseTx } from '../avax/baseTx';
3+
import type { Codec } from '../codec';
4+
import { serializable } from '../common/types';
5+
import { TypeSymbols } from '../constants';
6+
import { Id } from '../fxs/common';
7+
import { BigIntPr } from '../primitives';
8+
import { PVMTx } from './abstractTx';
9+
10+
@serializable()
11+
export class IncreaseBalanceTx extends PVMTx {
12+
_type = TypeSymbols.IncreaseBalanceTx;
13+
14+
constructor(
15+
public readonly baseTx: BaseTx,
16+
/**
17+
* The corresponding Validator ID.
18+
*/
19+
public readonly validationId: Id,
20+
/**
21+
* Balance <= sum of AVAX inputs - sum of AVAX outputs - Tx fee
22+
*/
23+
public readonly balance: BigIntPr,
24+
) {
25+
super();
26+
}
27+
28+
static fromBytes(
29+
bytes: Uint8Array,
30+
codec: Codec,
31+
): [increaseBalanceTx: IncreaseBalanceTx, rest: Uint8Array] {
32+
const [baseTx, validationId, balance, rest] = unpack(
33+
bytes,
34+
[BaseTx, Id, BigIntPr],
35+
codec,
36+
);
37+
38+
return [new IncreaseBalanceTx(baseTx, validationId, balance), rest];
39+
}
40+
41+
toBytes(codec: Codec) {
42+
return pack([this.baseTx, this.validationId, this.balance], codec);
43+
}
44+
}

src/serializable/pvm/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { AbstractSubnetTx } from './abstractSubnetTx';
2020
import { TransferSubnetOwnershipTx } from './transferSubnetOwnershipTx';
2121
import { TransformSubnetTx } from './transformSubnetTx';
2222
import { ConvertSubnetTx } from './convertSubnetTx';
23+
import { IncreaseBalanceTx } from './increaseBalanceTx';
2324

2425
export * from './typeGuards';
2526
export {
@@ -46,4 +47,5 @@ export {
4647
TransferSubnetOwnershipTx,
4748
TransformSubnetTx,
4849
ConvertSubnetTx,
50+
IncreaseBalanceTx,
4951
};

src/serializable/pvm/typeGuards.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
isPvmBaseTx,
1919
isTransferSubnetOwnershipTx,
2020
isTransformSubnetTx,
21+
isConvertSubnetTx,
22+
isIncreaseBalanceTx,
2123
} from './typeGuards';
2224

2325
const cases: [any, TypeSymbols][] = [
@@ -38,6 +40,8 @@ const cases: [any, TypeSymbols][] = [
3840
[isRewardValidatorTx, TypeSymbols.RewardValidatorTx],
3941
[isTransferSubnetOwnershipTx, TypeSymbols.TransferSubnetOwnershipTx],
4042
[isTransformSubnetTx, TypeSymbols.TransformSubnetTx],
43+
[isConvertSubnetTx, TypeSymbols.ConvertSubnetTx],
44+
[isIncreaseBalanceTx, TypeSymbols.IncreaseBalanceTx],
4145
];
4246

4347
const guards = cases.map((caseItem) => caseItem[0]);

src/serializable/pvm/typeGuards.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type { TransferSubnetOwnershipTx } from './transferSubnetOwnershipTx';
1717
import { TypeSymbols } from '../constants';
1818
import type { TransformSubnetTx } from './transformSubnetTx';
1919
import type { ConvertSubnetTx } from './convertSubnetTx';
20+
import type { IncreaseBalanceTx } from './increaseBalanceTx';
2021

2122
export function isPvmBaseTx(tx: Transaction): tx is BaseTx {
2223
return tx._type === TypeSymbols.PvmBaseTx;
@@ -92,6 +93,10 @@ export function isConvertSubnetTx(tx: Transaction): tx is ConvertSubnetTx {
9293
return tx._type === TypeSymbols.ConvertSubnetTx;
9394
}
9495

96+
export function isIncreaseBalanceTx(tx: Transaction): tx is IncreaseBalanceTx {
97+
return tx._type === TypeSymbols.IncreaseBalanceTx;
98+
}
99+
95100
export function isEmptySigner(
96101
signer: Signer | SignerEmpty,
97102
): signer is SignerEmpty {
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1+
import { testPVMCodec } from '../../fixtures/codec';
12
import { validator, validatorBytes } from '../../fixtures/pvm';
23
import { testSerialization } from '../../fixtures/utils/serializable';
34
import { Validator } from './validator';
45

5-
testSerialization('Validator', Validator, validator, validatorBytes);
6+
testSerialization(
7+
'Validator',
8+
Validator,
9+
validator,
10+
validatorBytes,
11+
testPVMCodec,
12+
);

0 commit comments

Comments
 (0)