Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: proxy pallet #2590

Merged
merged 4 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions src/renderer/shared/pallet/identity/storage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type ApiPromise } from '@polkadot/api';
import { zipWith } from 'lodash';
import { z } from 'zod';

import { substrateRpcPool } from '@/shared/api/substrate-helpers';
Expand Down Expand Up @@ -56,14 +57,10 @@ export const storage = {

return substrateRpcPool
.call(() => getQuery(api, 'identityOf').multi(accounts))
.then(response => {
const data = schema.parse(response);

return accounts.map((account, index) => ({ account, identity: data[index] ?? null }));
});
.then(schema.parse)
.then(response => zipWith(accounts, response, (account, identity) => ({ account, identity })));
},

// TODO implement
/**
* Usernames that an authority has granted, but that the account controller
* has not confirmed that they want it. Used primarily in cases where the
Expand Down
71 changes: 71 additions & 0 deletions src/renderer/shared/pallet/proxy/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { type ApiPromise } from '@polkadot/api';

import { pjsSchema } from '@/shared/polkadotjs-schemas';

const getPallet = (api: ApiPromise) => {
const pallet = api.consts['proxy'];
if (!pallet) {
throw new TypeError('proxy pallet not found');
}

return pallet;
};

export const consts = {
/**
* The base amount of currency needed to reserve for creating an announcement.
*
* This is held when a new storage item holding a `Balance` is created
* (typically 16 bytes).
*/
announcementDepositBase(api: ApiPromise) {
return pjsSchema.u128.parse(getPallet(api)['announcementDepositBase']);
},

/**
* The amount of currency needed per announcement made.
*
* This is held for adding an `AccountId`, `Hash` and `BlockNumber` (typically
* 68 bytes) into a pre-existing storage value.
*/
announcementDepositFactor(api: ApiPromise) {
return pjsSchema.u128.parse(getPallet(api)['announcementDepositFactor']);
},

/**
* The maximum amount of time-delayed announcements that are allowed to be
* pending.
*/
maxPending(api: ApiPromise) {
return pjsSchema.u32.parse(getPallet(api)['maxPending']);
},

/**
* The maximum amount of proxies allowed for a single account.
*/
maxProxies(api: ApiPromise) {
return pjsSchema.u32.parse(getPallet(api)['maxProxies']);
},

/**
* The base amount of currency needed to reserve for creating a proxy.
*
* This is held for an additional storage item whose value size is
* `sizeof(Balance)` bytes and whose key size is `sizeof(AccountId)` bytes.
*/
proxyDepositBase(api: ApiPromise) {
return pjsSchema.u128.parse(getPallet(api)['proxyDepositBase']);
},

/**
* The amount of currency needed per proxy added.
*
* This is held for adding 32 bytes plus an instance of `ProxyType` more into
* a pre-existing storage value. Thus, when configuring `ProxyDepositFactor`
* one should take into account `32 + proxy_type.encode().len()` bytes of
* data.
*/
proxyDepositFactor(api: ApiPromise) {
return pjsSchema.u128.parse(getPallet(api)['proxyDepositFactor']);
},
};
11 changes: 11 additions & 0 deletions src/renderer/shared/pallet/proxy/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { consts } from './consts';
import * as schema from './schema';
import { storage } from './storage';

export const proxyPallet = {
consts,
schema,
storage,
};

export type { KitchensinkRuntimeProxyType, ProxyProxyDefinition, ProxyAnnouncement } from './schema';
30 changes: 30 additions & 0 deletions src/renderer/shared/pallet/proxy/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { type z } from 'zod';

import { pjsSchema } from '@/shared/polkadotjs-schemas';

export type KitchensinkRuntimeProxyType = z.infer<typeof kitchensinkRuntimeProxyType>;
export const kitchensinkRuntimeProxyType = pjsSchema.enumType(
'Any',
'NonTransfer',
'Governance',
'Staking',
// All types below can be absent, need some research
'Auction',
'CancelProxy',
'IdentityJudgement',
'NominationPools',
);

export type ProxyProxyDefinition = z.infer<typeof proxyProxyDefinition>;
export const proxyProxyDefinition = pjsSchema.object({
delegate: pjsSchema.accountId,
delay: pjsSchema.blockHeight,
proxyType: kitchensinkRuntimeProxyType,
});

export type ProxyAnnouncement = z.infer<typeof proxyAnnouncement>;
export const proxyAnnouncement = pjsSchema.object({
real: pjsSchema.accountId,
callHash: pjsSchema.hex,
height: pjsSchema.blockHeight,
});
82 changes: 82 additions & 0 deletions src/renderer/shared/pallet/proxy/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { type ApiPromise } from '@polkadot/api';
import { zipWith } from 'lodash';

import { substrateRpcPool } from '@/shared/api/substrate-helpers';
import { type AccountId, pjsSchema } from '@/shared/polkadotjs-schemas';

