Skip to content

Commit c1562db

Browse files
committed
[Issue-4257] Init delegate
1 parent 1ce8c07 commit c1562db

File tree

23 files changed

+1443
-410
lines changed

23 files changed

+1443
-410
lines changed

packages/extension-base/src/background/KoniTypes.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import { SignerResult } from '@polkadot/types/types/extrinsic';
3737
import { HexString } from '@polkadot/util/types';
3838

3939
import { EarningSlippageResult } from '../services/earning-service/handlers/native-staking/dtao';
40-
import { _ReferendumInfo, StandardVoteRequest } from '../services/open-gov/type';
40+
import { _DelegateInfo, _ReferendumInfo, DelegateRequest, RemoveVoteRequest, StandardVoteRequest } from '../services/open-gov/type';
4141
import { TransactionWarning } from './warnings/TransactionWarning';
4242

4343
export enum RuntimeEnvironment {
@@ -542,6 +542,7 @@ export enum ExtrinsicType {
542542

543543
// SET_FEE_TOKEN = 'set_fee-token',
544544
VOTE = 'gov.vote',
545+
DELEGATE = 'gov.delegate',
545546

546547
EVM_EXECUTE = 'evm.execute',
547548
UNKNOWN = 'unknown'
@@ -599,6 +600,7 @@ export interface ExtrinsicDataTypeMap {
599600
[ExtrinsicType.EVM_EXECUTE]: TransactionConfig,
600601
[ExtrinsicType.CROWDLOAN]: any,
601602
[ExtrinsicType.VOTE]: any, // TODO: avoid any
603+
[ExtrinsicType.DELEGATE]: any, // TODO: avoid any
602604
[ExtrinsicType.SWAP]: SwapTxData
603605
[ExtrinsicType.UNKNOWN]: any
604606
}
@@ -2457,6 +2459,10 @@ export interface KoniRequestSignatures {
24572459
/* Gov */
24582460
'pri(openGov.fetchReferendums)': [string, _ReferendumInfo[]];
24592461
'pri(openGov.standardVote)': [StandardVoteRequest, SWTransactionResponse];
2462+
'pri(openGov.removeVote)': [RemoveVoteRequest, SWTransactionResponse];
2463+
2464+
'pri(openGov.fetchDelegates)': [string, _DelegateInfo[]];
2465+
'pri(openGov.delegate)': [DelegateRequest, SWTransactionResponse];
24602466
}
24612467

24622468
export interface ApplicationMetadataType {

packages/extension-base/src/koni/background/handlers/Extension.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import { calculateToAmountByReservePool } from '@subwallet/extension-base/servic
4848
import { batchExtrinsicSetFeeHydration, getAssetHubTokensCanPayFee, getHydrationTokensCanPayFee } from '@subwallet/extension-base/services/fee-service/utils/tokenPayFee';
4949
import { ClaimPolygonBridgeNotificationMetadata, NotificationSetup } from '@subwallet/extension-base/services/inapp-notification-service/interfaces';
5050
import { AppBannerData, AppConfirmationData, AppPopupData } from '@subwallet/extension-base/services/mkt-campaign-service/types';
51-
import { _ReferendumInfo, StandardVoteRequest } from '@subwallet/extension-base/services/open-gov/type';
51+
import { _DelegateInfo, _ReferendumInfo, DelegateRequest, RemoveVoteRequest, StandardVoteRequest } from '@subwallet/extension-base/services/open-gov/type';
5252
import { EXTENSION_REQUEST_URL } from '@subwallet/extension-base/services/request-service/constants';
5353
import { AuthUrls } from '@subwallet/extension-base/services/request-service/types';
5454
import { DEFAULT_AUTO_LOCK_TIME } from '@subwallet/extension-base/services/setting-service/constants';
@@ -4708,6 +4708,38 @@ export default class KoniExtension {
47084708
chainType: ChainType.SUBSTRATE
47094709
});
47104710
}
4711+
4712+
private async handleRemoveVote (request: RemoveVoteRequest): Promise<SWTransactionResponse> {
4713+
const extrinsic = await this.#koniState.openGovService.handleRemoveVote(request);
4714+
4715+
return await this.#koniState.transactionService.handleTransaction({
4716+
address: request.address,
4717+
chain: request.chain,
4718+
transaction: extrinsic,
4719+
data: request,
4720+
extrinsicType: ExtrinsicType.VOTE,
4721+
chainType: ChainType.SUBSTRATE
4722+
});
4723+
}
4724+
4725+
private async fetchDelegates (request: string): Promise<_DelegateInfo[]> {
4726+
const data = await this.#koniState.openGovService.fetchDelegates(request);
4727+
4728+
return data;
4729+
}
4730+
4731+
private async handleDelegate (request: DelegateRequest): Promise<SWTransactionResponse> {
4732+
const extrinsic = await this.#koniState.openGovService.handleDelegate(request);
4733+
4734+
return await this.#koniState.transactionService.handleTransaction({
4735+
address: request.userAddress,
4736+
chain: request.chain,
4737+
transaction: extrinsic,
4738+
data: request,
4739+
extrinsicType: ExtrinsicType.DELEGATE,
4740+
chainType: ChainType.SUBSTRATE
4741+
});
4742+
}
47114743
/* Gov */
47124744

