@avalanche-sdk/client
Version:
A TypeScript SDK for interacting with the Avalanche network through JSON-RPC APIs. This SDK provides a comprehensive set of tools to interact with all Avalanche chains (P-Chain, X-Chain, C-Chain) and various APIs, including wallet functionality for transa
215 lines (193 loc) • 8 kB
text/typescript
import { pvm, pvmSerial, UnsignedTx, utils } from "@avalabs/avalanchejs";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { avalancheFuji } from "../../../chains";
import { createAvalancheWalletClient } from "../../../clients/createAvalancheWalletClient";
import { getTxFromBytes } from "../../../utils";
import { testContext } from "../fixtures/testContext";
import { account1, account2, feeState } from "../fixtures/transactions/common";
import { getPChainMockServer } from "../fixtures/transactions/pChain";
import { checkOutputs } from "../fixtures/utils";
import { Output } from "../types/common";
import { toTransferableOutput } from "../utils";
const testInputAmount = 1;
const pChainWorker = getPChainMockServer({});
describe("addSubnetValidatorTx", () => {
const walletClient = createAvalancheWalletClient({
account: account1,
chain: avalancheFuji,
transport: {
type: "http",
url: "https://api.avax-test.network",
},
});
beforeAll(() => {
pChainWorker.listen();
});
afterAll(() => {
pChainWorker.close();
});
it("should create correct outputs and fees", async () => {
const changeAddresses = [account1.getXPAddress("P", "fuji")];
const endTime = Date.now() + 2 * 24 * 60 * 60 * 1000; // 2 days from now
const mockTxParams = {
changeAddresses, // staked outputs will be owned by these addresses
subnetId: "2CDLCyFr7x4LcxaJ82rE38gnSk4gcVTeFdJeFbwZRd5fgM1gDH",
nodeId: "NodeID-LbijL9cqXkmq2Q8oQYYGs8LmcSRhnrDWJ",
weight: 12345n,
end: BigInt(endTime),
subnetAuth: [0],
context: testContext,
};
const testOutputs: Output[] = [];
const txnRequest = await walletClient.pChain.prepareAddSubnetValidatorTxn(
mockTxParams
);
const outputs = (
txnRequest.tx.getTx() as pvmSerial.BaseTx
).baseTx.outputs.map(toTransferableOutput);
const fee = pvm.calculateFee(
txnRequest.tx.getTx(),
testContext.platformFeeConfig.weights,
feeState().price
);
const expectedFeesInAvax = Number(fee) / 1e9;
const expectedChangeAmount = testInputAmount - expectedFeesInAvax;
// expected change output
testOutputs.push({
amount: expectedChangeAmount,
addresses: changeAddresses,
});
// check change and staked outputs
checkOutputs(testOutputs, outputs);
// actual burned amount is same as fees
const allInputAmounts = (txnRequest.tx.getTx() as pvmSerial.BaseTx)
.getInputs()
.reduce((acc, i) => acc + i.amount(), 0n);
const allOutputAmounts = outputs.reduce(
(acc, i) => acc + i.output.amount(),
0n
);
expect(
allInputAmounts - allOutputAmounts,
"expected and actual burned amount mismatch"
).toBe(BigInt(expectedFeesInAvax * 1e9));
});
it("should create correct staking details", async () => {
const changeAddresses = [account1.getXPAddress("P", "fuji")];
const endTime = 1234356770;
const mockTxParams = {
changeAddresses, // staked outputs will be owned by these addresses
subnetId: "2CDLCyFr7x4LcxaJ82rE38gnSk4gcVTeFdJeFbwZRd5fgM1gDH",
nodeId: "NodeID-LbijL9cqXkmq2Q8oQYYGs8LmcSRhnrDWJ",
weight: 12345n,
end: BigInt(endTime),
subnetAuth: [0],
context: testContext,
};
const result = await walletClient.pChain.prepareAddSubnetValidatorTxn(
mockTxParams
);
// check staking details
const vldr = (result.tx.getTx() as pvmSerial.AddSubnetValidatorTx)
.subnetValidator.validator;
expect(vldr.nodeId.value(), "nodeId mismatch").toBe(mockTxParams.nodeId);
expect(vldr.endTime.value(), "endTime mismatch").toBe(
BigInt(mockTxParams.end)
);
expect(vldr.weight.value(), "weight mismatch").toBe(
BigInt(mockTxParams.weight)
);
expect(
(
result.tx.getTx() as pvmSerial.AddSubnetValidatorTx
).subnetValidator.subnetId.value(),
"subnetId mismatch"
).toBe(mockTxParams.subnetId);
expect(
(result.tx.getTx() as pvmSerial.AddSubnetValidatorTx)
.getSubnetAuth()
.values(),
"subnetAuth mismatch"
).deep.equal(mockTxParams.subnetAuth);
});
it("should correctly sign inputs and subnet auth", async () => {
const changeAddresses = [account1.getXPAddress("P", "fuji")];
const endTime = 1234356770;
const mockTxParams = {
changeAddresses, // staked outputs will be owned by these addresses
subnetId: "2CDLCyFr7x4LcxaJ82rE38gnSk4gcVTeFdJeFbwZRd5fgM1gDH",
nodeId: "NodeID-LbijL9cqXkmq2Q8oQYYGs8LmcSRhnrDWJ",
weight: 12345n,
end: BigInt(endTime),
subnetAuth: [0],
context: testContext,
};
const txnRequest = await walletClient.pChain.prepareAddSubnetValidatorTxn(
mockTxParams
);
const signedTx = await walletClient.signXPTransaction(txnRequest);
const [txn, credentials] = getTxFromBytes(signedTx.signedTxHex, "P");
const unsignedTxnInstance = new UnsignedTx(
txn,
[],
new utils.AddressMaps(),
credentials
);
expect(
unsignedTxnInstance.hasAllSignatures(),
"transaction is not signed"
).toBe(true);
});
it("should correctly sign multi-sig subnet auth", async () => {
const changeAddresses = [account1.getXPAddress("P", "fuji")];
const endTime = 1234356770;
const mockTxParams = {
changeAddresses, // staked outputs will be owned by these addresses
subnetId: "SLomSuJLyG9qk7KLcWevdcZ1i7kN2qTLNUytJLhkwPdxAAgoa",
nodeId: "NodeID-LbijL9cqXkmq2Q8oQYYGs8LmcSRhnrDWJ",
weight: 12345n,
end: BigInt(endTime),
subnetAuth: [0],
context: testContext,
};
const result = await walletClient.pChain.prepareAddSubnetValidatorTxn(
mockTxParams
);
const partialSignedTx = await walletClient.signXPTransaction(result);
const signedTx = await walletClient.signXPTransaction({
...partialSignedTx,
account: account2,
});
const [txn, credentials] = getTxFromBytes(signedTx.signedTxHex, "P");
const unsignedTxnInstance = new UnsignedTx(
txn,
[],
new utils.AddressMaps(),
credentials
);
expect(
unsignedTxnInstance.hasAllSignatures(),
"transaction is not signed"
).toBe(true);
});
it("should give correct transaction hash", async () => {
const changeAddresses = [account1.getXPAddress("P", "fuji")];
const endTime = 1234356770;
const mockTxParams = {
changeAddresses, // staked outputs will be owned by these addresses
subnetId: "2CDLCyFr7x4LcxaJ82rE38gnSk4gcVTeFdJeFbwZRd5fgM1gDH",
nodeId: "NodeID-LbijL9cqXkmq2Q8oQYYGs8LmcSRhnrDWJ",
weight: 12345n,
end: BigInt(endTime),
subnetAuth: [0],
context: testContext,
};
const result = await walletClient.pChain.prepareAddSubnetValidatorTxn(
mockTxParams
);
const signedTx = await walletClient.signXPTransaction(result);
expect(signedTx.signedTxHex, "transaction hash mismatch").toBe(
"0x00000000000d0000000500000000000000000000000000000000000000000000000000000000000000000000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000007000000003b9ac69b000000000000000000000001000000012a705f0a71d8b6e19d5e955b19d683ca6d68237000000001ba5eeb9cf2e099134ffba3d2ce1310fa6f07413e4512044cdd1caba9e03fa8c90000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000005000000003b9aca00000000010000000000000000d6fb5ded320a43a674fc4fe007c631532ce7a9b00000000000000000000000004992ca2200000000000030399d27db382a254d2ac9908dafa65f0eb4cebc8dce746fd2bf0f759e61509d618f0000000a000000010000000000000002000000090000000192b089f4d0c024c1faff3692d175f8c9c92a1ec55cb5060b3eb48e6e011761af1be0745c02f927997e5db6d058f1c7559e03a9a9da5e78cab605d31baf7ff47900000000090000000192b089f4d0c024c1faff3692d175f8c9c92a1ec55cb5060b3eb48e6e011761af1be0745c02f927997e5db6d058f1c7559e03a9a9da5e78cab605d31baf7ff47900aaea75e3"
);
});
});