import { proxyAnnouncement, proxyProxyDefinition } from './schema';

const getQuery = (api: ApiPromise, name: string) => {
const pallet = api.query['proxy'];
if (!pallet) {
throw new TypeError(`proxy pallet not found in ${api.runtimeChain.toString()} chain`);
}

const query = pallet[name];

if (!query) {
throw new TypeError(`${name} query not found`);
}

return query;
};

export const storage = {
/**
* The announcements made by the proxy (key).
*/
announcements(api: ApiPromise, accounts?: AccountId[]) {
const recordSchema = pjsSchema.tupleMap(
['announcements', pjsSchema.vec(proxyAnnouncement)],
['deposit', pjsSchema.u128],
);

if (accounts && accounts.length > 0) {
const schema = pjsSchema.vec(recordSchema);

return substrateRpcPool
.call(() => getQuery(api, 'announcements').multi(accounts))
.then(schema.parse)
.then(result => zipWith(accounts, result, (account, value) => ({ account, value })));
}

const schema = pjsSchema.vec(
pjsSchema.tupleMap(
['account', pjsSchema.storageKey(pjsSchema.accountId).transform(([account]) => account)],
['value', recordSchema],
),
);

return substrateRpcPool.call(() => getQuery(api, 'announcements').entries()).then(schema.parse);
},

/**
* The set of account proxies. Maps the account which has delegated to the
* accounts which are being delegated to, together with the amount held on
* deposit.
*/
proxies(api: ApiPromise, accounts?: AccountId[]) {
const recordSchema = pjsSchema.tupleMap(
['accounts', pjsSchema.vec(proxyProxyDefinition)],
['deposit', pjsSchema.u128],
);

if (accounts && accounts.length > 0) {
const schema = pjsSchema.vec(recordSchema);

return substrateRpcPool
.call(() => getQuery(api, 'proxies').entries())
.then(schema.parse)
.then(result => zipWith(accounts, result, (account, value) => ({ account, value })));
}

const schema = pjsSchema.vec(
pjsSchema.tupleMap(
['account', pjsSchema.storageKey(pjsSchema.accountId).transform(([accountId]) => accountId)],
['value', recordSchema],
),
);

return substrateRpcPool.call(() => getQuery(api, 'proxies').entries()).then(schema.parse);
},
};
3 changes: 2 additions & 1 deletion src/renderer/shared/pallet/referenda/storage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type ApiPromise } from '@polkadot/api';
import { zipWith } from 'lodash';

import { substrateRpcPool } from '@/shared/api/substrate-helpers';
import { polkadotjsHelpers } from '@/shared/polkadotjs-helpers';
Expand Down Expand Up @@ -46,7 +47,7 @@ export const storage = {
if (ids) {
const schemaWithIds = pjsSchema
.vec(pjsSchema.optional(referendaReferendumInfoConvictionVotingTally))
.transform(items => items.map((item, index) => ({ info: item, id: ids[index]! })));
.transform(items => zipWith(ids, items, (id, info) => ({ id, info })));
johnthecat marked this conversation as resolved.
Show resolved Hide resolved

return substrateRpcPool.call(() => getQuery(type, api, 'referendumInfoFor').multi(ids)).then(schemaWithIds.parse);
} else {
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/shared/polkadotjs-schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
bytesSchema,
bytesString,
dataStringSchema,
hexSchema,
i64Schema,
nullSchema,
perbillSchema,
Expand Down Expand Up @@ -56,6 +57,7 @@ export const pjsSchema = {
blockHeight: blockHeightSchema,
structHex: structHexSchema,
dataString: dataStringSchema,
hex: hexSchema,

object: objectSchema,
optional: optionalSchema,
Expand Down
6 changes: 4 additions & 2 deletions src/renderer/shared/polkadotjs-schemas/primitives.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Bytes, Data, Null, StorageKey, Struct, Text, bool, i64, u128, u16, u32, u64, u8 } from '@polkadot/types';
import { Bytes, Data, Null, Raw, StorageKey, Struct, Text, bool, i64, u128, u16, u32, u64, u8 } from '@polkadot/types';
import { GenericAccountId } from '@polkadot/types/generic/AccountId';
import { type Perbill, type Permill } from '@polkadot/types/interfaces';
import { BN, u8aToString } from '@polkadot/util';
import { BN, u8aToHex, u8aToString } from '@polkadot/util';
import { z } from 'zod';

import { isCorrectAccountId } from '@/shared/lib/utils';
Expand Down Expand Up @@ -56,6 +56,8 @@ export const dataStringSchema = z
.instanceof(Data)
.transform((value) => (value.isRaw ? u8aToString(value.asRaw) : value.value.toString()));

export const hexSchema = z.instanceof(Raw).transform((value) => u8aToHex(value.hash));

export type BlockHeight = z.infer<typeof blockHeightSchema>;
export const blockHeightSchema = u32Schema.describe('blockHeight').brand('blockHeight');

Expand Down
Loading