47134745
// --------------------------------------------------------------
@@ -5369,6 +5401,12 @@ export default class KoniExtension {
53695401
return this.fetchReferendums(request as string);
53705402
case 'pri(openGov.standardVote)':
53715403
return this.handleStandardVote(request as StandardVoteRequest);
5404+
case 'pri(openGov.removeVote)':
5405+
return this.handleRemoveVote(request as RemoveVoteRequest);
5406+
case 'pri(openGov.fetchDelegates)':
5407+
return this.fetchDelegates(request as string);
5408+
case 'pri(openGov.delegate)':
5409+
return this.handleDelegate(request as DelegateRequest);
53725410
/* Gov */
53735411

53745412
// Default

packages/extension-base/src/services/earning-service/service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,8 @@ export default class EarningService implements StoppableServiceInterface, Persis
211211
ExtrinsicType.TRANSFER_XCM,
212212
ExtrinsicType.SEND_NFT,
213213
ExtrinsicType.CROWDLOAN,
214-
ExtrinsicType.VOTE
214+
ExtrinsicType.VOTE,
215+
ExtrinsicType.DELEGATE
215216
];
216217

217218
if (notRequireReloadTypes.indexOf(transactionData.extrinsicType) === -1) {

packages/extension-base/src/services/open-gov/index.ts

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,46 @@
11
// Copyright 2019-2022 @subwallet/extension-base
22
// SPDX-License-Identifier: Apache-2.0
33

4+
import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
45
import KoniState from '@subwallet/extension-base/koni/background/handlers/State';
5-
import { TransactionData } from '@subwallet/extension-base/types';
6+
import { BasicTxErrorType, TransactionData } from '@subwallet/extension-base/types';
67

7-
import { _ReferendumInfo, numberToConviction, RemoveVoteRequest, SplitAbstainVoteRequest, StandardVoteRequest } from './type';
8+
import { _DelegateInfo, _ReferendumInfo, DelegateRequest, numberToConviction, RemoveVoteRequest, SplitAbstainVoteRequest, StandardVoteRequest, UnlockVoteRequest } from './type';
89

910
interface Referendums {
1011
items: _ReferendumInfo[];
1112
}
1213

14+
interface Delegates{
15+
items: _DelegateInfo[];
16+
}
1317
export default class OpenGovService {
1418
protected readonly state: KoniState;
1519

1620
constructor (state: KoniState) {
1721
this.state = state;
1822
}
1923

20-
public async fetchReferendums (request: string): Promise<_ReferendumInfo[]> {
21-
const url = `https://${request}.subsquare.io/api/gov2/referendums?page=1&page_size=100`;
24+
public async fetchReferendums (chain: string): Promise<_ReferendumInfo[]> {
25+
const url = `https://${chain}.subsquare.io/api/gov2/referendums?page=1&page_size=100`;
2226

2327
const res = await fetch(url);
24-
const json = await res.json() as Referendums;
2528

26-
return json.items;
29+
if (!res.ok) {
30+
return Promise.reject(new TransactionError(BasicTxErrorType.UNSUPPORTED));
31+
}
32+
33+
const referendums = await res.json() as Referendums;
34+
35+
if (!referendums || !Array.isArray(referendums.items)) {
36+
return [];
37+
}
38+
39+
return referendums.items;
2740
}
2841

2942
public async handleStandardVote (request: StandardVoteRequest): Promise<TransactionData> {
30-
const substrateApi = this.state.getSubstrateApi(request.chain);
43+
const substrateApi = this.state.getSubstrateApi(request.chain); // Todo: refactor to get this one time
3144

3245
const api = await substrateApi.isReady;
3346

@@ -76,4 +89,52 @@ export default class OpenGovService {
7689

7790
return extrinsic;
7891
}
92+
93+
public async handleUnlockVote (request: UnlockVoteRequest): Promise<TransactionData> {
94+
const substrateApi = this.state.getSubstrateApi(request.chain);
95+
const api = await substrateApi.isReady;
96+
97+
const extrinsic = api.api.tx.convictionVoting.unlock(
98+
request.trackId,
99+
request.target
100+
);
101+
102+
return extrinsic;
103+
}
104+
105+
public async fetchDelegates (chain: string): Promise<_DelegateInfo[]> {
106+
const url = `https://${chain}.subsquare.io/api/delegation/referenda/delegates?sort=&page=1&page_size=100`;
107+
108+
const res = await fetch(url);
109+
110+
if (!res.ok) {
111+
return Promise.reject(new TransactionError(BasicTxErrorType.UNSUPPORTED));
112+
}
113+
114+
const delegates = await res.json() as Delegates;
115+
116+
if (!delegates || !Array.isArray(delegates.items)) {
117+
return [];
118+
}
119+
120+
return delegates.items;
121+
}
122+
123+
public async handleDelegate (request: DelegateRequest): Promise<TransactionData> {
124+
const substrateApi = this.state.getSubstrateApi(request.chain);
125+
const api = await substrateApi.isReady;
126+
127+
const delegateExtrinsics = request.trackIds.map((trackId) => {
128+
return api.api.tx.convictionVoting.delegate(
129+
trackId,
130+
request.delegateAddress,
131+
numberToConviction[request.conviction],
132+
request.balance
133+
);
134+
}).filter((tx) => tx !== null);
135+
136+
const batchTx = api.api.tx.utility.batch([...delegateExtrinsics]);
137+
138+
return batchTx;
139+
}
79140
}

packages/extension-base/src/services/open-gov/type.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ export interface RemoveVoteRequest {
5454
referendumIndex: number;
5555
}
5656

57+
export interface UnlockVoteRequest {
58+
address: string;
59+
chain: string;
60+
trackId: number;
61+
target: string;
62+
}
63+
5764
export interface _ReferendumInfo {
5865
_id: string;
5966
referendumIndex: number;
@@ -85,5 +92,35 @@ export interface _ReferendumInfo {
8592
info: {
8693
alarm: number[]
8794
}
95+
};
96+
97+
}
98+
99+
export interface _EnhancedReferendumInfo extends _ReferendumInfo {
100+
endTime: number;
101+
}
102+
103+
// Delegate
104+
export interface _DelegateInfo {
105+
address: string
106+
delegatorsCount: number,
107+
votes: string,
108+
manifesto?: {
109+
source: string;
110+
name: string;
111+
image: string;
112+
shortDescription: string;
113+
longDescription: string;
114+
isOrganization: boolean
88115
}
89116
}
117+
118+
export interface DelegateRequest {
119+
userAddress: string,
120+
chain: string,
121+
trackIds: number[];
122+
conviction: number;
123+
balance: string;
124+
delegateAddress: string
125+
removeOtherTracks: boolean
126+
}

packages/extension-base/src/services/transaction-service/helpers/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ const typeName = (type: SWTransaction['extrinsicType']) => {
7272
return 'Start earning';
7373
case ExtrinsicType.VOTE:
7474
return 'Vote';
75+
case ExtrinsicType.DELEGATE:
76+
return 'Delegate';
7577
case ExtrinsicType.UNKNOWN:
7678
default:
7779
return 'unknown';

packages/extension-base/src/utils/account/transform.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,8 @@ const OTHER_ACTIONS: ExtrinsicType[] = [
238238
ExtrinsicType.SEND_NFT,
239239
ExtrinsicType.SWAP,
240240
ExtrinsicType.CROWDLOAN,
241-
ExtrinsicType.VOTE
241+
ExtrinsicType.VOTE,
242+
ExtrinsicType.DELEGATE
242243
];
243244

244245
export const getAccountTransactionActions = (signMode: AccountSignMode, networkType: AccountChainType, type?: KeypairType, _meta?: KeyringPair$Meta, _specialNetwork?: string): ExtrinsicType[] => {
@@ -347,7 +348,7 @@ export const getAccountTransactionActions = (signMode: AccountSignMode, networkT
347348
const result: ExtrinsicType[] = [];
348349
const specialNetwork = _specialNetwork || '';
349350

350-
result.push(...BASE_TRANSFER_ACTIONS, ...NATIVE_STAKE_ACTIONS, ...POOL_STAKE_ACTIONS, ExtrinsicType.SWAP, ExtrinsicType.CROWDLOAN, ExtrinsicType.VOTE);
351+
result.push(...BASE_TRANSFER_ACTIONS, ...NATIVE_STAKE_ACTIONS, ...POOL_STAKE_ACTIONS, ExtrinsicType.SWAP, ExtrinsicType.CROWDLOAN, ExtrinsicType.VOTE, ExtrinsicType.DELEGATE);
351352

352353
// NFT
353354
if (!['astar', 'avail_mainnet'].includes(specialNetwork)) {

packages/extension-koni-ui/src/Popup/Confirmations/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ const Component = function ({ className }: Props) {
294294
case ExtrinsicType.CROWDLOAN:
295295
case ExtrinsicType.EVM_EXECUTE:
296296
case ExtrinsicType.VOTE:
297+
case ExtrinsicType.DELEGATE:
297298
case ExtrinsicType.UNKNOWN:
298299
return t('Transaction confirm');
299300
}

packages/extension-koni-ui/src/Popup/Home/History/index.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { cancelSubscription, subscribeTransactionHistory } from '@subwallet/exte
1313
import { SessionStorage, ThemeProps, TransactionHistoryDisplayData, TransactionHistoryDisplayItem } from '@subwallet/extension-koni-ui/types';
1414
import { customFormatDate, formatHistoryDate, isTypeStaking, isTypeTransfer } from '@subwallet/extension-koni-ui/utils';
1515
import { ButtonProps, Icon, ModalContext, SwIconProps, SwList, SwSubHeader } from '@subwallet/react-ui';
16-
import { Alien, Aperture, ArrowDownLeft, ArrowsLeftRight, ArrowUpRight, Clock, ClockCounterClockwise, Database, FadersHorizontal, Rocket, Spinner } from 'phosphor-react';
16+
import { Alien, Aperture, ArrowDownLeft, ArrowsLeftRight, ArrowUpRight, Clock, ClockCounterClockwise, Database, FadersHorizontal, FlyingSaucer, Rocket, Spinner } from 'phosphor-react';
1717
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
1818
import { useTranslation } from 'react-i18next';
1919
import { useParams } from 'react-router-dom';
@@ -34,7 +34,8 @@ const IconMap: Record<string, SwIconProps['phosphorIcon']> = {
3434
default: ClockCounterClockwise,
3535
timeout: ClockCounterClockwise,
3636
swap: ArrowsLeftRight,
37-
vote: Alien
37+
vote: Alien,
38+
delegate: FlyingSaucer
3839
};
3940

4041
function getIcon (item: TransactionHistoryItem): SwIconProps['phosphorIcon'] {
@@ -70,6 +71,10 @@ function getIcon (item: TransactionHistoryItem): SwIconProps['phosphorIcon'] {
7071
return IconMap.vote;
7172
}
7273

74+
if (item.type === ExtrinsicType.DELEGATE) {
75+
return IconMap.delegate;
76+
}
77+
7378
return IconMap.default;
7479
}
7580

@@ -323,6 +328,7 @@ function Component ({ className = '' }: Props): React.ReactElement<Props> {
323328
[ExtrinsicType.SWAP]: t('Swap'),
324329
[ExtrinsicType.CLAIM_BRIDGE]: t('Claim token'),
325330
[ExtrinsicType.VOTE]: t('Vote'),
331+
[ExtrinsicType.DELEGATE]: t('Delegate'),
326332
[ExtrinsicType.UNKNOWN]: t('Unknown')
327333
}), [t]);
328334

@@ -369,6 +375,7 @@ function Component ({ className = '' }: Props): React.ReactElement<Props> {
369375
[ExtrinsicType.SWAP]: t('Swap transaction'),
370376
[ExtrinsicType.CLAIM_BRIDGE]: t('Claim token transaction'),
371377
[ExtrinsicType.VOTE]: t('Vote transaction'),
378+
[ExtrinsicType.DELEGATE]: t('Delegate transaction'),
372379
[ExtrinsicType.UNKNOWN]: t('Unknown transaction')
373380
}), [t]);
374381

0 commit comments

Comments
 (0)