Expand/simplify example: getStorage with keys definitions (#137)

* Update dependencies

* Update examples with simplified storage

* Update dependencies

* Revert tsc location override (check)

* Update dependencies
This commit is contained in:
Jaco Greeff 2018-07-12 19:44:00 +02:00 committed by GitHub
parent 8e23cd04d7
commit b5c4bc0122
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1441 additions and 1564 deletions

View File

@ -1,12 +1,12 @@
# examples
This guide will take you through some basic examples in using the Components and API to build tools to extract information from a Substrate network client. It follows the line of a set of tutorials that will setup basic infrastructure, queies and then string these together into doing some more complex things.
This guide will take you through some basic examples in using the Components and API to build tools to extract information from a network client. Here you will setup basic infrastructure, perform storage queries (via provided components and API) and then string all these together into doing some more complex things.
## tutorials
- [Basic application scaffolding](tut-001.md) Show the setup of a new app in this framework. (Typically only needed when new top-level features are added)
- [Storage and pulling from storage](tut-002.md) Shows how to use the built-in components to show something basic from storage
- [Query storage via API](tut-003.md) Shows how to construct an API query to pull information from storage
- [Basic application scaffolding](tut-001.md) Setup a new app in using this framework. (Typically only needed when new top-level features are added)
- [Storage and pulling from storage](tut-002.md) Use the built-in components to display something basic from storage
- [Query storage via API](tut-003.md) Construct an API query to pull information from storage
- [Query storage, replace HOC](tut-004.md) Queries storage, replacing the previous use of our HOC
- [Combining proposal and intentions](tut-005.md) Using the information collected previously, combine the displays
- [Showing selected validators](tut-006.md) Expand on intentions, filtering with current validators
@ -14,6 +14,6 @@ This guide will take you through some basic examples in using the Components and
## ... and in the end ...
You will know how to use UI components and the API to render a mapping of intentions, their balanances to proposals
You will know how to use UI components and the API to render a mapping of intentions, their balances to proposals
![final](https://raw.githubusercontent.com/polkadot-js/apps/master/examples/final.png)

View File

@ -9,32 +9,29 @@ As in previous examples, we will first show the [actual component](../packages/a
```js
class Comp extends React.PureComponent<ApiProps, State> {
...
// TODO We should unsubscribe from subscriptions
componentDidMount () {
this.subscribeProposals();
}
// TODO We should unsubscribe from subscriptions
subscribeProposals () {
const { api } = this.props;
const key = createStorageKey(method)();
const transform = storageTransform(method);
api.state
.getStorage(key)
.subscribe((value) => {
.getStorage(storage.democracy.public.proposals)
.subscribe((value: Array<StorageProposal>) => {
this.setState({
proposals: (transform(value, 0) as any[])
.reduce((proposals, [propIdx, proposal, accountId]) => {
proposals: value.reduce((proposals: StateProposals, [propIdx, proposal, accountId]) => {
const address = encodeAddress(accountId);
if (!proposals[address]) {
proposals[address] = [propIdx];
proposals[address] = [propIdx.toNumber()];
} else {
proposals[address].push(propIdx);
proposals[address].push(propIdx.toNumber());
}
return proposals;
}, {})
}, {} as StateProposals)
});
});
}
@ -48,14 +45,12 @@ export default withApi(Comp);
Since we are working on a slightly lower level now, a couple of things happen.
Unlike the case where we use an HOC, we need to manually create the actual storage key (as encoded) before calling the API. the `createStorageKey(<method>)(<params>)` function does that for us. In this case the storage entry doesn't require any params (as opposed to e.g. `staking.freeBalanceOf`). This encoding takes the method, params, applys the correct prefixes and hashes the result to form a key that is known to the staorage.
We use a helper function to decode the values as it comes back - without this we would just have a raw stream of bytes as retrieved from storage. The decding take the raw stream and splits it into a mangeable Array stream (based on trhe number of items), each containing a tuple of data - in this case the `PropIndex`, `Proposal` and `AccountId`.
We use the API provided on the component props, as injected via `withApi`, to make an API query - subscribing to any updates. As any updates happen to the storage, these will be automatically fed through the API subscriptions, updating the component display.
Finally we loop through the proposals retrieved, indexing these by the actual account address - this information we finally use in the render to update the display.
The API provided on the component props is injected via `withApi`, to make an API query - subscribing to any updates. As any updates happen to the storage, these will be automatically fed through the API subscriptions, updating the component display.
# next up
In the next round, we will extend our subscriptions to also pull the information we previously got via HOC and then map [these together](tut-004.md).

View File

@ -20,14 +20,12 @@ class Comp extends React.PureComponent<ApiProps, State> {
subscribeIntentions () {
const { api } = this.props;
const key = createStorageKey(intentionsMethod)();
const transform = storageTransform(intentionsMethod);
return api.state
.getStorage(key)
.subscribe((value) => {
.getStorage(storage.staking.public.intentions)
.subscribe((intentions: StorageIntentions) => {
this.setState({
intentions: (transform(value, 0) as any[]).map(encodeAddress)
intentions: intentions.map(encodeAddress)
});
});
}
@ -37,7 +35,7 @@ class Comp extends React.PureComponent<ApiProps, State> {
# walk-through
This subscription is much simpler than what was found before, however it follows the same basic approach - query, transform - in this case the transform is the same as the one applied even for the HOC component.
This subscription is much simpler than what was found before, however it follows the same basic approach - query, transform the result.
# next steps

View File

@ -11,14 +11,12 @@ class Comp extends React.PureComponent<ApiProps, State> {
...
subscribeValidators () {
const { api } = this.props;
const key = createStorageKey(validatorsMethod)();
const transform = storageTransform(validatorsMethod);
return api.state
.getStorage(key)
.subscribe((value) => {
.getStorage(storage.session.public.validators)
.subscribe((validators: StorageValidators) => {
this.setState({
validators: (transform(value, 0) as any[]).map(encodeAddress)
validators: validators.map(encodeAddress)
});
});
}

View File

@ -11,7 +11,6 @@ class Comp extends React.PureComponent<ApiProps, State> {
...
subscribeBalances (accounts: string[]) {
const { api } = this.props;
const keyCreator = createStorageKey(balanceMethod);
const { balances, subscriptions } = this.state;
const newBalances = { ...balances };
@ -25,12 +24,12 @@ class Comp extends React.PureComponent<ApiProps, State> {
subscriptions.push(
api.state
// Here we pass a parameter to the key generator, so it points to the correct storage entry
.getStorage(keyCreator(account))
.subscribe((balance) => {
.getStorage(storage.staking.public.freeBalanceOf, account)
.subscribe((balance: BN) => {
this.setState(({ balances }: State) => {
const newBalances = { ...balances };
newBalances[account] = u8aToBn(balance, true);
newBalances[account] = balance;
return {
balances: newBalances
@ -61,10 +60,6 @@ class Comp extends React.PureComponent<ApiProps, State> {
By now the subscription approach is known. Where things do differ however is the passing of a parameter to the key encoder. Since each balance entry is denoted by the actual account, the hashing needs to take the actual account and append it to the prefix location before making a hash that the runtime understands.
For the actual storage value, we have additional formatting on the raw value. Here we are taking the `Uint8Array` input and transforming it to a `BN`. All values in the storage are stored as little-endian-encoded values, hence passing the additional `isLe` flag to the `Uint8Array` -> `BN` conversion helper.
NOTE: It would make sense for future versions of the API to be expanded so additional formatting, either here or e.g. accountId are not needed. (At this early stage however the APIs are still at a too-low level to transparently take care of the transformation - although all information is contained in the params of the key being encoded)
# actual output
![tut-007](https://raw.githubusercontent.com/polkadot-js/apps/master/examples/tut-007.png)

View File

@ -4,20 +4,21 @@
import { ApiProps } from '@polkadot/ui-react-rx/types';
import BN from 'bn.js';
import React from 'react';
import storage from '@polkadot/storage';
import createStorageKey from '@polkadot/storage/key';
import withApi from '@polkadot/ui-react-rx/with/api';
import storageTransform from '@polkadot/ui-react-rx/with/transform/storage';
import encodeAddress from '@polkadot/util-keyring/address/encode';
const method = storage.democracy.public.proposals;
type StorageProposal = [BN, any, Uint8Array];
type StateProposals = {
[index: string]: number[]
};
type State = {
proposals: {
[index: string]: number[]
}
proposals: StateProposals
};
class Comp extends React.PureComponent<ApiProps, State> {
@ -29,32 +30,29 @@ class Comp extends React.PureComponent<ApiProps, State> {
};
}
// TODO We should unsubscribe from subscriptions
componentDidMount () {
this.subscribeProposals();
}
// TODO We should unsubscribe from subscriptions
subscribeProposals () {
const { api } = this.props;
const key = createStorageKey(method)();
const transform = storageTransform(method);
api.state
.getStorage(key)
.subscribe((value) => {
.getStorage(storage.democracy.public.proposals)
.subscribe((value: Array<StorageProposal>) => {
this.setState({
proposals: (transform(value, 0) as any[])
.reduce((proposals, [propIdx, proposal, accountId]) => {
proposals: value.reduce((proposals: StateProposals, [propIdx, proposal, accountId]) => {
const address = encodeAddress(accountId);
if (!proposals[address]) {
proposals[address] = [propIdx];
proposals[address] = [propIdx.toNumber()];
} else {
proposals[address].push(propIdx);
proposals[address].push(propIdx.toNumber());
}
return proposals;
}, {})
}, {} as StateProposals)
});
});
}

View File

@ -4,23 +4,24 @@
import { ApiProps } from '@polkadot/ui-react-rx/types';
import BN from 'bn.js';
import React from 'react';
import storage from '@polkadot/storage';
import createStorageKey from '@polkadot/storage/key';
import withApi from '@polkadot/ui-react-rx/with/api';
import storageTransform from '@polkadot/ui-react-rx/with/transform/storage';
import encodeAddress from '@polkadot/util-keyring/address/encode';
const intentionsMethod = storage.staking.public.intentions;
const proposalMethod = storage.democracy.public.proposals;
type StorageProposal = [BN, any, Uint8Array];
type StorageIntentions = Array<Uint8Array>;
type StateProposals = {
[index: string]: number[]
};
type State = {
intentions: string[],
proposals: {
[index: string]: number[]
},
subscriptions: any[]
intentions: Array<string>,
proposals: StateProposals,
subscriptions: Array<any>
};
class Comp extends React.PureComponent<ApiProps, State> {
@ -45,39 +46,34 @@ class Comp extends React.PureComponent<ApiProps, State> {
subscribeIntentions () {
const { api } = this.props;
const key = createStorageKey(intentionsMethod)();
const transform = storageTransform(intentionsMethod);
return api.state
.getStorage(key)
.subscribe((value) => {
.getStorage(storage.staking.public.intentions)
.subscribe((intentions: StorageIntentions) => {
this.setState({
intentions: (transform(value, 0) as any[]).map(encodeAddress)
intentions: intentions.map(encodeAddress)
});
});
}
subscribeProposals () {
const { api } = this.props;
const key = createStorageKey(proposalMethod)();
const transform = storageTransform(proposalMethod);
return api.state
.getStorage(key)
.subscribe((value) => {
.getStorage(storage.democracy.public.proposals)
.subscribe((value: Array<StorageProposal>) => {
this.setState({
proposals: (transform(value, 0) as any[])
.reduce((proposals, [propIdx, proposal, accountId]) => {
proposals: value.reduce((proposals: StateProposals, [propIdx, proposal, accountId]) => {
const address = encodeAddress(accountId);
if (!proposals[address]) {
proposals[address] = [propIdx];
proposals[address] = [propIdx.toNumber()];
} else {
proposals[address].push(propIdx);
proposals[address].push(propIdx.toNumber());
}
return proposals;
}, {})
}, {} as StateProposals)
});
});
}

View File

@ -4,25 +4,26 @@
import { ApiProps } from '@polkadot/ui-react-rx/types';
import BN from 'bn.js';
import React from 'react';
import storage from '@polkadot/storage';
import createStorageKey from '@polkadot/storage/key';
import withApi from '@polkadot/ui-react-rx/with/api';
import storageTransform from '@polkadot/ui-react-rx/with/transform/storage';
import encodeAddress from '@polkadot/util-keyring/address/encode';
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
import Balance from '@polkadot/ui-react-rx/Balance';
const intentionsMethod = storage.staking.public.intentions;
const proposalMethod = storage.democracy.public.proposals;
type StorageProposal = [BN, any, Uint8Array];
type StorageIntentions = Array<Uint8Array>;
type StateProposals = {
[index: string]: number[]
};
type State = {
intentions: string[],
proposals: {
[index: string]: number[]
},
subscriptions: any[]
intentions: Array<string>,
proposals: StateProposals,
subscriptions: Array<any>
};
class Comp extends React.PureComponent<ApiProps, State> {
@ -47,39 +48,34 @@ class Comp extends React.PureComponent<ApiProps, State> {
subscribeIntentions () {
const { api } = this.props;
const key = createStorageKey(intentionsMethod)();
const transform = storageTransform(intentionsMethod);
return api.state
.getStorage(key)
.subscribe((value) => {
.getStorage(storage.staking.public.intentions)
.subscribe((intentions: StorageIntentions) => {
this.setState({
intentions: (transform(value, 0) as any[]).map(encodeAddress)
intentions: intentions.map(encodeAddress)
});
});
}
subscribeProposals () {
const { api } = this.props;
const key = createStorageKey(proposalMethod)();
const transform = storageTransform(proposalMethod);
return api.state
.getStorage(key)
.subscribe((value) => {
.getStorage(storage.democracy.public.proposals)
.subscribe((value: Array<StorageProposal>) => {
this.setState({
proposals: (transform(value, 0) as any[])
.reduce((proposals, [propIdx, proposal, accountId]) => {
proposals: value.reduce((proposals: StateProposals, [propIdx, proposal, accountId]) => {
const address = encodeAddress(accountId);
if (!proposals[address]) {
proposals[address] = [propIdx];
proposals[address] = [propIdx.toNumber()];
} else {
proposals[address].push(propIdx);
proposals[address].push(propIdx.toNumber());
}
return proposals;
}, {})
}, {} as StateProposals)
});
});
}

View File

@ -4,27 +4,28 @@
import { ApiProps } from '@polkadot/ui-react-rx/types';
import BN from 'bn.js';
import React from 'react';
import storage from '@polkadot/storage';
import createStorageKey from '@polkadot/storage/key';
import withApi from '@polkadot/ui-react-rx/with/api';
import storageTransform from '@polkadot/ui-react-rx/with/transform/storage';
import encodeAddress from '@polkadot/util-keyring/address/encode';
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
import Balance from '@polkadot/ui-react-rx/Balance';
const intentionsMethod = storage.staking.public.intentions;
const proposalMethod = storage.democracy.public.proposals;
const validatorsMethod = storage.session.public.validators;
type StorageProposal = [BN, any, Uint8Array];
type StorageIntentions = Array<Uint8Array>;
type StorageValidators = Array<Uint8Array>;
type StateProposals = {
[index: string]: number[]
};
type State = {
intentions: string[],
proposals: {
[index: string]: number[]
},
subscriptions: any[],
validators: string[]
intentions: Array<string>,
proposals: StateProposals,
subscriptions: Array<any>,
validators: Array<string>
};
class Comp extends React.PureComponent<ApiProps, State> {
@ -51,53 +52,46 @@ class Comp extends React.PureComponent<ApiProps, State> {
subscribeIntentions () {
const { api } = this.props;
const key = createStorageKey(intentionsMethod)();
const transform = storageTransform(intentionsMethod);
return api.state
.getStorage(key)
.subscribe((value) => {
.getStorage(storage.staking.public.intentions)
.subscribe((intentions: StorageIntentions) => {
this.setState({
intentions: (transform(value, 0) as any[]).map(encodeAddress)
intentions: intentions.map(encodeAddress)
});
});
}
subscribeProposals () {
const { api } = this.props;
const key = createStorageKey(proposalMethod)();
const transform = storageTransform(proposalMethod);
return api.state
.getStorage(key)
.subscribe((value) => {
.getStorage(storage.democracy.public.proposals)
.subscribe((value: Array<StorageProposal>) => {
this.setState({
proposals: (transform(value, 0) as any[])
.reduce((proposals, [propIdx, proposal, accountId]) => {
proposals: value.reduce((proposals: StateProposals, [propIdx, proposal, accountId]) => {
const address = encodeAddress(accountId);
if (!proposals[address]) {
proposals[address] = [propIdx];
proposals[address] = [propIdx.toNumber()];
} else {
proposals[address].push(propIdx);
proposals[address].push(propIdx.toNumber());
}
return proposals;
}, {})
}, {} as StateProposals)
});
});
}
subscribeValidators () {
const { api } = this.props;
const key = createStorageKey(validatorsMethod)();
const transform = storageTransform(validatorsMethod);
return api.state
.getStorage(key)
.subscribe((value) => {
.getStorage(storage.session.public.validators)
.subscribe((validators: StorageValidators) => {
this.setState({
validators: (transform(value, 0) as any[]).map(encodeAddress)
validators: validators.map(encodeAddress)
});
});
}

View File

@ -8,32 +8,32 @@ import BN from 'bn.js';
import React from 'react';
import storage from '@polkadot/storage';
import createStorageKey from '@polkadot/storage/key';
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
import withApi from '@polkadot/ui-react-rx/with/api';
import storageTransform from '@polkadot/ui-react-rx/with/transform/storage';
import encodeAddress from '@polkadot/util-keyring/address/encode';
import u8aToBn from '@polkadot/util/u8a/toBn';
const balanceMethod = storage.staking.public.freeBalanceOf;
const intentionsMethod = storage.staking.public.intentions;
const proposalMethod = storage.democracy.public.proposals;
const validatorsMethod = storage.session.public.validators;
type StorageProposal = [BN, any, Uint8Array];
type StorageIntentions = Array<Uint8Array>;
type StorageValidators = Array<Uint8Array>;
const ZERO = new BN(0);
type StateBalances = {
[index: string]: BN
};
type StateProposals = {
[index: string]: number[]
};
type State = {
balances: {
[index: string]: BN
},
intentions: string[],
proposals: {
[index: string]: number[]
},
subscriptions: any[],
validators: string[]
balances: StateBalances,
intentions: Array<string>,
proposals: StateProposals,
subscriptions: Array<any>,
validators: Array<string>
};
const ZERO = new BN(0);
class Comp extends React.PureComponent<ApiProps, State> {
constructor (props: ApiProps) {
super(props);
@ -59,13 +59,11 @@ class Comp extends React.PureComponent<ApiProps, State> {
subscribeIntentions () {
const { api } = this.props;
const key = createStorageKey(intentionsMethod)();
const transform = storageTransform(intentionsMethod);
return api.state
.getStorage(key)
.subscribe((value) => {
const intentions = (transform(value, 0) as any[]).map(encodeAddress);
.getStorage(storage.staking.public.intentions)
.subscribe((storage: StorageIntentions) => {
const intentions = storage.map(encodeAddress);
this.setState({ intentions }, () => {
this.subscribeBalances(intentions);
@ -75,46 +73,40 @@ class Comp extends React.PureComponent<ApiProps, State> {
subscribeProposals () {
const { api } = this.props;
const key = createStorageKey(proposalMethod)();
const transform = storageTransform(proposalMethod);
return api.state
.getStorage(key)
.subscribe((value) => {
.getStorage(storage.democracy.public.proposals)
.subscribe((value: Array<StorageProposal>) => {
this.setState({
proposals: (transform(value, 0) as any[])
.reduce((proposals, [propIdx, proposal, accountId]) => {
proposals: value.reduce((proposals: StateProposals, [propIdx, proposal, accountId]) => {
const address = encodeAddress(accountId);
if (!proposals[address]) {
proposals[address] = [propIdx];
proposals[address] = [propIdx.toNumber()];
} else {
proposals[address].push(propIdx);
proposals[address].push(propIdx.toNumber());
}
return proposals;
}, {})
}, {} as StateProposals)
});
});
}
subscribeValidators () {
const { api } = this.props;
const key = createStorageKey(validatorsMethod)();
const transform = storageTransform(validatorsMethod);
return api.state
.getStorage(key)
.subscribe((value) => {
const validators = (transform(value, 0) as any[]).map(encodeAddress);
this.setState({ validators });
.getStorage(storage.session.public.validators)
.subscribe((validators: StorageValidators) => {
this.setState({
validators: validators.map(encodeAddress)
});
});
}
subscribeBalances (accounts: string[]) {
const { api } = this.props;
const keyCreator = createStorageKey(balanceMethod);
const { balances, subscriptions } = this.state;
const newBalances = { ...balances };
@ -128,12 +120,12 @@ class Comp extends React.PureComponent<ApiProps, State> {
subscriptions.push(
api.state
// Here we pass a parameter to the key generator, so it points to the correct storage entry
.getStorage(keyCreator(account))
.subscribe((balance) => {
.getStorage(storage.staking.public.freeBalanceOf, account)
.subscribe((balance: BN) => {
this.setState(({ balances }: State) => {
const newBalances = { ...balances };
newBalances[account] = u8aToBn(balance, true);
newBalances[account] = balance;
return {
balances: newBalances

2699
yarn.lock

File diff suppressed because it is too large Load Diff