From 36c6696497aa2cb5a038c77b8a9528a263cfec20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Tue, 22 Feb 2022 14:01:56 +0100 Subject: [PATCH 01/29] Updated chain parameters with new version --- src/types.ts | 120 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 107 insertions(+), 13 deletions(-) diff --git a/src/types.ts b/src/types.ts index d38314d21..d1cb4c41f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -257,22 +257,44 @@ export interface FinalizationData { finalizers: PartyInfo[]; } -export interface ExchangeRate { +export interface Ratio { numerator: bigint; denominator: bigint; } +export type ExchangeRate = Ratio; + +export interface InclusiveRange { + min: N; + max: N; +} + +/** + * Used as index of a reward period, or as a number of reward periods. + * A reward period spans a number of epochs, as defined in chain parameters as "rewardPeriodLength" + */ +export type RewardPeriod = bigint; +/** Index of an epoch, or number of epochs. */ +export type Epoch = bigint; + export interface TransactionFeeDistribution { baker: RewardFraction; gasAccount: RewardFraction; } -export interface MintDistribution { - mintPerSlot: number; +interface MintDistributionBase { bakingReward: RewardFraction; finalizationReward: RewardFraction; } +interface MintDistributionV0 { + mintPerSlot: number; +} + +type MintDistributionV1 = MintDistributionBase; + +export type MintDistribution = MintDistributionV0 | MintDistributionV1; + export interface GasRewards { baker: RewardFraction; finalizationProof: RewardFraction; @@ -280,23 +302,76 @@ export interface GasRewards { chainUpdate: RewardFraction; } -export interface RewardParameters { +interface RewardParametersBase { transactionFeeDistribution: TransactionFeeDistribution; - mintDistribution: MintDistribution; gASRewards: GasRewards; } -export interface ChainParameters { +interface RewardParametersV0 extends RewardParametersBase { + mintDistribution: MintDistributionV0; +} + +interface RewardParametersV1 extends RewardParametersBase { + mintDistribution: MintDistributionV1; +} + +export type RewardParameters = RewardParametersV0 | RewardParametersV1; + +interface CooldownParametersV0 { + bakerCooldownEpochs: Epoch; +} + +interface CooldownParametersV1 { + poolOwnerCooldown: RewardPeriod; + delegatorCooldown: RewardPeriod; +} + +interface PoolParametersV0 { + minimumThresholdForBaking: bigint; +} + +interface PoolParametersV1 { + finalizationCommissionLPool: RewardFraction; + bakingCommissionLPool: RewardFraction; + transactionCommissionLPool: RewardFraction; + finalizationCommissionRange: InclusiveRange; + bakingCommissionRange: InclusiveRange; + transactionCommissionRange: InclusiveRange; + minimumEquityCapital: bigint; + capitalBound: RewardFraction; + leverageBound: Ratio; +} + +interface TimeParametersV1 { + rewardPeriodLength: Epoch; + mintPerPayday: number; +} + +interface ChainParametersBase { electionDifficulty: number; euroPerEnergy: ExchangeRate; microGTUPerEuro: ExchangeRate; accountCreationLimit: number; - bakerCooldownEpochs: bigint; - minimumThresholdForBaking: bigint; - rewardParameters: RewardParameters; foundationAccountIndex: bigint; } +interface ChainParametersV0 + extends ChainParametersBase, + CooldownParametersV0, + PoolParametersV0 { + rewardParameters: RewardParametersV0; +} + +interface ChainParametersV1 + extends ChainParametersBase, + CooldownParametersV1, + PoolParametersV1, + TimeParametersV1 { + rewardParameters: RewardParametersV1; +} + +export type ChainParameters = ChainParametersV0 | ChainParametersV1; + export interface Authorization { threshold: number; authorizedKeys: number[]; @@ -359,18 +434,37 @@ interface UpdateQueues { level2Keys: UpdateQueue; } -export interface Updates { - chainParameters: ChainParameters; +interface UpdatesBase { keys: Keys; updateQueues: UpdateQueues; } -export interface BlockSummary { +interface UpdatesV0 extends UpdatesBase { + chainParameters: ChainParametersV0; +} + +interface UpdatesV1 extends UpdatesBase { + chainParameters: ChainParametersV1; +} + +export type Updates = UpdatesV0 | UpdatesV1; + +interface BlockSummaryBase { finalizationData: FinalizationData; transactionSummaries: TransactionSummary[]; - updates: Updates; } +interface BlockSummaryV0 extends BlockSummaryBase { + updates: UpdatesV0; +} + +interface BlockSummaryV1 extends BlockSummaryBase { + updates: UpdatesV1; + protocolVersion: bigint; +} + +export type BlockSummary = BlockSummaryV0 | BlockSummaryV1; + export interface BlockInfo { blockParent: string; blockHash: string; From a7250253104ba4139370209069d8b793ca6c07f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Tue, 22 Feb 2022 15:53:43 +0100 Subject: [PATCH 02/29] New BlockSummary version implemented --- src/types.ts | 79 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/src/types.ts b/src/types.ts index d1cb4c41f..8b87d1d29 100644 --- a/src/types.ts +++ b/src/types.ts @@ -282,7 +282,7 @@ export interface TransactionFeeDistribution { gasAccount: RewardFraction; } -interface MintDistributionBase { +interface MintDistributionCommon { bakingReward: RewardFraction; finalizationReward: RewardFraction; } @@ -291,7 +291,7 @@ interface MintDistributionV0 { mintPerSlot: number; } -type MintDistributionV1 = MintDistributionBase; +type MintDistributionV1 = MintDistributionCommon; export type MintDistribution = MintDistributionV0 | MintDistributionV1; @@ -302,16 +302,16 @@ export interface GasRewards { chainUpdate: RewardFraction; } -interface RewardParametersBase { +interface RewardParametersCommon { transactionFeeDistribution: TransactionFeeDistribution; gASRewards: GasRewards; } -interface RewardParametersV0 extends RewardParametersBase { +interface RewardParametersV0 extends RewardParametersCommon { mintDistribution: MintDistributionV0; } -interface RewardParametersV1 extends RewardParametersBase { +interface RewardParametersV1 extends RewardParametersCommon { mintDistribution: MintDistributionV1; } @@ -347,7 +347,7 @@ interface TimeParametersV1 { mintPerPayday: number; } -interface ChainParametersBase { +interface ChainParametersCommon { electionDifficulty: number; euroPerEnergy: ExchangeRate; microGTUPerEuro: ExchangeRate; @@ -356,14 +356,14 @@ interface ChainParametersBase { } interface ChainParametersV0 - extends ChainParametersBase, + extends ChainParametersCommon, CooldownParametersV0, PoolParametersV0 { rewardParameters: RewardParametersV0; } interface ChainParametersV1 - extends ChainParametersBase, + extends ChainParametersCommon, CooldownParametersV1, PoolParametersV1, TimeParametersV1 { @@ -377,7 +377,7 @@ export interface Authorization { authorizedKeys: number[]; } -export interface Authorizations { +interface AuthorizationsCommon { emergency: Authorization; microGTUPerEuro: Authorization; euroPerEnergy: Authorization; @@ -393,17 +393,35 @@ export interface Authorizations { keys: VerifyKey[]; } +type AuthorizationsV0 = AuthorizationsCommon; + +interface AuthorizationsV1 extends AuthorizationsCommon { + cooldownParameters: Authorization; + timeParameters: Authorization; +} + +export type Authorizations = AuthorizationsV0 | AuthorizationsV1; + export interface KeysWithThreshold { keys: VerifyKey[]; threshold: number; } -export interface Keys { +interface KeysCommon { rootKeys: KeysWithThreshold; level1Keys: KeysWithThreshold; - level2Keys: Authorizations; } +interface KeysV0 extends KeysCommon { + level2Keys: AuthorizationsV0; +} + +interface KeysV1 extends KeysCommon { + level2Keys: AuthorizationsV1; +} + +export type Keys = KeysV0 | KeysV1; + export interface UpdateQueueQueue { effectiveTime: Date; // TODO Update the type of update to a generic update transaction when @@ -417,7 +435,7 @@ export interface UpdateQueue { queue: UpdateQueueQueue; } -interface UpdateQueues { +interface UpdateQueuesCommon { microGTUPerEuro: UpdateQueue; euroPerEnergy: UpdateQueue; transactionFeeDistribution: UpdateQueue; @@ -434,33 +452,52 @@ interface UpdateQueues { level2Keys: UpdateQueue; } -interface UpdatesBase { - keys: Keys; - updateQueues: UpdateQueues; +type UpdateQueuesV0 = UpdateQueuesCommon; + +interface UpdateQueuesV1 extends UpdateQueuesCommon { + cooldownParameters: UpdateQueue; + timeParameters: UpdateQueue; +} + +export type UpdateQueues = UpdateQueuesV0 | UpdateQueuesV1; + +interface ProtocolUpdate { + message: string; + specificationUrl: string; + specificationHash: string; + specificationAuxiliaryData: string; +} + +interface UpdatesCommon { + protocolUpdate: ProtocolUpdate | undefined; } -interface UpdatesV0 extends UpdatesBase { +interface UpdatesV0 extends UpdatesCommon { chainParameters: ChainParametersV0; + updateQueues: UpdateQueuesV0; + keys: KeysV0; } -interface UpdatesV1 extends UpdatesBase { +interface UpdatesV1 extends UpdatesCommon { chainParameters: ChainParametersV1; + updateQueues: UpdateQueuesV1; + keys: KeysV1; } export type Updates = UpdatesV0 | UpdatesV1; -interface BlockSummaryBase { +interface BlockSummaryCommon { finalizationData: FinalizationData; transactionSummaries: TransactionSummary[]; } -interface BlockSummaryV0 extends BlockSummaryBase { +interface BlockSummaryV0 extends BlockSummaryCommon { updates: UpdatesV0; } -interface BlockSummaryV1 extends BlockSummaryBase { +interface BlockSummaryV1 extends BlockSummaryCommon { updates: UpdatesV1; - protocolVersion: bigint; + protocolVersion: bigint; // Protocol version 4 and onwards } export type BlockSummary = BlockSummaryV0 | BlockSummaryV1; From aa3c15e862e2e5ab7150c564235b8be924f4a6a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Thu, 24 Feb 2022 10:11:14 +0100 Subject: [PATCH 03/29] =?UTF-8?q?Updated=20account=20info=20model=20to=20m?= =?UTF-8?q?atch=20delegation=C2=A0protocol?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client.ts | 8 +++---- src/types.ts | 62 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/client.ts b/src/client.ts index f9f1a4fe4..c191810b4 100644 --- a/src/client.ts +++ b/src/client.ts @@ -25,8 +25,8 @@ import { AccountTransaction, AccountTransactionSignature, ArInfo, - BakerReduceStakePendingChange, - BakerRemovalPendingChange, + ReduceStakePendingChange, + RemovalPendingChange, BlockInfo, BlockSummary, ChainParameters, @@ -210,8 +210,8 @@ export default class ConcordiumNodeClient { | keyof AccountReleaseSchedule | keyof ReleaseSchedule | keyof AccountBakerDetails - | keyof BakerReduceStakePendingChange - | keyof BakerRemovalPendingChange + | keyof ReduceStakePendingChange + | keyof RemovalPendingChange )[] = [ 'accountAmount', 'accountNonce', diff --git a/src/types.ts b/src/types.ts index 8b87d1d29..094033e73 100644 --- a/src/types.ts +++ b/src/types.ts @@ -656,29 +656,74 @@ export interface InitialAccountCredential { contents: InitialCredentialDeploymentValues; } -export interface BakerReduceStakePendingChange { +export interface ReduceStakePendingChange { change: 'ReduceStake'; newStake: bigint; epoch: bigint; } -export interface BakerRemovalPendingChange { +export interface RemovalPendingChange { change: 'RemoveBaker'; epoch: bigint; } -export type BakerPendingChange = - | BakerReduceStakePendingChange - | BakerRemovalPendingChange; +export type StakePendingChange = + | ReduceStakePendingChange + | RemovalPendingChange; + +export enum OpenStatus { + OpenForAll = 0, + ClosedForNew = 1, + ClosedForAll = 2, +} + +export interface CommissionRates { + transactionCommission: RewardFraction; + bakingCommission: RewardFraction; + finalizationCommission: RewardFraction; +} + +export type BakerId = bigint; +export enum DelegationTargetType { + LPool = 'L-Pool', + Baker = 'Baker', +} + +export interface DelegationTargetLPool { + delegationType: DelegationTargetType.LPool; +} + +export interface DelegationTargetBaker { + delegationType: DelegationTargetType.Baker; + bakerId: BakerId; +} + +export type DelegationTarget = DelegationTargetLPool | DelegationTargetBaker; + +export interface BakerPoolInfo { + openStatus: OpenStatus; + metadataUrl: string; + commissionRates: CommissionRates; +} export interface AccountBakerDetails { restakeEarnings: boolean; - bakerId: bigint; + bakerId: BakerId; bakerAggregationVerifyKey: string; bakerElectionVerifyKey: string; bakerSignatureVerifyKey: string; stakedAmount: bigint; - pendingChange?: BakerPendingChange; + pendingChange?: StakePendingChange; + + /** Protocol version 4 and later. */ + bakerPoolInfo?: BakerPoolInfo; +} + +export interface AccountDelegationDetails { + restakeEarnings: boolean; + stakedAmount: bigint; + delegationTarget: DelegationTarget; + pendingChange?: StakePendingChange; } export interface AccountInfo { @@ -698,7 +743,10 @@ export interface AccountInfo { Versioned >; + // Only one of either accountBaker or accountDelegation can be active at any time. accountBaker?: AccountBakerDetails; + /** Protocol version 4 and later. */ + accountDelegation?: AccountDelegationDetails; } export interface Description { From 5e8a110f1b25c3cfa92be0eccf071df4c2d8d3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Thu, 24 Feb 2022 11:31:34 +0100 Subject: [PATCH 04/29] delegation grpc api --- deps/concordium-grpc-api | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/concordium-grpc-api b/deps/concordium-grpc-api index 3f0b42a69..ac2d3acc5 160000 --- a/deps/concordium-grpc-api +++ b/deps/concordium-grpc-api @@ -1 +1 @@ -Subproject commit 3f0b42a696cea73e49ab3fec74ca0dc4189b93f9 +Subproject commit ac2d3acc5ff5da5034a1b1ceca67d9bfab766f2d From 69ece2cdf2a8fe5ab79578f15cd1f0d5cf20827e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Thu, 24 Feb 2022 12:29:59 +0100 Subject: [PATCH 05/29] getBakerList query added --- src/client.ts | 24 ++++++++++++++++++++++++ src/types.ts | 1 + src/util.ts | 4 ++++ 3 files changed, 29 insertions(+) diff --git a/src/client.ts b/src/client.ts index c191810b4..e2c92a173 100644 --- a/src/client.ts +++ b/src/client.ts @@ -48,9 +48,11 @@ import { Versioned, InstanceInfo, InstanceInfoSerialized, + BakerId, } from './types'; import { buildJsonResponseReviver, + intListToStringList, intToStringTransformer, isValidHash, unwrapBoolResponse, @@ -59,6 +61,7 @@ import { import { GtuAmount } from './types/gtuAmount'; import { ModuleReference } from './types/moduleReference'; import { Buffer as BufferFormater } from 'buffer/'; + /** * A concordium-node specific gRPC client wrapper. * @@ -600,6 +603,27 @@ export default class ConcordiumNodeClient { } } + /** + * Retrieve list of bakers on the network. + * @param blockHash the block hash to get the smart contact instances at + * @returns A JSON list of baker IDs + */ + async getBakerList(blockHash: string): Promise { + if (!isValidHash(blockHash)) { + throw new Error('The input was not a valid hash: ' + blockHash); + } + const response = await this.sendRequest( + this.client.getBakerList, + blockHash + ); + + return unwrapJsonResponse( + response, + (_, v) => BigInt(v as string), + intListToStringList + ); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types sendRequest(command: any, input: T): Promise { const deadline = new Date(Date.now() + this.timeout); diff --git a/src/types.ts b/src/types.ts index 094033e73..b4ac95d32 100644 --- a/src/types.ts +++ b/src/types.ts @@ -677,6 +677,7 @@ export enum OpenStatus { ClosedForAll = 2, } +export type BakerId = bigint; export interface CommissionRates { transactionCommission: RewardFraction; bakingCommission: RewardFraction; diff --git a/src/util.ts b/src/util.ts index f132d8748..0a24c1358 100644 --- a/src/util.ts +++ b/src/util.ts @@ -23,6 +23,10 @@ function intToString(jsonStruct: string, keys: string[]): string { return result; } +export function intListToStringList(jsonStruct: string): string { + return jsonStruct.replace(/([0-9]+)/g, '"$1"'); +} + /** * A transformer that converts all the values provided as keys to * string values. From 7879e4be7f36424e0d78194283a3aeb7e4dc7607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Thu, 24 Feb 2022 14:35:52 +0100 Subject: [PATCH 06/29] type version helpers --- src/blockSummaryHelpers.ts | 34 +++++++++++++++ src/client.ts | 5 ++- src/types.ts | 89 ++++++++++++++++++++++++++++---------- test/client.test.ts | 31 +++++++------ 4 files changed, 120 insertions(+), 39 deletions(-) create mode 100644 src/blockSummaryHelpers.ts diff --git a/src/blockSummaryHelpers.ts b/src/blockSummaryHelpers.ts new file mode 100644 index 000000000..451c173ba --- /dev/null +++ b/src/blockSummaryHelpers.ts @@ -0,0 +1,34 @@ +import { + Authorizations, + AuthorizationsV1, + BlockSummary, + BlockSummaryV1, + ChainParameters, + ChainParametersV1, + Keys, + KeysV1, + UpdateQueues, + UpdateQueuesV1, + Updates, + UpdatesV1, +} from './types'; + +export const isAuthorizationsV1 = (a: Authorizations): a is AuthorizationsV1 => + (a as AuthorizationsV1).timeParameters !== undefined; + +export const isChainParametersV1 = ( + cp: ChainParameters +): cp is ChainParametersV1 => + (cp as ChainParametersV1).mintPerPayday !== undefined; + +export const isKeysV1 = (k: Keys): k is KeysV1 => + isAuthorizationsV1(k.level2Keys); + +export const isUpdateQueuesV1 = (uq: UpdateQueues): uq is UpdateQueuesV1 => + (uq as UpdateQueuesV1).timeParameters !== undefined; + +export const isUpdatesV1 = (u: Updates): u is UpdatesV1 => + isUpdateQueuesV1(u.updateQueues); + +export const isBlockSummaryV1 = (bs: BlockSummary): bs is BlockSummaryV1 => + (bs as BlockSummaryV1).protocolVersion !== undefined; diff --git a/src/client.ts b/src/client.ts index e2c92a173..fd80712a4 100644 --- a/src/client.ts +++ b/src/client.ts @@ -29,7 +29,6 @@ import { RemovalPendingChange, BlockInfo, BlockSummary, - ChainParameters, ConsensusStatus, ContractAddress, CredentialDeploymentTransaction, @@ -49,6 +48,8 @@ import { InstanceInfo, InstanceInfoSerialized, BakerId, + ChainParametersV0, + ChainParametersV1, } from './types'; import { buildJsonResponseReviver, @@ -320,7 +321,7 @@ export default class ConcordiumNodeClient { | keyof PartyInfo | keyof FinalizationData | keyof TransactionSummary - | keyof ChainParameters + | keyof (ChainParametersV0 & ChainParametersV1) | keyof ExchangeRate | keyof UpdateQueue | keyof KeysWithThreshold diff --git a/src/types.ts b/src/types.ts index b4ac95d32..9fe55370d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -287,11 +287,11 @@ interface MintDistributionCommon { finalizationReward: RewardFraction; } -interface MintDistributionV0 { +export interface MintDistributionV0 extends MintDistributionCommon { mintPerSlot: number; } -type MintDistributionV1 = MintDistributionCommon; +export type MintDistributionV1 = MintDistributionCommon; export type MintDistribution = MintDistributionV0 | MintDistributionV1; @@ -307,30 +307,36 @@ interface RewardParametersCommon { gASRewards: GasRewards; } -interface RewardParametersV0 extends RewardParametersCommon { +/** + * Used from protocol version 1-3 + */ +export interface RewardParametersV0 extends RewardParametersCommon { mintDistribution: MintDistributionV0; } -interface RewardParametersV1 extends RewardParametersCommon { +/** + * Used from protocol version 4 + */ +export interface RewardParametersV1 extends RewardParametersCommon { mintDistribution: MintDistributionV1; } export type RewardParameters = RewardParametersV0 | RewardParametersV1; -interface CooldownParametersV0 { +export interface CooldownParametersV0 { bakerCooldownEpochs: Epoch; } -interface CooldownParametersV1 { +export interface CooldownParametersV1 { poolOwnerCooldown: RewardPeriod; delegatorCooldown: RewardPeriod; } -interface PoolParametersV0 { +export interface PoolParametersV0 { minimumThresholdForBaking: bigint; } -interface PoolParametersV1 { +export interface PoolParametersV1 { finalizationCommissionLPool: RewardFraction; bakingCommissionLPool: RewardFraction; transactionCommissionLPool: RewardFraction; @@ -342,7 +348,7 @@ interface PoolParametersV1 { leverageBound: Ratio; } -interface TimeParametersV1 { +export interface TimeParametersV1 { rewardPeriodLength: Epoch; mintPerPayday: number; } @@ -355,14 +361,20 @@ interface ChainParametersCommon { foundationAccountIndex: bigint; } -interface ChainParametersV0 +/** + * Used from protocol version 1-3 + */ +export interface ChainParametersV0 extends ChainParametersCommon, CooldownParametersV0, PoolParametersV0 { rewardParameters: RewardParametersV0; } -interface ChainParametersV1 +/** + * Used from protocol version 4 + */ +export interface ChainParametersV1 extends ChainParametersCommon, CooldownParametersV1, PoolParametersV1, @@ -393,9 +405,15 @@ interface AuthorizationsCommon { keys: VerifyKey[]; } -type AuthorizationsV0 = AuthorizationsCommon; +/** + * Used from protocol version 1-3 + */ +export type AuthorizationsV0 = AuthorizationsCommon; -interface AuthorizationsV1 extends AuthorizationsCommon { +/** + * Used from protocol version 4 + */ +export interface AuthorizationsV1 extends AuthorizationsCommon { cooldownParameters: Authorization; timeParameters: Authorization; } @@ -412,11 +430,17 @@ interface KeysCommon { level1Keys: KeysWithThreshold; } -interface KeysV0 extends KeysCommon { +/** + * Used from protocol version 1-3 + */ +export interface KeysV0 extends KeysCommon { level2Keys: AuthorizationsV0; } -interface KeysV1 extends KeysCommon { +/** + * Used from protocol version 4 + */ +export interface KeysV1 extends KeysCommon { level2Keys: AuthorizationsV1; } @@ -426,7 +450,7 @@ export interface UpdateQueueQueue { effectiveTime: Date; // TODO Update the type of update to a generic update transaction when // update types have been added. - // Information about the actual update. + /** Information about the actual update. */ update: unknown; } @@ -452,9 +476,15 @@ interface UpdateQueuesCommon { level2Keys: UpdateQueue; } -type UpdateQueuesV0 = UpdateQueuesCommon; +/** + * Used from protocol version 1-3 + */ +export type UpdateQueuesV0 = UpdateQueuesCommon; -interface UpdateQueuesV1 extends UpdateQueuesCommon { +/** + * Used from protocol version 4 + */ +export interface UpdateQueuesV1 extends UpdateQueuesCommon { cooldownParameters: UpdateQueue; timeParameters: UpdateQueue; } @@ -472,13 +502,19 @@ interface UpdatesCommon { protocolUpdate: ProtocolUpdate | undefined; } -interface UpdatesV0 extends UpdatesCommon { +/** + * Used from protocol version 1-3 + */ +export interface UpdatesV0 extends UpdatesCommon { chainParameters: ChainParametersV0; updateQueues: UpdateQueuesV0; keys: KeysV0; } -interface UpdatesV1 extends UpdatesCommon { +/** + * Used from protocol version 4 + */ +export interface UpdatesV1 extends UpdatesCommon { chainParameters: ChainParametersV1; updateQueues: UpdateQueuesV1; keys: KeysV1; @@ -491,13 +527,19 @@ interface BlockSummaryCommon { transactionSummaries: TransactionSummary[]; } -interface BlockSummaryV0 extends BlockSummaryCommon { +/** + * Used from protocol version 1-3 + */ +export interface BlockSummaryV0 extends BlockSummaryCommon { updates: UpdatesV0; } -interface BlockSummaryV1 extends BlockSummaryCommon { +/** + * Used from protocol version 4 + */ +export interface BlockSummaryV1 extends BlockSummaryCommon { updates: UpdatesV1; - protocolVersion: bigint; // Protocol version 4 and onwards + protocolVersion: bigint; } export type BlockSummary = BlockSummaryV0 | BlockSummaryV1; @@ -684,7 +726,6 @@ export interface CommissionRates { finalizationCommission: RewardFraction; } -export type BakerId = bigint; export enum DelegationTargetType { LPool = 'L-Pool', Baker = 'Baker', diff --git a/test/client.test.ts b/test/client.test.ts index ab3ae8c83..4b2d4fbc0 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -1,5 +1,5 @@ import { - BakerReduceStakePendingChange, + ReduceStakePendingChange, ConsensusStatus, instanceOfTransferWithMemoTransactionSummary, NormalAccountCredential, @@ -12,6 +12,7 @@ import { bulletProofGenerators } from './resources/bulletproofgenerators'; import { ipVerifyKey1, ipVerifyKey2 } from './resources/ipVerifyKeys'; import { PeerElement } from '../grpc/concordium_p2p_rpc_pb'; import { CredentialRegistrationId } from '../src/types/CredentialRegistrationId'; +import { isBlockSummaryV1 } from '../src/blockSummaryHelpers'; const client = getNodeClient(); @@ -111,6 +112,21 @@ test('block summary for valid block hash retrieves block summary', async () => { if (!blockSummary) { throw new Error('The block could not be found by the test'); } + + // Pre delegation protocol version (protocol version 4) + if (!isBlockSummaryV1(blockSummary)) { + expect( + blockSummary.updates.chainParameters.rewardParameters + .mintDistribution.mintPerSlot + ).toBe(7.555665e-10); + expect( + blockSummary.updates.chainParameters.minimumThresholdForBaking + ).toBe(15000000000n); + expect(blockSummary.updates.chainParameters.bakerCooldownEpochs).toBe( + 166n + ); + } + return Promise.all([ expect(blockSummary.finalizationData.finalizationIndex).toBe(15436n), expect(blockSummary.finalizationData.finalizationDelay).toBe(0n), @@ -150,10 +166,6 @@ test('block summary for valid block hash retrieves block summary', async () => { blockSummary.updates.chainParameters.rewardParameters .mintDistribution.finalizationReward ).toBe(0.3), - expect( - blockSummary.updates.chainParameters.rewardParameters - .mintDistribution.mintPerSlot - ).toBe(7.555665e-10), expect( blockSummary.updates.chainParameters.rewardParameters.gASRewards .chainUpdate @@ -170,10 +182,6 @@ test('block summary for valid block hash retrieves block summary', async () => { blockSummary.updates.chainParameters.rewardParameters.gASRewards .finalizationProof ).toBe(0.005), - - expect( - blockSummary.updates.chainParameters.minimumThresholdForBaking - ).toBe(15000000000n), expect( blockSummary.updates.chainParameters.microGTUPerEuro.numerator ).toBe(500000n), @@ -190,9 +198,6 @@ test('block summary for valid block hash retrieves block summary', async () => { expect( blockSummary.updates.chainParameters.foundationAccountIndex ).toBe(10n), - expect(blockSummary.updates.chainParameters.bakerCooldownEpochs).toBe( - 166n - ), expect(blockSummary.updates.chainParameters.accountCreationLimit).toBe( 10 ), @@ -507,7 +512,7 @@ test('account info with baker details, and with a pending stake reduction', asyn } expect(pendingChange.change).toEqual('ReduceStake'); - expect((pendingChange as BakerReduceStakePendingChange).newStake).toEqual( + expect((pendingChange as ReduceStakePendingChange).newStake).toEqual( 14000000000n ); expect(pendingChange.epoch).toEqual(838n); From 3420083613fbe7f5a36920c347371fa51e2c8a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Thu, 24 Feb 2022 14:50:05 +0100 Subject: [PATCH 07/29] Major version bump due to breaking changes to types --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ec668d86a..2551bea69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@concordium/node-sdk", - "version": "0.6.0", + "version": "1.0.0", "description": "Helpers for interacting with the Concordium node", "repository": { "type": "git", From c633f5ec63b9af7d67acb512dbca0ef20b27ef06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Fri, 25 Feb 2022 10:30:41 +0100 Subject: [PATCH 08/29] small updates --- deps/concordium-grpc-api | 2 +- src/client.ts | 9 +++++---- src/types.ts | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/deps/concordium-grpc-api b/deps/concordium-grpc-api index ac2d3acc5..65a388838 160000 --- a/deps/concordium-grpc-api +++ b/deps/concordium-grpc-api @@ -1 +1 @@ -Subproject commit ac2d3acc5ff5da5034a1b1ceca67d9bfab766f2d +Subproject commit 65a388838570b0f2ce8a368426e33f9ae083c4f9 diff --git a/src/client.ts b/src/client.ts index fd80712a4..06056cf22 100644 --- a/src/client.ts +++ b/src/client.ts @@ -613,10 +613,11 @@ export default class ConcordiumNodeClient { if (!isValidHash(blockHash)) { throw new Error('The input was not a valid hash: ' + blockHash); } - const response = await this.sendRequest( - this.client.getBakerList, - blockHash - ); + + const bh = new BlockHash(); + bh.setBlockHash(blockHash); + + const response = await this.sendRequest(this.client.getBakerList, bh); return unwrapJsonResponse( response, diff --git a/src/types.ts b/src/types.ts index 9fe55370d..85d1911aa 100644 --- a/src/types.ts +++ b/src/types.ts @@ -343,7 +343,7 @@ export interface PoolParametersV1 { finalizationCommissionRange: InclusiveRange; bakingCommissionRange: InclusiveRange; transactionCommissionRange: InclusiveRange; - minimumEquityCapital: bigint; + minimumEquityCapital: Amount; capitalBound: RewardFraction; leverageBound: Ratio; } From a9198f386cffed8d8b155acb63829b13d4924392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Fri, 25 Feb 2022 13:12:22 +0100 Subject: [PATCH 09/29] Added getPoolStatus function to client --- src/client.ts | 62 +++++++++++++++++++++++++++ src/types.ts | 116 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 172 insertions(+), 6 deletions(-) diff --git a/src/client.ts b/src/client.ts index 06056cf22..65649748b 100644 --- a/src/client.ts +++ b/src/client.ts @@ -8,6 +8,7 @@ import { BlockHeight, Empty, GetAddressInfoRequest, + GetPoolStatusRequest, PeerListResponse, PeersRequest, SendTransactionRequest, @@ -50,6 +51,12 @@ import { BakerId, ChainParametersV0, ChainParametersV1, + PoolStatus, + BakerPoolStatusDetails, + CurrentPaydayBakerPoolStatus, + LPoolStatusDetails, + KeysMatching, + BakerPoolPendingChangeReduceBakerCapitalDetails, } from './types'; import { buildJsonResponseReviver, @@ -626,6 +633,61 @@ export default class ConcordiumNodeClient { ); } + /** + * Gets the status of either a baker, if a baker ID is supplied, or the L-Pool if left undefined. + * @param blockHash the block hash to get the smart contact instances at + * @param [bakerId] the ID of the baker to get the status for. If left undefined, the status of the L-Pool is returned. + * @returns The status of the corresponding pool. + */ + async getPoolStatus( + blockHash: string, + bakerId?: BakerId + ): Promise { + if (!isValidHash(blockHash)) { + throw new Error('The input was not a valid hash: ' + blockHash); + } + + const req = new GetPoolStatusRequest(); + req.setBlockHash(blockHash); + req.setLPool(bakerId === undefined); + + if (bakerId !== undefined) { + req.setBakerId(bakerId.toString()); + } + + type DateKey = KeysMatching< + BakerPoolPendingChangeReduceBakerCapitalDetails, + Date + >; + type BigIntKey = KeysMatching< + BakerPoolStatusDetails & + LPoolStatusDetails & + CurrentPaydayBakerPoolStatus, + bigint + >; + + const dates: DateKey[] = ['effectiveTime']; + const bigInts: BigIntKey[] = [ + 'bakerId', + 'bakerEquityCapital', + 'delegatedCapital', + 'delegatedCapitalCap', + 'currentPaydayTransactionFeesEarned', + 'currentPaydayDelegatedCapital', + 'blocksBaked', + 'transactionFeesEarned', + 'effectiveStake', + ]; + + const response = await this.sendRequest(this.client.getPoolStatus, req); + + return unwrapJsonResponse( + response, + buildJsonResponseReviver(dates, bigInts), + intToStringTransformer(bigInts) + ); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types sendRequest(command: any, input: T): Promise { const deadline = new Date(Date.now() + this.timeout); diff --git a/src/types.ts b/src/types.ts index 85d1911aa..7a9656bbc 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,6 +5,13 @@ import { TransactionExpiry } from './types/transactionExpiry'; import { Buffer } from 'buffer/'; import { ModuleReference } from './types/moduleReference'; +/** + * Returns a union of all keys of type T with values matching type V. + */ +export type KeysMatching = { + [K in keyof T]-?: T[K] extends V ? K : never; +}[keyof T]; + /** * A reward fraction with a resolution of 1/100000, i.e. the * denominator is implicitly 100000, and the interface therefore @@ -719,13 +726,116 @@ export enum OpenStatus { ClosedForAll = 2, } +export type Hex = string; +export type Amount = bigint; export type BakerId = bigint; + +export interface BakerPoolInfo { + openStatus: OpenStatus; + metadataUrl: string; + commissionRates: CommissionRates; +} + export interface CommissionRates { transactionCommission: RewardFraction; bakingCommission: RewardFraction; finalizationCommission: RewardFraction; } +export interface CurrentPaydayBakerPoolStatus { + blocksBaked: bigint; + finalizationLive: boolean; + transactionFeesEarned: Amount; + effectiveStake: Amount; + lotteryPower: number; + bakerEquityCapital: Amount; + delegatedCapital: Amount; +} + +export enum BakerPoolPendingChangeType { + ReduceBakerCapital = 'ReduceBakerCapital', + RemovePool = 'RemovePool', + NoChange = 'NoChange', +} + +interface BakerPoolPendingChangeWrapper< + T extends keyof typeof BakerPoolPendingChangeType, + S +> { + pendingChangeType: T; + pendingChangeDetails: S; +} + +export interface BakerPoolPendingChangeReduceBakerCapitalDetails { + bakerEquityCapital: Amount; + effectiveTime: Date; +} + +export type BakerPoolPendingChangeReduceBakerCapital = + BakerPoolPendingChangeWrapper< + BakerPoolPendingChangeType.ReduceBakerCapital, + BakerPoolPendingChangeReduceBakerCapitalDetails + >; + +export interface BakerPoolPendingChangeRemovePoolDetails { + effectiveTime: Date; +} + +export type BakerPoolPendingChangeRemovePool = BakerPoolPendingChangeWrapper< + BakerPoolPendingChangeType.RemovePool, + BakerPoolPendingChangeRemovePoolDetails +>; + +export type BakerPoolPendingChangeNoChange = BakerPoolPendingChangeWrapper< + BakerPoolPendingChangeType.NoChange, + undefined +>; + +export type BakerPoolPendingChange = + | BakerPoolPendingChangeReduceBakerCapital + | BakerPoolPendingChangeRemovePool + | BakerPoolPendingChangeNoChange; + +export enum PoolStatusType { + BakerPool = 'BakerPool', + LPool = 'LPool', +} + +interface PoolStatusWrapper { + poolType: T; + poolStatus: S; +} + +export interface BakerPoolStatusDetails { + bakerId: BakerId; + bakerAddress: Hex; + bakerEquityCapital: Amount; + delegatedCapital: Amount; + delegatedCapitalCap: Amount; + poolInfo: BakerPoolInfo; + bakerStakePendingChange: BakerPoolPendingChange; + currentPaydayStatus?: CurrentPaydayBakerPoolStatus; +} + +export type BakerPoolStatus = PoolStatusWrapper< + PoolStatusType.BakerPool, + BakerPoolStatusDetails +>; + +export interface LPoolStatusDetails { + delegatedCapital: Amount; + commissionRates: CommissionRates; + currentPaydayTransactionFeesEarned: Amount; + currentPaydayDelegatedCapital: Amount; +} + +export type LPoolStatus = PoolStatusWrapper< + PoolStatusType.LPool, + LPoolStatusDetails +>; + +export type PoolStatus = BakerPoolStatus | LPoolStatus; + export enum DelegationTargetType { LPool = 'L-Pool', Baker = 'Baker', @@ -742,12 +852,6 @@ export interface DelegationTargetBaker { export type DelegationTarget = DelegationTargetLPool | DelegationTargetBaker; -export interface BakerPoolInfo { - openStatus: OpenStatus; - metadataUrl: string; - commissionRates: CommissionRates; -} - export interface AccountBakerDetails { restakeEarnings: boolean; bakerId: BakerId; From d0576d15fc0577a1b75f1f9dc5738be3883e63e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Fri, 25 Feb 2022 13:37:03 +0100 Subject: [PATCH 10/29] documentation and readme update --- README.md | 20 ++++++++++++++++++++ src/client.ts | 24 +++++++++++++++++++++--- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3c01d50ff..29f9768a4 100644 --- a/README.md +++ b/README.md @@ -542,6 +542,26 @@ const peersList = peerListResponse.getPeersList(); ... ``` +## getBakerList +Retrieves the list of ID's for registered bakers on the network at a specific block. +```js +const blockHash = "7f7409679e53875567e2ae812c9fcefe90ced8961d08554756f42bf268a42749"; +const bakerIds = await client.getBakerList(blockHash); +... +``` + +## getPoolStatus +Retrieves the status of a pool (either L-Pool or specific baker) at a specific block. +If a baker ID is specified, the status of that baker is returned. To get the status of the L-Pool, a baker ID should be left undefined. +```js +const blockHash = "7f7409679e53875567e2ae812c9fcefe90ced8961d08554756f42bf268a42749"; +const bakerId = BigInt(1); + +const bakerStatus = await client.getPoolStatus(blockHash, bakerId); +const lPoolStatus = await client.getPoolStatus(blockHash); +... +``` + ## Check block for transfers with memo The following example demonstrates how to check and parse a block for transfers with a memo. diff --git a/src/client.ts b/src/client.ts index 65649748b..af642cebe 100644 --- a/src/client.ts +++ b/src/client.ts @@ -57,6 +57,8 @@ import { LPoolStatusDetails, KeysMatching, BakerPoolPendingChangeReduceBakerCapitalDetails, + LPoolStatus, + BakerPoolStatus, } from './types'; import { buildJsonResponseReviver, @@ -634,9 +636,25 @@ export default class ConcordiumNodeClient { } /** - * Gets the status of either a baker, if a baker ID is supplied, or the L-Pool if left undefined. - * @param blockHash the block hash to get the smart contact instances at - * @param [bakerId] the ID of the baker to get the status for. If left undefined, the status of the L-Pool is returned. + * Gets the status the L-pool. + * @param blockHash the block hash the status at + * @returns The status of the L-pool. + */ + async getPoolStatus(blockHash: string): Promise; + /** + * Gets the status a baker. + * @param blockHash the block hash the status at + * @param bakerId the ID of the baker to get the status for. + * @returns The status of the corresponding baker pool. + */ + async getPoolStatus( + blockHash: string, + bakerId: BakerId + ): Promise; + /** + * Gets the status of either a baker, if a baker ID is supplied, or the L-pool if left undefined. + * @param blockHash the block hash the status at + * @param [bakerId] the ID of the baker to get the status for. If left undefined, the status of the L-pool is returned. * @returns The status of the corresponding pool. */ async getPoolStatus( From d70cab98eab5dd7448e27b178f50e5999ff1bf6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Fri, 25 Feb 2022 14:04:25 +0100 Subject: [PATCH 11/29] changelog + export version helpers --- CHANGELOG.md | 13 +++++++++++++ src/index.ts | 1 + 2 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fe26e04a..6bf111169 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## Unreleased + +### Added + +- Support for getting baker list from node. +- Support for getting status of Baker-/L-pool (required node to have protocol version 4 or later) +- Helper functions for determining the version of `BlockSummary` and nested types. + +### Changed + +- Updated `BlockSummary` type to include new version, effective from protocol version 4. +- Updated `AccountInfo` type to include new fields related to delegation introduced with protocol version 4. + ## 0.6.0 2022-02-02 ### Added diff --git a/src/index.ts b/src/index.ts index 3a4ad3aea..d5a2fe891 100644 --- a/src/index.ts +++ b/src/index.ts @@ -34,3 +34,4 @@ export { } from './credentialDeploymentTransactions'; export { isAlias, getAlias } from './alias'; export { deserializeContractState } from './deserialization'; +export * from './blockSummaryHelpers'; From 16ad7ffec7367152443227f7cb1539d92dee934e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Fri, 25 Feb 2022 14:51:06 +0100 Subject: [PATCH 12/29] minor corrections --- src/types.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/types.ts b/src/types.ts index 7a9656bbc..1ad9a63c8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -294,8 +294,13 @@ interface MintDistributionCommon { finalizationReward: RewardFraction; } +export interface MintRate { + mantissa: number; + exponent: number; +} + export interface MintDistributionV0 extends MintDistributionCommon { - mintPerSlot: number; + mintPerSlot: MintRate; } export type MintDistributionV1 = MintDistributionCommon; @@ -340,7 +345,7 @@ export interface CooldownParametersV1 { } export interface PoolParametersV0 { - minimumThresholdForBaking: bigint; + minimumThresholdForBaking: Amount; } export interface PoolParametersV1 { @@ -712,7 +717,7 @@ export interface ReduceStakePendingChange { } export interface RemovalPendingChange { - change: 'RemoveBaker'; + change: 'RemoveStake'; epoch: bigint; } From 3b1c1edc93f78bbc4ced524f6a7efdb4d700dac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Mon, 28 Feb 2022 11:45:24 +0100 Subject: [PATCH 13/29] Unit tests... so far --- src/types.ts | 3 +-- src/util.ts | 2 +- test/util.test.ts | 27 +++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 test/util.test.ts diff --git a/src/types.ts b/src/types.ts index 1ad9a63c8..f5b60bc43 100644 --- a/src/types.ts +++ b/src/types.ts @@ -731,7 +731,6 @@ export enum OpenStatus { ClosedForAll = 2, } -export type Hex = string; export type Amount = bigint; export type BakerId = bigint; @@ -813,7 +812,7 @@ interface PoolStatusWrapper { export interface BakerPoolStatusDetails { bakerId: BakerId; - bakerAddress: Hex; + bakerAddress: string; bakerEquityCapital: Amount; delegatedCapital: Amount; delegatedCapitalCap: Amount; diff --git a/src/util.ts b/src/util.ts index 0a24c1358..784c4b233 100644 --- a/src/util.ts +++ b/src/util.ts @@ -24,7 +24,7 @@ function intToString(jsonStruct: string, keys: string[]): string { } export function intListToStringList(jsonStruct: string): string { - return jsonStruct.replace(/([0-9]+)/g, '"$1"'); + return jsonStruct.replace(/(\-?[0-9]+)/g, '"$1"'); } /** diff --git a/test/util.test.ts b/test/util.test.ts new file mode 100644 index 000000000..94998daad --- /dev/null +++ b/test/util.test.ts @@ -0,0 +1,27 @@ +import { intListToStringList } from '../src/util'; + +test('Correctly converts stringified list of numbers to stringified list of corresponding strings', () => { + // List of ints + let numbers = '[1, 22, 332]'; + let strings = intListToStringList(numbers); + + expect(strings).toEqual('["1", "22", "332"]'); + + // Empty list + numbers = '[]'; + strings = intListToStringList(numbers); + + expect(strings).toEqual('[]'); + + // Single int list + numbers = '[1]'; + strings = intListToStringList(numbers); + + expect(strings).toEqual('["1"]'); + + // negative int list + numbers = '[-1, 21, -32]'; + strings = intListToStringList(numbers); + + expect(strings).toEqual('["-1", "21", "-32"]'); +}); From 848ee535a2fc19cd851e0d1627da4010e80f7b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Mon, 28 Feb 2022 14:35:21 +0100 Subject: [PATCH 14/29] Added strict differentiation between baker and delegator account types --- package.json | 2 +- src/accountHelpers.ts | 9 +++++++++ src/types.ts | 21 ++++++++++++++++----- 3 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 src/accountHelpers.ts diff --git a/package.json b/package.json index 2551bea69..53f5dadd1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@concordium/node-sdk", - "version": "1.0.0", + "version": "0.7.0", "description": "Helpers for interacting with the Concordium node", "repository": { "type": "git", diff --git a/src/accountHelpers.ts b/src/accountHelpers.ts new file mode 100644 index 000000000..cb4cf030d --- /dev/null +++ b/src/accountHelpers.ts @@ -0,0 +1,9 @@ +import { AccountInfo, AccountInfoBaker, AccountInfoDelegator } from './types'; + +export const isDelegatorAccount = ( + ai: AccountInfo +): ai is AccountInfoDelegator => + (ai as AccountInfoDelegator).accountDelegation !== undefined; + +export const isBakerAccount = (ai: AccountInfo): ai is AccountInfoBaker => + (ai as AccountInfoBaker).accountBaker !== undefined; diff --git a/src/types.ts b/src/types.ts index f5b60bc43..631d7d633 100644 --- a/src/types.ts +++ b/src/types.ts @@ -876,7 +876,7 @@ export interface AccountDelegationDetails { pendingChange?: StakePendingChange; } -export interface AccountInfo { +interface AccountInfoCommon { accountNonce: bigint; accountAmount: bigint; accountIndex: bigint; @@ -892,13 +892,24 @@ export interface AccountInfo { number, Versioned >; +} - // Only one of either accountBaker or accountDelegation can be active at any time. - accountBaker?: AccountBakerDetails; - /** Protocol version 4 and later. */ - accountDelegation?: AccountDelegationDetails; +export type AccountInfoSimple = AccountInfoCommon; + +export interface AccountInfoBaker extends AccountInfoCommon { + accountBaker: AccountBakerDetails; } +/** Protocol version 4 and later. */ +export interface AccountInfoDelegator extends AccountInfoCommon { + accountDelegation: AccountDelegationDetails; +} + +export type AccountInfo = + | AccountInfoSimple + | AccountInfoBaker + | AccountInfoDelegator; + export interface Description { name: string; url: string; From 5d230f15df7e96c7e5e642a950d4e41c2aad1d10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Mon, 28 Feb 2022 15:23:21 +0100 Subject: [PATCH 15/29] More explicit division of account info versions --- src/accountHelpers.ts | 10 +++++++++- src/types.ts | 22 +++++++++++++++++----- test/client.test.ts | 13 ++++++++++--- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/accountHelpers.ts b/src/accountHelpers.ts index cb4cf030d..076eb6662 100644 --- a/src/accountHelpers.ts +++ b/src/accountHelpers.ts @@ -1,4 +1,9 @@ -import { AccountInfo, AccountInfoBaker, AccountInfoDelegator } from './types'; +import { + AccountInfo, + AccountInfoBaker, + AccountInfoBakerV1, + AccountInfoDelegator, +} from './types'; export const isDelegatorAccount = ( ai: AccountInfo @@ -7,3 +12,6 @@ export const isDelegatorAccount = ( export const isBakerAccount = (ai: AccountInfo): ai is AccountInfoBaker => (ai as AccountInfoBaker).accountBaker !== undefined; + +export const isBakerAccountV1 = (ai: AccountInfo): ai is AccountInfoBakerV1 => + (ai as AccountInfoBakerV1).accountBaker?.bakerPoolInfo !== undefined; diff --git a/src/types.ts b/src/types.ts index 631d7d633..c5501cd52 100644 --- a/src/types.ts +++ b/src/types.ts @@ -856,7 +856,7 @@ export interface DelegationTargetBaker { export type DelegationTarget = DelegationTargetLPool | DelegationTargetBaker; -export interface AccountBakerDetails { +interface AccountBakerDetailsCommon { restakeEarnings: boolean; bakerId: BakerId; bakerAggregationVerifyKey: string; @@ -864,11 +864,16 @@ export interface AccountBakerDetails { bakerSignatureVerifyKey: string; stakedAmount: bigint; pendingChange?: StakePendingChange; +} + +export type AccountBakerDetailsV0 = AccountBakerDetailsCommon; - /** Protocol version 4 and later. */ - bakerPoolInfo?: BakerPoolInfo; +export interface AccountBakerDetailsV1 extends AccountBakerDetailsCommon { + bakerPoolInfo: BakerPoolInfo; } +export type AccountBakerDetails = AccountBakerDetailsV0 | AccountBakerDetailsV1; + export interface AccountDelegationDetails { restakeEarnings: boolean; stakedAmount: bigint; @@ -896,10 +901,17 @@ interface AccountInfoCommon { export type AccountInfoSimple = AccountInfoCommon; -export interface AccountInfoBaker extends AccountInfoCommon { - accountBaker: AccountBakerDetails; +export interface AccountInfoBakerV0 extends AccountInfoCommon { + accountBaker: AccountBakerDetailsV0; } +/** Protocol version 4 and later. */ +export interface AccountInfoBakerV1 extends AccountInfoCommon { + accountBaker: AccountBakerDetailsV1; +} + +export type AccountInfoBaker = AccountInfoBakerV0 | AccountInfoBakerV1; + /** Protocol version 4 and later. */ export interface AccountInfoDelegator extends AccountInfoCommon { accountDelegation: AccountDelegationDetails; diff --git a/test/client.test.ts b/test/client.test.ts index 4b2d4fbc0..abc49b7b1 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -13,6 +13,7 @@ import { ipVerifyKey1, ipVerifyKey2 } from './resources/ipVerifyKeys'; import { PeerElement } from '../grpc/concordium_p2p_rpc_pb'; import { CredentialRegistrationId } from '../src/types/CredentialRegistrationId'; import { isBlockSummaryV1 } from '../src/blockSummaryHelpers'; +import { isBakerAccount } from '../src/accountHelpers'; const client = getNodeClient(); @@ -409,7 +410,9 @@ test('account info with baker details, and with no pending change', async () => throw new Error('Test failed to find account info'); } - const bakerDetails = accountInfo.accountBaker; + const bakerDetails = isBakerAccount(accountInfo) + ? accountInfo.accountBaker + : undefined; if (!bakerDetails) { throw new Error('Account info doesnt contain baker details'); @@ -444,7 +447,9 @@ test('account info with baker details, and with a pending baker removal', async throw new Error('Test failed to find account info'); } - const bakerDetails = accountInfo.accountBaker; + const bakerDetails = isBakerAccount(accountInfo) + ? accountInfo.accountBaker + : undefined; if (!bakerDetails) { throw new Error('Account info doesnt contain baker details'); @@ -486,7 +491,9 @@ test('account info with baker details, and with a pending stake reduction', asyn throw new Error('Test failed to find account info'); } - const bakerDetails = accountInfo.accountBaker; + const bakerDetails = isBakerAccount(accountInfo) + ? accountInfo.accountBaker + : undefined; if (!bakerDetails) { throw new Error('Account info doesnt contain baker details'); From f18e45f6a5005831b79fcbd79f1850f7713f5352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Tue, 1 Mar 2022 08:16:18 +0100 Subject: [PATCH 16/29] Changed v1 cooldowns to duration in seconds instead of reward periods, to align with recent change in node repo --- src/types.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/types.ts b/src/types.ts index c5501cd52..fe20dc45b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -277,10 +277,9 @@ export interface InclusiveRange { } /** - * Used as index of a reward period, or as a number of reward periods. - * A reward period spans a number of epochs, as defined in chain parameters as "rewardPeriodLength" + * A duration in seconds. */ -export type RewardPeriod = bigint; +export type DurationSeconds = bigint; /** Index of an epoch, or number of epochs. */ export type Epoch = bigint; @@ -340,8 +339,8 @@ export interface CooldownParametersV0 { } export interface CooldownParametersV1 { - poolOwnerCooldown: RewardPeriod; - delegatorCooldown: RewardPeriod; + poolOwnerCooldown: DurationSeconds; + delegatorCooldown: DurationSeconds; } export interface PoolParametersV0 { From ae4f163bd47b9d737999e05d309066826fb764f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Tue, 1 Mar 2022 12:54:35 +0100 Subject: [PATCH 17/29] Added overload with optional baker ID --- src/client.ts | 4 ++++ src/types.ts | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/client.ts b/src/client.ts index af642cebe..4174d4a3b 100644 --- a/src/client.ts +++ b/src/client.ts @@ -657,6 +657,10 @@ export default class ConcordiumNodeClient { * @param [bakerId] the ID of the baker to get the status for. If left undefined, the status of the L-pool is returned. * @returns The status of the corresponding pool. */ + async getPoolStatus( + blockHash: string, + bakerId?: BakerId + ): Promise; async getPoolStatus( blockHash: string, bakerId?: BakerId diff --git a/src/types.ts b/src/types.ts index fe20dc45b..3aa26aa9c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -276,9 +276,6 @@ export interface InclusiveRange { max: N; } -/** - * A duration in seconds. - */ export type DurationSeconds = bigint; /** Index of an epoch, or number of epochs. */ export type Epoch = bigint; From bc5bc2585f28e9af8e11d376b6efa2dac9d818db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Tue, 1 Mar 2022 14:44:35 +0100 Subject: [PATCH 18/29] .. --- test/client.test.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/client.test.ts b/test/client.test.ts index abc49b7b1..fae252f2f 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -410,14 +410,12 @@ test('account info with baker details, and with no pending change', async () => throw new Error('Test failed to find account info'); } - const bakerDetails = isBakerAccount(accountInfo) - ? accountInfo.accountBaker - : undefined; - - if (!bakerDetails) { + if (!isBakerAccount(accountInfo)) { throw new Error('Account info doesnt contain baker details'); } + const bakerDetails = accountInfo.accountBaker; + expect(bakerDetails.bakerId).toEqual(743n); expect(bakerDetails.stakedAmount).toEqual(15000000000n); expect(bakerDetails.restakeEarnings).toEqual(true); From 09ff32fe28a250ef62a00319ca551c86600a92d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Wed, 2 Mar 2022 10:13:46 +0100 Subject: [PATCH 19/29] Added reward status query --- src/client.ts | 40 ++++++++++++++++++++++++++++++++++++ src/types.ts | 29 ++++++++++++++++++++++++++ test/client.test.ts | 50 +++++++++++++++++++++++++++++++++++++-------- 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/src/client.ts b/src/client.ts index 4174d4a3b..fb7b30695 100644 --- a/src/client.ts +++ b/src/client.ts @@ -59,6 +59,9 @@ import { BakerPoolPendingChangeReduceBakerCapitalDetails, LPoolStatus, BakerPoolStatus, + RewardStatusV0, + RewardStatus, + RewardStatusV1, } from './types'; import { buildJsonResponseReviver, @@ -613,6 +616,43 @@ export default class ConcordiumNodeClient { } } + async getRewardStatus( + blockHash: string + ): Promise { + if (!isValidHash(blockHash)) { + throw new Error('The input was not a valid hash: ' + blockHash); + } + + type DateKey = KeysMatching; + type BigIntKey = KeysMatching; + + const dates: DateKey[] = ['nextPaydayTime']; + const bigInts: BigIntKey[] = [ + 'protocolVersion', + 'gasAccount', + 'totalAmount', + 'totalStakedCapital', + 'bakingRewardAccount', + 'totalEncryptedAmount', + 'finalizationRewardAccount', + 'foundationTransactionRewards', + ]; + + const bh = new BlockHash(); + bh.setBlockHash(blockHash); + + const response = await this.sendRequest( + this.client.getRewardStatus, + bh + ); + + return unwrapJsonResponse( + response, + buildJsonResponseReviver(dates, bigInts), + intToStringTransformer(bigInts) + ); + } + /** * Retrieve list of bakers on the network. * @param blockHash the block hash to get the smart contact instances at diff --git a/src/types.ts b/src/types.ts index 3aa26aa9c..7965448a4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -357,6 +357,9 @@ export interface PoolParametersV1 { } export interface TimeParametersV1 { + /** + * In epochs + */ rewardPeriodLength: Epoch; mintPerPayday: number; } @@ -552,6 +555,26 @@ export interface BlockSummaryV1 extends BlockSummaryCommon { export type BlockSummary = BlockSummaryV0 | BlockSummaryV1; +interface RewardStatusCommon { + totalAmount: Amount; + totalEncryptedAmount: Amount; + bakingRewardAccount: Amount; + finalizationRewardAccount: Amount; + gasAccount: Amount; +} + +export type RewardStatusV0 = RewardStatusCommon; + +export interface RewardStatusV1 extends RewardStatusCommon { + foundationTransactionRewards: Amount; + nextPaydayTime: Date; + nextPaydayMintRate: MintRate; + totalStakedCapital: Amount; + protocolVersion: bigint; +} + +export type RewardStatus = RewardStatusV0 | RewardStatusV1; + export interface BlockInfo { blockParent: string; blockHash: string; @@ -579,7 +602,13 @@ export interface ConsensusStatus { currentEraGenesisBlock: string; lastFinalizedBlock: string; + /** + * In milliseconds + */ epochDuration: bigint; + /** + * In milliseconds + */ slotDuration: bigint; bestBlockHeight: bigint; lastFinalizedBlockHeight: bigint; diff --git a/test/client.test.ts b/test/client.test.ts index fae252f2f..eab19ab1f 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -106,7 +106,7 @@ test('transferred event is parsed correctly', async () => { } }); -test('block summary for valid block hash retrieves block summary', async () => { +test('block summary for valid block hash retrieves block summary (v0)', async () => { const blockHash = '4b39a13d326f422c76f12e20958a90a4af60a2b7e098b2a59d21d402fff44bfc'; const blockSummary = await client.getBlockSummary(blockHash); @@ -114,21 +114,21 @@ test('block summary for valid block hash retrieves block summary', async () => { throw new Error('The block could not be found by the test'); } - // Pre delegation protocol version (protocol version 4) - if (!isBlockSummaryV1(blockSummary)) { + if (isBlockSummaryV1(blockSummary)) { + throw new Error('Expected block to adhere to version 0 spec.'); + } + + return Promise.all([ expect( blockSummary.updates.chainParameters.rewardParameters .mintDistribution.mintPerSlot - ).toBe(7.555665e-10); + ).toBe(7.555665e-10), expect( blockSummary.updates.chainParameters.minimumThresholdForBaking - ).toBe(15000000000n); + ).toBe(15000000000n), expect(blockSummary.updates.chainParameters.bakerCooldownEpochs).toBe( 166n - ); - } - - return Promise.all([ + ), expect(blockSummary.finalizationData.finalizationIndex).toBe(15436n), expect(blockSummary.finalizationData.finalizationDelay).toBe(0n), expect(blockSummary.finalizationData.finalizationBlockPointer).toBe( @@ -1074,3 +1074,35 @@ test('anonymity revokers are retrieved at the given block', async () => { ), ]); }); + +test('reward status can be accessed at given block', async () => { + const blockHash = + '7f7409679e53875567e2ae812c9fcefe90ced8761d08554756f42bf268a42749'; + + const rewardStatus = await client.getRewardStatus(blockHash); + + if (!rewardStatus) { + throw new Error('Test could not retrieve reward status of block.'); + } + + const { + finalizationRewardAccount, + totalEncryptedAmount, + bakingRewardAccount, + totalAmount, + gasAccount, + } = rewardStatus; + + expect(finalizationRewardAccount.toString()).toBe('5'); + expect(totalEncryptedAmount.toString()).toBe('0'); + expect(bakingRewardAccount.toString()).toBe('3663751591'); + expect(totalAmount.toString()).toBe('10014486887211834'); + expect(gasAccount.toString()).toBe('3'); +}); + +test('reward status is undefined at an unknown block', async () => { + const blockHash = + '7f7409679e53875567e2ae812c9fcefe90ced8961d08554756f42bf268a42749'; + const rs = await client.getRewardStatus(blockHash); + return expect(rs).toBeUndefined(); +}); From a92cbacae428dd1f9f13a04189129973d80bebd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Wed, 2 Mar 2022 11:03:06 +0100 Subject: [PATCH 20/29] helpers for reward status --- src/rewardStatusHelpers.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/rewardStatusHelpers.ts diff --git a/src/rewardStatusHelpers.ts b/src/rewardStatusHelpers.ts new file mode 100644 index 000000000..2fb4f4471 --- /dev/null +++ b/src/rewardStatusHelpers.ts @@ -0,0 +1,4 @@ +import { RewardStatus, RewardStatusV1 } from './types'; + +export const isRewardStatusV1 = (rs: RewardStatus): rs is RewardStatusV1 => + (rs as RewardStatusV1).protocolVersion !== undefined; From 1fe6bdde61352051064fcbd763a26649dd21207a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Mon, 7 Mar 2022 12:00:35 +0100 Subject: [PATCH 21/29] Small fixes according to PR feedback --- src/client.ts | 4 +-- src/types.ts | 18 ++++++------- test/client.test.ts | 64 +++++++++++++++++++++++++++++++++++++++++---- test/testHelpers.ts | 4 +-- 4 files changed, 72 insertions(+), 18 deletions(-) diff --git a/src/client.ts b/src/client.ts index fb7b30695..17db54341 100644 --- a/src/client.ts +++ b/src/client.ts @@ -670,9 +670,9 @@ export default class ConcordiumNodeClient { return unwrapJsonResponse( response, - (_, v) => BigInt(v as string), + undefined, intListToStringList - ); + )?.map((v) => BigInt(v)); } /** diff --git a/src/types.ts b/src/types.ts index 7965448a4..1b5b7904d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -479,7 +479,6 @@ interface UpdateQueuesCommon { mintDistribution: UpdateQueue; protocol: UpdateQueue; gasRewards: UpdateQueue; - bakerStakeThreshold: UpdateQueue; addAnonymityRevoker: UpdateQueue; addIdentityProvider: UpdateQueue; rootKeys: UpdateQueue; @@ -490,7 +489,9 @@ interface UpdateQueuesCommon { /** * Used from protocol version 1-3 */ -export type UpdateQueuesV0 = UpdateQueuesCommon; +export interface UpdateQueuesV0 extends UpdateQueuesCommon { + bakerStakeThreshold: UpdateQueue; +} /** * Used from protocol version 4 @@ -498,6 +499,7 @@ export type UpdateQueuesV0 = UpdateQueuesCommon; export interface UpdateQueuesV1 extends UpdateQueuesCommon { cooldownParameters: UpdateQueue; timeParameters: UpdateQueue; + poolParameters: UpdateQueue; } export type UpdateQueues = UpdateQueuesV0 | UpdateQueuesV1; @@ -787,13 +789,12 @@ export enum BakerPoolPendingChangeType { NoChange = 'NoChange', } -interface BakerPoolPendingChangeWrapper< +type BakerPoolPendingChangeWrapper< T extends keyof typeof BakerPoolPendingChangeType, S -> { +> = S & { pendingChangeType: T; - pendingChangeDetails: S; -} +}; export interface BakerPoolPendingChangeReduceBakerCapitalDetails { bakerEquityCapital: Amount; @@ -830,10 +831,9 @@ export enum PoolStatusType { LPool = 'LPool', } -interface PoolStatusWrapper { +type PoolStatusWrapper = S & { poolType: T; - poolStatus: S; -} +}; export interface BakerPoolStatusDetails { bakerId: BakerId; diff --git a/test/client.test.ts b/test/client.test.ts index eab19ab1f..be4b7c48f 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -4,6 +4,7 @@ import { instanceOfTransferWithMemoTransactionSummary, NormalAccountCredential, TransferredWithScheduleEvent, + PoolStatusType, } from '../src/types'; import { AccountAddress } from '../src/types/accountAddress'; import { isHex } from '../src/util'; @@ -1093,11 +1094,11 @@ test('reward status can be accessed at given block', async () => { gasAccount, } = rewardStatus; - expect(finalizationRewardAccount.toString()).toBe('5'); - expect(totalEncryptedAmount.toString()).toBe('0'); - expect(bakingRewardAccount.toString()).toBe('3663751591'); - expect(totalAmount.toString()).toBe('10014486887211834'); - expect(gasAccount.toString()).toBe('3'); + expect(finalizationRewardAccount).toBe(5n); + expect(totalEncryptedAmount).toBe(0n); + expect(bakingRewardAccount).toBe(3663751591n); + expect(totalAmount).toBe(10014486887211834n); + expect(gasAccount).toBe(3n); }); test('reward status is undefined at an unknown block', async () => { @@ -1106,3 +1107,56 @@ test('reward status is undefined at an unknown block', async () => { const rs = await client.getRewardStatus(blockHash); return expect(rs).toBeUndefined(); }); + +test('baker list can be accessed at given block', async () => { + const blockHash = + '1e69dbed0234f0e8cf7965191bae42cd49415646984346e01716c8f8577ab6e0'; + const bl = await client.getBakerList(blockHash); + + expect(bl).toEqual([0n, 1n, 2n, 3n, 4n]); +}); + +test('baker list is undefined at an unknown block', async () => { + const blockHash = + '7f7409679e53875567e2ae812c9fcefe90ced8961d08554756f42bf268a42749'; + const bl = await client.getBakerList(blockHash); + return expect(bl).toBeUndefined(); +}); + +test('pool status can be accessed at given block for L-pool', async () => { + const blockHash = + '1e69dbed0234f0e8cf7965191bae42cd49415646984346e01716c8f8577ab6e0'; + + const ps = await client.getPoolStatus(blockHash); + + if (!ps) { + throw new Error('Test could not retrieve reward status of block.'); + } + + expect(ps.poolType).toBe(PoolStatusType.LPool); + + if (ps.poolType !== PoolStatusType.LPool) { + throw new Error('Test assumes pool status type of L-pool'); + } + + const { + commissionRates, + delegatedCapital, + currentPaydayDelegatedCapital, + currentPaydayTransactionFeesEarned, + } = ps; + + expect(commissionRates.bakingCommission).toBe(0.1); + expect(commissionRates.transactionCommission).toBe(0.1); + expect(commissionRates.finalizationCommission).toBe(1); + expect(delegatedCapital.toString()).toBe('1000000000'); + expect(currentPaydayDelegatedCapital.toString()).toBe('0'); + expect(currentPaydayTransactionFeesEarned.toString()).toBe('0'); +}); + +test('pool status is undefined at an unknown block', async () => { + const blockHash = + '7f7409679e53875567e2ae812c9fcefe90ced8961d08554756f42bf268a42749'; + const ps = await client.getPoolStatus(blockHash); + return expect(ps).toBeUndefined(); +}); diff --git a/test/testHelpers.ts b/test/testHelpers.ts index 8c3df0970..2d673b91d 100644 --- a/test/testHelpers.ts +++ b/test/testHelpers.ts @@ -9,11 +9,11 @@ import { MobileWalletExport } from '../src/wallet/types'; * Creates a client to communicate with a local concordium-node * used for automatic tests. */ -export function getNodeClient(): ConcordiumNodeClient { +export function getNodeClient(address = '127.0.0.1'): ConcordiumNodeClient { const metadata = new Metadata(); metadata.add('authentication', 'rpcadmin'); return new ConcordiumNodeClient( - '127.0.0.1', + address, 10000, credentials.createInsecure(), metadata, From 2391191a747a76088c9db1548e103fa64d3d250e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Mon, 7 Mar 2022 12:06:00 +0100 Subject: [PATCH 22/29] Add explanation of level 2 key responsibility change --- src/types.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/types.ts b/src/types.ts index 1b5b7904d..a9dcf1234 100644 --- a/src/types.ts +++ b/src/types.ts @@ -409,6 +409,9 @@ interface AuthorizationsCommon { mintDistribution: Authorization; protocol: Authorization; paramGASRewards: Authorization; + /** + * From protocol version 4 and later, these control the configuration of poolParamenters. + */ bakerStakeThreshold: Authorization; electionDifficulty: Authorization; addAnonymityRevoker: Authorization; From 12d4952fc9fe960283c3ad316f5f8c5226611f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Mon, 7 Mar 2022 13:32:20 +0100 Subject: [PATCH 23/29] Added test for status of specific baker --- src/types.ts | 13 +++++++--- test/client.test.ts | 63 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/src/types.ts b/src/types.ts index a9dcf1234..0b66c3338 100644 --- a/src/types.ts +++ b/src/types.ts @@ -761,11 +761,17 @@ export enum OpenStatus { ClosedForAll = 2, } +export enum OpenStatusText { + OpenForAll = 'openForAll', + ClosedForNew = 'closedForNew', + ClosedForAll = 'closedForAll', +} + export type Amount = bigint; export type BakerId = bigint; export interface BakerPoolInfo { - openStatus: OpenStatus; + openStatus: OpenStatusText; metadataUrl: string; commissionRates: CommissionRates; } @@ -794,7 +800,7 @@ export enum BakerPoolPendingChangeType { type BakerPoolPendingChangeWrapper< T extends keyof typeof BakerPoolPendingChangeType, - S + S extends Record > = S & { pendingChangeType: T; }; @@ -821,7 +827,8 @@ export type BakerPoolPendingChangeRemovePool = BakerPoolPendingChangeWrapper< export type BakerPoolPendingChangeNoChange = BakerPoolPendingChangeWrapper< BakerPoolPendingChangeType.NoChange, - undefined + // eslint-disable-next-line @typescript-eslint/ban-types + {} >; export type BakerPoolPendingChange = diff --git a/test/client.test.ts b/test/client.test.ts index be4b7c48f..317124fdc 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -5,6 +5,9 @@ import { NormalAccountCredential, TransferredWithScheduleEvent, PoolStatusType, + BakerId, + BakerPoolPendingChangeType, + OpenStatusText, } from '../src/types'; import { AccountAddress } from '../src/types/accountAddress'; import { isHex } from '../src/util'; @@ -1135,10 +1138,6 @@ test('pool status can be accessed at given block for L-pool', async () => { expect(ps.poolType).toBe(PoolStatusType.LPool); - if (ps.poolType !== PoolStatusType.LPool) { - throw new Error('Test assumes pool status type of L-pool'); - } - const { commissionRates, delegatedCapital, @@ -1154,6 +1153,62 @@ test('pool status can be accessed at given block for L-pool', async () => { expect(currentPaydayTransactionFeesEarned.toString()).toBe('0'); }); +test('pool status can be accessed at given block for specific baker', async () => { + const blockHash = + '1e69dbed0234f0e8cf7965191bae42cd49415646984346e01716c8f8577ab6e0'; + const bid: BakerId = 0n; + + const ps = await client.getPoolStatus(blockHash, bid); + + if (!ps) { + throw new Error('Test could not retrieve reward status of block.'); + } + + expect(ps.poolType).toBe(PoolStatusType.BakerPool); + + const { + delegatedCapital, + bakerId, + poolInfo: { + openStatus, + metadataUrl, + commissionRates: { + finalizationCommission, + transactionCommission, + bakingCommission, + }, + }, + bakerAddress, + bakerEquityCapital, + delegatedCapitalCap, + bakerStakePendingChange, + currentPaydayStatus, + } = ps; + + expect(bakerId).toBe(0n); + expect(bakerAddress).toBe( + '2zmRFpd7g12oBAZHSDqnbJ3Eg5HGr2sE9aFCL6mD3pyUSsiDSJ' + ); + expect(delegatedCapital).toBe(1000000000n); + expect(openStatus).toBe(OpenStatusText.OpenForAll); + expect(metadataUrl).toBe(''); + expect(finalizationCommission).toBe(1); + expect(transactionCommission).toBe(0.05); + expect(bakingCommission).toBe(0.05); + expect(bakerEquityCapital).toBe(3000000000000n); + expect(delegatedCapitalCap).toBe(1000333333333n); + expect(bakerStakePendingChange.pendingChangeType).toBe( + BakerPoolPendingChangeType.NoChange + ); + expect(currentPaydayStatus?.bakerEquityCapital).toBe(3000000000000n); + expect(currentPaydayStatus?.blocksBaked).toBe(9n); + expect(currentPaydayStatus?.finalizationLive).toBe(true); + expect(currentPaydayStatus?.transactionFeesEarned).toBe(0n); + expect(currentPaydayStatus?.effectiveStake).toBe(3000000000000n); + expect(currentPaydayStatus?.lotteryPower).toBe(0.2); + expect(currentPaydayStatus?.delegatedCapital).toBe(0n); +}); + test('pool status is undefined at an unknown block', async () => { const blockHash = '7f7409679e53875567e2ae812c9fcefe90ced8961d08554756f42bf268a42749'; From 2127cd9d4d1f6da0e7997fcadefd0de95cf831b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Mon, 7 Mar 2022 14:31:08 +0100 Subject: [PATCH 24/29] Tests for new version of reward status --- src/types.ts | 5 ++++- test/client.test.ts | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/types.ts b/src/types.ts index 0b66c3338..814aa0959 100644 --- a/src/types.ts +++ b/src/types.ts @@ -296,7 +296,7 @@ export interface MintRate { } export interface MintDistributionV0 extends MintDistributionCommon { - mintPerSlot: MintRate; + mintPerSlot: number; } export type MintDistributionV1 = MintDistributionCommon; @@ -761,6 +761,9 @@ export enum OpenStatus { ClosedForAll = 2, } +/** + * How the node translates OpenStatus to JSON. + */ export enum OpenStatusText { OpenForAll = 'openForAll', ClosedForNew = 'closedForNew', diff --git a/test/client.test.ts b/test/client.test.ts index 317124fdc..e469092c9 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -18,6 +18,7 @@ import { PeerElement } from '../grpc/concordium_p2p_rpc_pb'; import { CredentialRegistrationId } from '../src/types/CredentialRegistrationId'; import { isBlockSummaryV1 } from '../src/blockSummaryHelpers'; import { isBakerAccount } from '../src/accountHelpers'; +import { isRewardStatusV1 } from '../src/rewardStatusHelpers'; const client = getNodeClient(); @@ -1104,6 +1105,47 @@ test('reward status can be accessed at given block', async () => { expect(gasAccount).toBe(3n); }); +test('new version of reward status can be accessed at given block', async () => { + const blockHash = + '1e69dbed0234f0e8cf7965191bae42cd49415646984346e01716c8f8577ab6e0'; + + const rewardStatus = await client.getRewardStatus(blockHash); + + if (!rewardStatus) { + throw new Error('Test could not retrieve reward status of block.'); + } + + if (!isRewardStatusV1(rewardStatus)) { + throw new Error( + 'Test expected reward status to be delegation protocol version.' + ); + } + + const { + finalizationRewardAccount, + totalEncryptedAmount, + bakingRewardAccount, + totalAmount, + gasAccount, + nextPaydayTime, + protocolVersion, + nextPaydayMintRate, + totalStakedCapital, + foundationTransactionRewards, + } = rewardStatus; + + expect(finalizationRewardAccount).toBe(3n); + expect(totalEncryptedAmount).toBe(0n); + expect(bakingRewardAccount).toBe(2n); + expect(totalAmount).toBe(188279875066742n); + expect(gasAccount).toBe(3n); + expect(protocolVersion).toBe(4n); + expect(totalStakedCapital).toBe(15002000000000n); + expect(foundationTransactionRewards).toBe(0n); + expect(nextPaydayTime).toEqual(new Date('2022-03-07T07:15:01.5Z')); + expect(nextPaydayMintRate).toBe(1.088e-5); +}); + test('reward status is undefined at an unknown block', async () => { const blockHash = '7f7409679e53875567e2ae812c9fcefe90ced8961d08554756f42bf268a42749'; From 71413204c73aa22e35a9696f62206a4df090f484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Tue, 8 Mar 2022 08:50:27 +0100 Subject: [PATCH 25/29] Update src/types.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Søren Hjort <87635671+shjortConcordium@users.noreply.github.com> --- src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.ts b/src/types.ts index 0b66c3338..57a465bb9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -410,7 +410,7 @@ interface AuthorizationsCommon { protocol: Authorization; paramGASRewards: Authorization; /** - * From protocol version 4 and later, these control the configuration of poolParamenters. + * From protocol version 4 and later, this controls the authorization of the poolParameters update. */ bakerStakeThreshold: Authorization; electionDifficulty: Authorization; From 53e000cdee84d35856699bf24bbb2f08a8a2ff38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Tue, 8 Mar 2022 11:41:14 +0100 Subject: [PATCH 26/29] unit tests for new account info variants (baker and delegator) --- src/types.ts | 4 +- test/client.test.ts | 140 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 133 insertions(+), 11 deletions(-) diff --git a/src/types.ts b/src/types.ts index 814aa0959..e5c31e0d1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -884,11 +884,11 @@ export enum DelegationTargetType { } export interface DelegationTargetLPool { - delegationType: DelegationTargetType.LPool; + delegateType: DelegationTargetType.LPool; } export interface DelegationTargetBaker { - delegationType: DelegationTargetType.Baker; + delegateType: DelegationTargetType.Baker; bakerId: BakerId; } diff --git a/test/client.test.ts b/test/client.test.ts index e469092c9..6b262a1b9 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -8,6 +8,8 @@ import { BakerId, BakerPoolPendingChangeType, OpenStatusText, + DelegationTargetType, + DelegationTargetBaker, } from '../src/types'; import { AccountAddress } from '../src/types/accountAddress'; import { isHex } from '../src/util'; @@ -17,7 +19,11 @@ import { ipVerifyKey1, ipVerifyKey2 } from './resources/ipVerifyKeys'; import { PeerElement } from '../grpc/concordium_p2p_rpc_pb'; import { CredentialRegistrationId } from '../src/types/CredentialRegistrationId'; import { isBlockSummaryV1 } from '../src/blockSummaryHelpers'; -import { isBakerAccount } from '../src/accountHelpers'; +import { + isBakerAccount, + isBakerAccountV1, + isDelegatorAccount, +} from '../src/accountHelpers'; import { isRewardStatusV1 } from '../src/rewardStatusHelpers'; const client = getNodeClient(); @@ -1198,7 +1204,7 @@ test('pool status can be accessed at given block for L-pool', async () => { test('pool status can be accessed at given block for specific baker', async () => { const blockHash = '1e69dbed0234f0e8cf7965191bae42cd49415646984346e01716c8f8577ab6e0'; - const bid: BakerId = 0n; + const bid: BakerId = 1n; const ps = await client.getPoolStatus(blockHash, bid); @@ -1206,9 +1212,8 @@ test('pool status can be accessed at given block for specific baker', async () = throw new Error('Test could not retrieve reward status of block.'); } - expect(ps.poolType).toBe(PoolStatusType.BakerPool); - const { + poolType, delegatedCapital, bakerId, poolInfo: { @@ -1227,23 +1232,27 @@ test('pool status can be accessed at given block for specific baker', async () = currentPaydayStatus, } = ps; - expect(bakerId).toBe(0n); + console.log(ps); + console.log(ps.currentPaydayStatus); + + expect(poolType).toBe(PoolStatusType.BakerPool); + expect(bakerId).toBe(1n); expect(bakerAddress).toBe( - '2zmRFpd7g12oBAZHSDqnbJ3Eg5HGr2sE9aFCL6mD3pyUSsiDSJ' + '39BjG2g6JaTUVSEizZQu6DrsPjwNMKW2ftqToBvTVTMna7pNud' ); - expect(delegatedCapital).toBe(1000000000n); + expect(delegatedCapital).toBe(0n); expect(openStatus).toBe(OpenStatusText.OpenForAll); expect(metadataUrl).toBe(''); expect(finalizationCommission).toBe(1); expect(transactionCommission).toBe(0.05); expect(bakingCommission).toBe(0.05); expect(bakerEquityCapital).toBe(3000000000000n); - expect(delegatedCapitalCap).toBe(1000333333333n); + expect(delegatedCapitalCap).toBe(1000666666666n); expect(bakerStakePendingChange.pendingChangeType).toBe( BakerPoolPendingChangeType.NoChange ); expect(currentPaydayStatus?.bakerEquityCapital).toBe(3000000000000n); - expect(currentPaydayStatus?.blocksBaked).toBe(9n); + expect(currentPaydayStatus?.blocksBaked).toBe(25n); expect(currentPaydayStatus?.finalizationLive).toBe(true); expect(currentPaydayStatus?.transactionFeesEarned).toBe(0n); expect(currentPaydayStatus?.effectiveStake).toBe(3000000000000n); @@ -1257,3 +1266,116 @@ test('pool status is undefined at an unknown block', async () => { const ps = await client.getPoolStatus(blockHash); return expect(ps).toBeUndefined(); }); + +test('account info with new baker info can be accessed', async () => { + const blockHash = + '1e69dbed0234f0e8cf7965191bae42cd49415646984346e01716c8f8577ab6e0'; + const address = new AccountAddress( + '2zmRFpd7g12oBAZHSDqnbJ3Eg5HGr2sE9aFCL6mD3pyUSsiDSJ' + ); + + const ai = await client.getAccountInfo(address, blockHash); + + if (!ai) { + throw new Error('Expected account info to be accessible.'); + } + + if (!isBakerAccountV1(ai)) { + throw new Error( + 'Test assumes the account is a baker on delegation protocol version' + ); + } + + const { + accountBaker: { + bakerId, + stakedAmount, + bakerPoolInfo: { + metadataUrl, + openStatus, + commissionRates: { + bakingCommission, + transactionCommission, + finalizationCommission, + }, + }, + restakeEarnings, + pendingChange, + }, + } = ai; + + expect(bakerId).toBe(0n); + expect(stakedAmount).toBe(3000000000000n); + expect(restakeEarnings).toBe(false); + expect(pendingChange).toBeUndefined(); + expect(metadataUrl).toBe(''); + expect(openStatus).toBe(OpenStatusText.OpenForAll); + expect(bakingCommission).toBe(0.05); + expect(transactionCommission).toBe(0.05); + expect(finalizationCommission).toBe(1); +}); + +test('account info with delegation to specific baker can be accessed', async () => { + const blockHash = + '1e69dbed0234f0e8cf7965191bae42cd49415646984346e01716c8f8577ab6e0'; + const address = new AccountAddress( + '3UztagvMc6dMeRgW51tG8SR18BdHXaGyWBdx8nAz3caKTrWnuo' + ); + + const ai = await client.getAccountInfo(address, blockHash); + + if (!ai) { + throw new Error('Expected account info to be accessible.'); + } + + if (!isDelegatorAccount(ai)) { + throw new Error('Test assumes the account is a delegator'); + } + + const { + accountDelegation: { + restakeEarnings, + stakedAmount, + pendingChange, + delegationTarget, + }, + } = ai; + + expect(stakedAmount).toBe(1000000000n); + expect(restakeEarnings).toBe(true); + expect(pendingChange).toBeUndefined(); + expect(delegationTarget.delegateType).toBe(DelegationTargetType.Baker); + expect((delegationTarget as DelegationTargetBaker).bakerId).toBe(0n); +}); + +test('account info with delegation to L-pool can be accessed', async () => { + const blockHash = + '1e69dbed0234f0e8cf7965191bae42cd49415646984346e01716c8f8577ab6e0'; + const address = new AccountAddress( + '4Y7qexYDywtB8K5NySAqZDUqg8FBNwDdu616NdvVqUfVQ1ULSq' + ); + + const ai = await client.getAccountInfo(address, blockHash); + + if (!ai) { + throw new Error('Expected account info to be accessible.'); + } + + if (!isDelegatorAccount(ai)) { + throw new Error('Test assumes the account is a delegator'); + } + + const { + accountDelegation: { + restakeEarnings, + stakedAmount, + pendingChange, + delegationTarget, + }, + } = ai; + + expect(stakedAmount).toBe(1000000000n); + expect(restakeEarnings).toBe(true); + expect(pendingChange).toBeUndefined(); + expect(delegationTarget.delegateType).toBe(DelegationTargetType.LPool); +}); From 70b96c64b6a03672a899e7bf850cf6f1f81533a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Tue, 8 Mar 2022 13:51:39 +0100 Subject: [PATCH 27/29] unit tests for delegation version of block summary --- src/client.ts | 12 +++++- src/types.ts | 36 +++++++++--------- test/client.test.ts | 89 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 20 deletions(-) diff --git a/src/client.ts b/src/client.ts index 17db54341..355efff2f 100644 --- a/src/client.ts +++ b/src/client.ts @@ -347,8 +347,6 @@ export default class ConcordiumNodeClient { 'cost', 'energyCost', 'index', - 'bakerCooldownEpochs', - 'minimumThresholdForBaking', 'foundationAccountIndex', 'numerator', 'denominator', @@ -356,6 +354,16 @@ export default class ConcordiumNodeClient { 'amount', 'index', 'subindex', + + // v0 keys + 'bakerCooldownEpochs', + 'minimumThresholdForBaking', + + // v1 keys + 'rewardPeriodLength', + 'minimumEquityCapital', + 'poolOwnerCooldown', + 'delegatorCooldown', ]; return unwrapJsonResponse( diff --git a/src/types.ts b/src/types.ts index e747b76dc..0791ef19a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -281,13 +281,8 @@ export type DurationSeconds = bigint; export type Epoch = bigint; export interface TransactionFeeDistribution { - baker: RewardFraction; - gasAccount: RewardFraction; -} - -interface MintDistributionCommon { - bakingReward: RewardFraction; - finalizationReward: RewardFraction; + baker: number; + gasAccount: number; } export interface MintRate { @@ -295,6 +290,11 @@ export interface MintRate { exponent: number; } +interface MintDistributionCommon { + bakingReward: number; + finalizationReward: number; +} + export interface MintDistributionV0 extends MintDistributionCommon { mintPerSlot: number; } @@ -304,10 +304,10 @@ export type MintDistributionV1 = MintDistributionCommon; export type MintDistribution = MintDistributionV0 | MintDistributionV1; export interface GasRewards { - baker: RewardFraction; - finalizationProof: RewardFraction; - accountCreation: RewardFraction; - chainUpdate: RewardFraction; + baker: number; + finalizationProof: number; + accountCreation: number; + chainUpdate: number; } interface RewardParametersCommon { @@ -345,14 +345,14 @@ export interface PoolParametersV0 { } export interface PoolParametersV1 { - finalizationCommissionLPool: RewardFraction; - bakingCommissionLPool: RewardFraction; - transactionCommissionLPool: RewardFraction; - finalizationCommissionRange: InclusiveRange; - bakingCommissionRange: InclusiveRange; - transactionCommissionRange: InclusiveRange; + finalizationCommissionLPool: number; + bakingCommissionLPool: number; + transactionCommissionLPool: number; + finalizationCommissionRange: InclusiveRange; + bakingCommissionRange: InclusiveRange; + transactionCommissionRange: InclusiveRange; minimumEquityCapital: Amount; - capitalBound: RewardFraction; + capitalBound: number; leverageBound: Ratio; } diff --git a/test/client.test.ts b/test/client.test.ts index 6b262a1b9..04abd7c4b 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -285,6 +285,95 @@ test('block summary for valid block hash retrieves block summary (v0)', async () ]); }); +test('block summary for valid block hash retrieves block summary (v1)', async () => { + const blockHash = + '1e69dbed0234f0e8cf7965191bae42cd49415646984346e01716c8f8577ab6e0'; + const blockSummary = await client.getBlockSummary(blockHash); + if (!blockSummary) { + throw new Error('The block could not be found by the test'); + } + + if (!isBlockSummaryV1(blockSummary)) { + throw new Error('Expected block to adhere to version 1 spec.'); + } + + return Promise.all([ + // Chain parameters + expect(blockSummary.updates.chainParameters.mintPerPayday).toBe( + 1.088e-5 + ), + expect(blockSummary.updates.chainParameters.rewardPeriodLength).toBe( + 4n + ), + expect(blockSummary.updates.chainParameters.minimumEquityCapital).toBe( + 14000n + ), + expect(blockSummary.updates.chainParameters.capitalBound).toBe(0.25), + expect(blockSummary.updates.chainParameters.poolOwnerCooldown).toBe( + 10800n + ), + expect(blockSummary.updates.chainParameters.delegatorCooldown).toBe( + 7200n + ), + expect( + blockSummary.updates.chainParameters.transactionCommissionLPool + ).toBe(0.1), + expect( + blockSummary.updates.chainParameters.finalizationCommissionLPool + ).toBe(1.0), + expect(blockSummary.updates.chainParameters.bakingCommissionLPool).toBe( + 0.1 + ), + expect( + blockSummary.updates.chainParameters.leverageBound.numerator + ).toBe(3n), + expect( + blockSummary.updates.chainParameters.leverageBound.denominator + ).toBe(1n), + expect( + blockSummary.updates.chainParameters.transactionCommissionRange.min + ).toBe(0.05), + expect( + blockSummary.updates.chainParameters.transactionCommissionRange.max + ).toBe(0.05), + expect( + blockSummary.updates.chainParameters.bakingCommissionRange.min + ).toBe(0.05), + expect( + blockSummary.updates.chainParameters.bakingCommissionRange.max + ).toBe(0.05), + expect( + blockSummary.updates.chainParameters.finalizationCommissionRange.min + ).toBe(1), + expect( + blockSummary.updates.chainParameters.finalizationCommissionRange.max + ).toBe(1), + + // Update queues + expect( + blockSummary.updates.updateQueues.protocol.nextSequenceNumber + ).toBe(4n), + expect( + blockSummary.updates.updateQueues.cooldownParameters + .nextSequenceNumber + ).toBe(1n), + expect( + blockSummary.updates.updateQueues.timeParameters.nextSequenceNumber + ).toBe(1n), + expect( + blockSummary.updates.updateQueues.poolParameters.nextSequenceNumber + ).toBe(1n), + + // keys + expect( + blockSummary.updates.keys.level2Keys.cooldownParameters.threshold + ).toBe(1), + expect( + blockSummary.updates.keys.level2Keys.timeParameters.threshold + ).toBe(1), + ]); +}); + test('block summary for invalid block hash throws error', async () => { const invalidBlockHash = 'fd4915edca67b4e8f6521641a638a3abdbdd7934e42a9a52d8673861e2ebdd2'; From 5f3ac13880aaf92cf4970cffa2a0b9d1c53193ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Tue, 8 Mar 2022 14:17:08 +0100 Subject: [PATCH 28/29] Removed notion of reward fraction, as it was incorrectly used for response models --- src/types.ts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/types.ts b/src/types.ts index 0791ef19a..51520363b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -12,14 +12,6 @@ export type KeysMatching = { [K in keyof T]-?: T[K] extends V ? K : never; }[keyof T]; -/** - * A reward fraction with a resolution of 1/100000, i.e. the - * denominator is implicitly 100000, and the interface therefore - * only contains the numerator value which can be in the interval - * [1, 100000]. - */ -export type RewardFraction = number; - /* eslint-disable @typescript-eslint/no-explicit-any */ export interface Versioned { v: number; @@ -780,9 +772,9 @@ export interface BakerPoolInfo { } export interface CommissionRates { - transactionCommission: RewardFraction; - bakingCommission: RewardFraction; - finalizationCommission: RewardFraction; + transactionCommission: number; + bakingCommission: number; + finalizationCommission: number; } export interface CurrentPaydayBakerPoolStatus { From 0bbb678d409990230ce1c027021ae8c50c0dd02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bruus=20Zeppelin?= Date: Wed, 9 Mar 2022 08:36:38 +0100 Subject: [PATCH 29/29] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changelog update Co-authored-by: Søren Hjort <87635671+shjortConcordium@users.noreply.github.com> --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bf111169..447e4a5a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,13 @@ # Changelog -## Unreleased +## 0.7.0 2022-03-09 ### Added - Support for getting baker list from node. - Support for getting status of Baker-/L-pool (required node to have protocol version 4 or later) - Helper functions for determining the version of `BlockSummary` and nested types. +- Support for initiating and updating contracts with parameters. ### Changed