Skip to content

Commit

Permalink
Include token metadata for zero-balance ValueViews of delegation toke…
Browse files Browse the repository at this point in the history
…ns (#619)

* Install constants package in types package

* Customize metadata symbols at render time, rather than at block processing time

* Fix broken test

* Go back to customizing delegation token symbols in the block processor; customize delegation tokens on the fly in the staking fetcher
  • Loading branch information
jessepinho authored Mar 1, 2024
1 parent e7f1496 commit aae6660
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 17 deletions.
14 changes: 12 additions & 2 deletions apps/webapp/src/fetchers/staking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import {
AddressIndex,
IdentityKey,
} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/keys/v1/keys_pb';
import { stakingClient } from '../clients/grpc';
import { stakingClient, viewClient } from '../clients/grpc';
import { ValueView } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb';
import {
bech32IdentityKey,
customizeSymbol,
getDisplayDenomFromView,
getIdentityKeyFromValidatorInfo,
getValidatorInfo,
Expand All @@ -25,6 +26,9 @@ const isDelegationBalance = (balance: BalancesResponse, identityKey: IdentityKey
return bech32IdentityKey(identityKey) === matchGroups.bech32IdentityKey;
};

const getDelegationTokenBaseDenom = (validatorInfo: ValidatorInfo) =>
`udelegation_${bech32IdentityKey(getIdentityKeyFromValidatorInfo(validatorInfo))}`;

/**
* Given an `AddressIndex`, yields `ValueView`s of the given address's balance
* of delegation tokens. Each `ValueView` has an `extendedMetadata` property
Expand All @@ -46,9 +50,10 @@ export const getDelegationsForAccount = async function* (addressIndex: AddressIn
const validatorInfoResponses = stakingClient.validatorInfo({ showInactive: false });

for await (const validatorInfoResponse of validatorInfoResponses) {
const validatorInfo = getValidatorInfo(validatorInfoResponse);
const extendedMetadata = new Any({
typeUrl: ValidatorInfo.typeName,
value: validatorInfoResponse.validatorInfo?.toBinary(),
value: validatorInfo.toBinary(),
});

const identityKey = getValidatorInfo.pipe(getIdentityKeyFromValidatorInfo)(
Expand All @@ -66,6 +71,10 @@ export const getDelegationsForAccount = async function* (addressIndex: AddressIn

yield withValidatorInfo;
} else {
const { denomMetadata } = await viewClient.assetMetadataById({
assetId: { altBaseDenom: getDelegationTokenBaseDenom(validatorInfo) },
});

yield new ValueView({
valueView: {
case: 'knownAssetId',
Expand All @@ -74,6 +83,7 @@ export const getDelegationsForAccount = async function* (addressIndex: AddressIn
hi: 0n,
lo: 0n,
},
metadata: denomMetadata ? customizeSymbol(denomMetadata) : undefined,
extendedMetadata,
},
},
Expand Down
10 changes: 9 additions & 1 deletion apps/webapp/src/state/staking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import {
ValidatorInfo,
ValidatorInfoResponse,
} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/component/stake/v1/stake_pb';
import { ValueView } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb';
import {
Metadata,
ValueView,
} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb';
import { bech32IdentityKey, getValidatorInfoFromValueView } from '@penumbra-zone/types';
import {
AddressView,
Expand Down Expand Up @@ -159,8 +162,13 @@ vi.mock('../fetchers/balances', () => ({
),
}));

const mockViewClient = vi.hoisted(() => ({
assetMetadataById: vi.fn(() => new Metadata()),
}));

vi.mock('../clients/grpc', () => ({
stakingClient: mockStakingClient,
viewClient: mockViewClient,
}));

describe('Staking Slice', () => {
Expand Down
5 changes: 2 additions & 3 deletions packages/query/src/block-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { RootQuerier } from './root-querier';
import { sha256Hash } from '@penumbra-zone/crypto-web';
import {
BlockProcessorInterface,
customizeSymbol,
IndexedDbInterface,
ViewServerInterface,
} from '@penumbra-zone/types';
Expand All @@ -24,7 +25,6 @@ import {
TransactionInfo,
} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb';
import { backOff } from 'exponential-backoff';
import { customizeSymbol } from './customize-symbol';

interface QueryClientProps {
fullViewingKey: string;
Expand Down Expand Up @@ -250,8 +250,7 @@ export class BlockProcessor implements BlockProcessorInterface {
const metadataFromNode = await this.querier.shieldedPool.assetMetadata(assetId);

if (metadataFromNode) {
customizeSymbol(metadataFromNode);
await this.indexedDb.saveAssetsMetadata(metadataFromNode);
await this.indexedDb.saveAssetsMetadata(customizeSymbol(metadataFromNode));
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"test": "vitest run"
},
"dependencies": {
"@penumbra-zone/constants": "workspace:*",
"@types/chrome": "0.0.260",
"bech32": "^2.0.0",
"bignumber.js": "^9.1.2",
"idb": "^8.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,27 @@ describe('Customizing metadata', () => {
display:
'delegation_penumbravalid1fjuj67ayaqueqxg03d65ps5aah6m39u39qeacu3zv2cw3dzxssyq3yrcez',
});
customizeSymbol(metadata);
expect(metadata.symbol).toBe('Delegated UM (fjuj67ayaqueqxg03d65ps5aa...)');

expect(customizeSymbol(metadata).symbol).toBe('Delegated UM (fjuj67ayaqueqxg03d65ps5aa...)');
});

test('should work for unbonding token', () => {
const metadata = new Metadata({
display:
'uunbonding_epoch_29_penumbravalid1fjuj67ayaqueqxg03d65ps5aah6m39u39qeacu3zv2cw3dzxssyq3yrcez',
});
customizeSymbol(metadata);
expect(metadata.symbol).toBe('Unbonding UM, epoch 29 (fjuj67ayaqueqxg03d65ps5aa...)');

expect(customizeSymbol(metadata).symbol).toBe(
'Unbonding UM, epoch 29 (fjuj67ayaqueqxg03d65ps5aa...)',
);
});

test('should do nothing if no matches', () => {
const metadata = new Metadata({
display: 'test_usd',
symbol: 'usdc',
});
customizeSymbol(metadata);
expect(metadata.symbol).toBe('usdc');

expect(customizeSymbol(metadata).symbol).toBe('usdc');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,19 @@ export const customizeSymbol = (metadata: Metadata) => {
if (delegationMatch) {
const { id } = delegationMatch.groups as unknown as DelegationCaptureGroups;
const shortenedId = id.slice(0, DELEGATION_SYMBOL_LENGTH);
metadata.symbol = `Delegated UM (${shortenedId}...)`;
const customized = metadata.clone();
customized.symbol = `Delegated UM (${shortenedId}...)`;
return customized;
}

const unbondingMatch = assetPatterns.unbondingToken.exec(metadata.display);
if (unbondingMatch) {
const { id, epoch } = unbondingMatch.groups as unknown as UnbondingCaptureGroups;
const shortenedId = id.slice(0, UNBONDING_SYMBOL_LENGTH);
metadata.symbol = `Unbonding UM, epoch ${epoch} (${shortenedId}...)`;
const customized = metadata.clone();
customized.symbol = `Unbonding UM, epoch ${epoch} (${shortenedId}...)`;
return customized;
}

return metadata;
};
1 change: 1 addition & 0 deletions packages/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ export * from './type-predicates';
export * from './getters';
export * from './staking';
export * from './identity-key';
export * from './customize-symbol';
16 changes: 13 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit aae6660

Please sign in to comment.