Skip to content

Commit d48e349

Browse files
committed
feat!: store stake reg deposit in reward acct info
Chain history provider returns the stake registration certificates from conway-era, which include the deposit value. Store this value in the RewardAccountInfo, to be used at deregistration. BREAKING CHANGE: `isLastStakeKeyCertOfType` was renamed to `lastStakeKeyCertOfType` and returns the certificate or undefined.
1 parent 6825506 commit d48e349

File tree

7 files changed

+81
-51
lines changed

7 files changed

+81
-51
lines changed

packages/core/src/Cardano/types/Certificate.ts

+6-15
Original file line numberDiff line numberDiff line change
@@ -176,21 +176,19 @@ export type Certificate =
176176
| UnRegisterDelegateRepresentativeCertificate
177177
| UpdateDelegateRepresentativeCertificate;
178178

179-
export const StakeRegistrationCertificateTypes = [
180-
CertificateType.StakeRegistration,
179+
export const PostConwayStakeRegistrationCertificateTypes = [
181180
CertificateType.Registration,
182181
CertificateType.VoteRegistrationDelegation,
183182
CertificateType.StakeRegistrationDelegation,
184183
CertificateType.StakeVoteRegistrationDelegation
185184
] as const;
186185

187-
export type StakeRegistrationCertificateTypes = typeof StakeRegistrationCertificateTypes[number];
186+
export const StakeRegistrationCertificateTypes = [
187+
CertificateType.StakeRegistration,
188+
...PostConwayStakeRegistrationCertificateTypes
189+
] as const;
188190

189-
export type StakeDelegationCertificateUnion =
190-
| StakeDelegationCertificate
191-
| StakeVoteDelegationCertificate
192-
| StakeRegistrationDelegationCertificate
193-
| StakeVoteRegistrationDelegationCertificate;
191+
export type StakeRegistrationCertificateTypes = typeof StakeRegistrationCertificateTypes[number];
194192

195193
export const StakeDelegationCertificateTypes = [
196194
CertificateType.StakeDelegation,
@@ -201,13 +199,6 @@ export const StakeDelegationCertificateTypes = [
201199

202200
export type StakeDelegationCertificateTypes = typeof StakeDelegationCertificateTypes[number];
203201

204-
export type RegAndDeregCertificateUnion =
205-
| StakeAddressCertificate
206-
| NewStakeAddressCertificate
207-
| VoteRegistrationDelegationCertificate
208-
| StakeRegistrationDelegationCertificate
209-
| StakeVoteRegistrationDelegationCertificate;
210-
211202
export const RegAndDeregCertificateTypes = [
212203
...StakeRegistrationCertificateTypes,
213204
CertificateType.Unregistration,

packages/core/src/Cardano/types/DelegationsAndRewards.ts

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface RewardAccountInfo {
2929
delegatee?: Delegatee;
3030
rewardBalance: Lovelace;
3131
// Maybe add rewardsHistory for each reward account too
32+
deposit?: Lovelace; // defined only when keyStatus is Registered
3233
}
3334

3435
export interface Cip17Pool {

packages/wallet/src/services/DelegationTracker/RewardAccounts.ts

+31-17
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { RetryBackoffConfig } from 'backoff-rxjs';
2525
import { TrackedStakePoolProvider } from '../ProviderTracker';
2626
import { TxWithEpoch } from './types';
2727
import { coldObservableProvider } from '@cardano-sdk/util-rxjs';
28-
import { isLastStakeKeyCertOfType } from './transactionCertificates';
28+
import { lastStakeKeyCertOfType } from './transactionCertificates';
2929
import findLast from 'lodash/findLast';
3030
import isEqual from 'lodash/isEqual';
3131
import uniq from 'lodash/uniq';
@@ -124,15 +124,12 @@ export const createRewardsProvider =
124124
);
125125
export type ObservableRewardsProvider = ReturnType<typeof createRewardsProvider>;
126126

127-
const isDelegationCertificate = (cert: Cardano.Certificate): cert is Cardano.StakeDelegationCertificateUnion =>
128-
Cardano.StakeDelegationCertificateTypes.includes(cert.__typename as Cardano.StakeDelegationCertificateTypes);
129-
130127
const getAccountsKeyStatus =
131128
(addresses: Cardano.RewardAccount[]) =>
132129
([transactions, transactionsInFlight]: [TxWithEpoch[], TxInFlight[]]) => {
133130
const certificatesInFlight = transactionsInFlight.map(({ body: { certificates } }) => certificates || []);
134131
return addresses.map((address) => {
135-
const isRegistered = isLastStakeKeyCertOfType(
132+
const regCert = lastStakeKeyCertOfType(
136133
transactions.map(
137134
({
138135
tx: {
@@ -143,23 +140,31 @@ const getAccountsKeyStatus =
143140
Cardano.StakeRegistrationCertificateTypes,
144141
address
145142
);
146-
const isRegistering = isLastStakeKeyCertOfType(
143+
144+
let deposit: Cardano.Lovelace | undefined;
145+
if (regCert && Cardano.isCertType(regCert, Cardano.PostConwayStakeRegistrationCertificateTypes)) {
146+
deposit = regCert.deposit;
147+
}
148+
149+
const isRegistering = !!lastStakeKeyCertOfType(
147150
certificatesInFlight,
148151
Cardano.StakeRegistrationCertificateTypes,
149152
address
150153
);
151-
const isUnregistering = isLastStakeKeyCertOfType(
154+
const isUnregistering = !!lastStakeKeyCertOfType(
152155
certificatesInFlight,
153156
[Cardano.CertificateType.StakeDeregistration, Cardano.CertificateType.Unregistration],
154157
address
155158
);
156-
return isRegistering
159+
const keyStatus = isRegistering
157160
? Cardano.StakeKeyStatus.Registering
158161
: isUnregistering
159162
? Cardano.StakeKeyStatus.Unregistering
160-
: isRegistered
163+
: regCert
161164
? Cardano.StakeKeyStatus.Registered
162165
: Cardano.StakeKeyStatus.Unregistered;
166+
167+
return { ...(keyStatus === Cardano.StakeKeyStatus.Registered && { deposit }), keyStatus };
163168
});
164169
};
165170

@@ -173,11 +178,15 @@ const accountCertificateTransactions = (
173178
transactions
174179
.map(({ tx, epoch }) => ({
175180
certificates: (tx.body.certificates || [])
176-
.filter((cert): cert is Cardano.RegAndDeregCertificateUnion | Cardano.StakeDelegationCertificateUnion =>
177-
[...Cardano.RegAndDeregCertificateTypes, ...Cardano.StakeDelegationCertificateTypes].includes(
178-
cert.__typename as Cardano.RegAndDeregCertificateTypes | Cardano.StakeDelegationCertificateTypes
179-
)
181+
.map((cert) =>
182+
Cardano.isCertType(cert, [
183+
...Cardano.RegAndDeregCertificateTypes,
184+
...Cardano.StakeDelegationCertificateTypes
185+
])
186+
? cert
187+
: null
180188
)
189+
.filter(isNotNil)
181190
.filter((cert) => (cert.stakeCredential.hash as unknown as Crypto.Ed25519KeyHashHex) === stakeKeyHash),
182191
epoch
183192
}))
@@ -204,15 +213,19 @@ export const getStakePoolIdAtEpoch = (transactions: TransactionsCertificates) =>
204213
const certificatesUpToEpoch = transactions
205214
.filter(({ epoch }) => epoch < atEpoch - 2)
206215
.map(({ certificates }) => certificates);
207-
if (!isLastStakeKeyCertOfType(certificatesUpToEpoch, Cardano.StakeRegistrationCertificateTypes)) {
216+
if (!lastStakeKeyCertOfType(certificatesUpToEpoch, Cardano.StakeRegistrationCertificateTypes)) {
208217
return;
209218
}
210219

211220
const delegationTxCertificates = findLast(certificatesUpToEpoch, (certs) =>
212221
Cardano.includesAnyCertificate(certs, Cardano.StakeDelegationCertificateTypes)
213222
);
214223
if (!delegationTxCertificates) return;
215-
return findLast(delegationTxCertificates.filter(isDelegationCertificate))?.poolId;
224+
return findLast(
225+
delegationTxCertificates
226+
.map((cert) => (Cardano.isCertType(cert, Cardano.StakeDelegationCertificateTypes) ? cert : null))
227+
.filter(isNotNil)
228+
)?.poolId;
216229
};
217230

218231
export const createDelegateeTracker = (
@@ -304,15 +317,16 @@ export const addressRewards = (
304317
export const toRewardAccounts =
305318
(addresses: Cardano.RewardAccount[]) =>
306319
([statuses, delegatees, rewards]: [
307-
Cardano.StakeKeyStatus[],
320+
{ keyStatus: Cardano.StakeKeyStatus; deposit?: Cardano.Lovelace }[],
308321
(Cardano.Delegatee | undefined)[],
309322
Cardano.Lovelace[]
310323
]) =>
311324
addresses.map(
312325
(address, i): Cardano.RewardAccountInfo => ({
313326
address,
314327
delegatee: delegatees[i],
315-
keyStatus: statuses[i],
328+
deposit: statuses[i].deposit,
329+
keyStatus: statuses[i].keyStatus,
316330
rewardBalance: rewards[i]
317331
})
318332
);

packages/wallet/src/services/DelegationTracker/transactionCertificates.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { isNotNil } from '@cardano-sdk/util';
55
import { transactionsEquals } from '../util/equals';
66
import last from 'lodash/last';
77

8-
export const isLastStakeKeyCertOfType = (
8+
export const lastStakeKeyCertOfType = <K extends Cardano.RegAndDeregCertificateTypes>(
99
transactionsCertificates: Cardano.Certificate[][],
10-
certTypes: readonly Cardano.RegAndDeregCertificateTypes[],
10+
certTypes: readonly K[],
1111
rewardAccount?: Cardano.RewardAccount
1212
) => {
1313
const stakeKeyHash = rewardAccount
@@ -24,7 +24,10 @@ export const isLastStakeKeyCertOfType = (
2424
})
2525
.filter(isNotNil)
2626
);
27-
return certTypes.includes(lastRegOrDereg?.__typename as Cardano.RegAndDeregCertificateTypes);
27+
28+
if (lastRegOrDereg && Cardano.isCertType(lastRegOrDereg, certTypes)) {
29+
return lastRegOrDereg;
30+
}
2831
};
2932

3033
export const transactionsWithCertificates = (

packages/wallet/test/services/DelegationTracker/DelegationTracker.test.ts

+21-3
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,35 @@ describe('DelegationTracker', () => {
103103
it('does not emit outgoing transactions with certificates not signed by the reward accounts', () => {
104104
createTestScheduler().run(({ cold, expectObservable }) => {
105105
const rewardAccount = Cardano.RewardAccount('stake_test1upqykkjq3zhf4085s6n70w8cyp57dl87r0ezduv9rnnj2uqk5zmdv');
106+
const foreignRewardAccount = Cardano.RewardAccount(
107+
'stake_test1up7pvfq8zn4quy45r2g572290p9vf99mr9tn7r9xrgy2l2qdsf58d'
108+
);
106109
const transactions = [
107110
createStubTxWithCertificates([
108-
{ __typename: Cardano.CertificateType.StakeRegistration } as Cardano.Certificate
111+
{
112+
__typename: Cardano.CertificateType.StakeRegistration,
113+
stakeCredential: {
114+
hash: Crypto.Hash28ByteBase16.fromEd25519KeyHashHex(Cardano.RewardAccount.toHash(foreignRewardAccount))
115+
}
116+
} as Cardano.Certificate
109117
]),
110118
createStubTxWithCertificates([
111119
{ __typename: Cardano.CertificateType.PoolRetirement } as Cardano.Certificate,
112-
{ __typename: Cardano.CertificateType.StakeDelegation } as Cardano.Certificate
120+
{
121+
__typename: Cardano.CertificateType.StakeDelegation,
122+
stakeCredential: {
123+
hash: Crypto.Hash28ByteBase16.fromEd25519KeyHashHex(Cardano.RewardAccount.toHash(foreignRewardAccount))
124+
}
125+
} as Cardano.Certificate
113126
]),
114127
createStubTxWithCertificates(),
115128
createStubTxWithCertificates([
116-
{ __typename: Cardano.CertificateType.StakeDeregistration } as Cardano.Certificate
129+
{
130+
__typename: Cardano.CertificateType.StakeDeregistration,
131+
stakeCredential: {
132+
hash: Crypto.Hash28ByteBase16.fromEd25519KeyHashHex(Cardano.RewardAccount.toHash(foreignRewardAccount))
133+
}
134+
} as Cardano.Certificate
117135
])
118136
];
119137
const slotEpochCalc = jest.fn().mockReturnValueOnce(284).mockReturnValueOnce(285);

packages/wallet/test/services/DelegationTracker/RewardAccounts.test.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,6 @@ describe('RewardAccounts', () => {
217217

218218
test.each([
219219
Cardano.CertificateType.Registration,
220-
Cardano.CertificateType.StakeRegistration,
221220
Cardano.CertificateType.StakeRegistrationDelegation,
222221
Cardano.CertificateType.StakeVoteRegistrationDelegation,
223222
Cardano.CertificateType.VoteRegistrationDelegation
@@ -321,10 +320,10 @@ describe('RewardAccounts', () => {
321320
});
322321
const tracker$ = addressKeyStatuses([rewardAccount], transactions$, transactionsInFlight$);
323322
expectObservable(tracker$).toBe('abcda', {
324-
a: [Cardano.StakeKeyStatus.Unregistered],
325-
b: [Cardano.StakeKeyStatus.Registering],
326-
c: [Cardano.StakeKeyStatus.Registered],
327-
d: [Cardano.StakeKeyStatus.Unregistering]
323+
a: [{ keyStatus: Cardano.StakeKeyStatus.Unregistered }],
324+
b: [{ keyStatus: Cardano.StakeKeyStatus.Registering }],
325+
c: [{ deposit: 0n, keyStatus: Cardano.StakeKeyStatus.Registered }],
326+
d: [{ keyStatus: Cardano.StakeKeyStatus.Unregistering }]
328327
});
329328
});
330329
});

packages/wallet/test/services/DelegationTracker/transactionCertificates.test.ts

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import * as Crypto from '@cardano-sdk/crypto';
22
import { Cardano } from '@cardano-sdk/core';
33
import { createTestScheduler } from '@cardano-sdk/util-dev';
4-
import { isLastStakeKeyCertOfType, transactionsWithCertificates } from '../../../src';
4+
import { lastStakeKeyCertOfType, transactionsWithCertificates } from '../../../src';
55

66
describe('transactionCertificates', () => {
7-
test('isLastStakeKeyCertOfType', () => {
7+
test('lastStakeKeyCertOfType', () => {
88
const rewardAccount = Cardano.RewardAccount('stake_test1up7pvfq8zn4quy45r2g572290p9vf99mr9tn7r9xrgy2l2qdsf58d');
99
const stakeKeyHash = Cardano.RewardAccount.toHash(rewardAccount);
1010
const stakeCredential = {
@@ -15,7 +15,8 @@ describe('transactionCertificates', () => {
1515
const certificates = [
1616
[
1717
{
18-
__typename: Cardano.CertificateType.StakeRegistration,
18+
__typename: Cardano.CertificateType.Registration,
19+
deposit: 2_000_000n,
1920
stakeCredential
2021
} as Cardano.Certificate,
2122
{
@@ -26,19 +27,22 @@ describe('transactionCertificates', () => {
2627
[
2728
({ __typename: Cardano.CertificateType.PoolRegistration } as Cardano.Certificate,
2829
{
29-
__typename: Cardano.CertificateType.StakeDeregistration,
30+
__typename: Cardano.CertificateType.Unregistration,
31+
deposit: 2_000_000n,
3032
stakeCredential: {
3133
hash: Crypto.Hash28ByteBase16('00000000000000000000000000000000000000000000000000000000'),
3234
type: Cardano.CredentialType.KeyHash
3335
}
3436
} as Cardano.Certificate)
3537
]
3638
];
37-
expect(isLastStakeKeyCertOfType(certificates, [Cardano.CertificateType.StakeRegistration])).toBe(false);
38-
expect(isLastStakeKeyCertOfType(certificates, [Cardano.CertificateType.StakeRegistration], rewardAccount)).toBe(
39-
true
39+
expect(lastStakeKeyCertOfType(certificates, [Cardano.CertificateType.Registration])).toBeFalsy();
40+
expect(lastStakeKeyCertOfType(certificates, [Cardano.CertificateType.Registration], rewardAccount)).toEqual(
41+
expect.objectContaining({ __typename: Cardano.CertificateType.Registration, deposit: 2_000_000n })
42+
);
43+
expect(lastStakeKeyCertOfType(certificates, [Cardano.CertificateType.Unregistration])).toEqual(
44+
expect.objectContaining({ __typename: Cardano.CertificateType.Unregistration, deposit: 2_000_000n })
4045
);
41-
expect(isLastStakeKeyCertOfType(certificates, [Cardano.CertificateType.StakeDeregistration])).toBe(true);
4246
});
4347

4448
test('outgoingTransactionsWithCertificates', () => {

0 commit comments

Comments
 (0)