Handle storage with optional modifiers (#717)

* Handle storage with optional modifiers

* Storage docs indicate Option

* Cleanup Option logic

* Convert Optional derive to use Option

* Bump version, update changelog

* Update CHANGELOG description

* Enum.spec.js -> Enum.spec.ts

* Add isEmpty to primitive types
This commit is contained in:
Jaco Greeff
2019-02-19 13:47:36 +01:00
committed by GitHub
parent 4f17047c77
commit f807a93e87
40 changed files with 259 additions and 96 deletions
+16
View File
@@ -1,3 +1,19 @@
# 0.45.1
- Storage with option values now correctly return `Option<Type>` and is indicated as such in the documentation
```js
// old
const ll = await api.query.session.lastLengthChange();
console.log('ll', ll /* BlockNumber */); // would be 0 if not set yet
// new
const llo = await api.query.session.lastLengthChange();
console.log('llo', llo.unwrapOr('not set') /* Option<BlockNumber> */)
```
# 0.44.1
- Split primitives and types into seperate folders. This should not affect external use since the exports remain the same, however does have an impact where classes are referenced directly. e.g.
+1 -1
View File
@@ -9,5 +9,5 @@
"packages": [
"packages/*"
],
"version": "0.44.1"
"version": "0.45.0"
}
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/api-derive",
"version": "0.44.1",
"version": "0.45.0",
"description": "Common functions used across Polkadot, derived from RPC calls and storage queries.",
"main": "index.js",
"keywords": [
@@ -31,8 +31,8 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/api-derive#readme",
"dependencies": {
"@babel/runtime": "^7.3.1",
"@polkadot/api": "^0.44.1",
"@polkadot/types": "^0.44.1",
"@polkadot/api": "^0.45.0",
"@polkadot/types": "^0.45.0",
"@types/memoizee": "^0.4.2",
"memoizee": "^0.4.14"
}
@@ -6,7 +6,7 @@ import BN from 'bn.js';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ApiInterface$Rx } from '@polkadot/api/types';
import { ReferendumInfo, ReferendumIndex } from '@polkadot/types/index';
import { Option, ReferendumInfo, ReferendumIndex } from '@polkadot/types/index';
import { drr } from '../util/drr';
@@ -16,27 +16,32 @@ import { drr } from '../util/drr';
* A [[ReferendumInfo]] with an additional `index` field
*/
export class ReferendumInfoExtended extends ReferendumInfo {
private _index: ReferendumIndex;
constructor (info: ReferendumInfo, index: BN | number) {
constructor (info: any) {
super(info);
this._index = new ReferendumIndex(index);
this.set('index', new ReferendumIndex(info.index));
}
/**
* @description Convenience getter, returns the referendumIndex
*/
get index (): ReferendumIndex {
return this._index;
return this.get('index') as ReferendumIndex;
}
}
export function referendumInfo (api: ApiInterface$Rx) {
return (id: BN | number): Observable<ReferendumInfoExtended> => {
return (api.query.democracy.referendumInfoOf(id) as Observable<ReferendumInfo>)
return (index: BN | number): Observable<Option<ReferendumInfoExtended>> => {
return (api.query.democracy.referendumInfoOf(index) as Observable<Option<ReferendumInfo>>)
.pipe(
map((info) => new ReferendumInfoExtended(info, id)),
map((info) =>
new Option<ReferendumInfoExtended>(
ReferendumInfoExtended,
info.isSome
? { ...info.toJSON(), index }
: null
)
),
drr()
);
};
@@ -5,6 +5,7 @@
import BN from 'bn.js';
import { combineLatest, Observable, of } from 'rxjs';
import { ApiInterface$Rx } from '@polkadot/api/types';
import { Option } from '@polkadot/types/index';
import { drr } from '../util/drr';
import { referendumInfo, ReferendumInfoExtended } from './referendumInfo';
@@ -12,7 +13,7 @@ import { referendumInfo, ReferendumInfoExtended } from './referendumInfo';
export function referendumInfos (api: ApiInterface$Rx) {
const referendumInfoOf = referendumInfo(api);
return (ids: Array<BN | number> = []): Observable<Array<ReferendumInfoExtended>> => {
return (ids: Array<BN | number> = []): Observable<Array<Option<ReferendumInfoExtended>>> => {
return !ids || !ids.length
? of([]).pipe(drr())
: combineLatest(
@@ -5,14 +5,14 @@
import { combineLatest, Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { ApiInterface$Rx } from '@polkadot/api/types';
import { ReferendumIndex } from '@polkadot/types/index';
import { Option, ReferendumIndex } from '@polkadot/types/index';
import { drr } from '../util/drr';
import { ReferendumInfoExtended } from './referendumInfo';
import { referendumInfos } from './referendumInfos';
export function referendums (api: ApiInterface$Rx) {
return (): Observable<Array<ReferendumInfoExtended>> =>
return (): Observable<Array<Option<ReferendumInfoExtended>>> =>
(combineLatest(
api.query.democracy.nextTally(),
api.query.democracy.referendumCount()
@@ -6,7 +6,7 @@ import BN from 'bn.js';
import { combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ApiInterface$Rx } from '@polkadot/api/types';
import { BlockNumber } from '@polkadot/types/index';
import { BlockNumber, Option } from '@polkadot/types/index';
import { bestNumber } from '../chain';
import { drr } from '../util/drr';
@@ -21,7 +21,7 @@ export function sessionProgress (api: ApiInterface$Rx) {
map(
([bestNumber, sessionLength, lastLengthChange]) =>
(bestNumber || new BN(0))
.sub(lastLengthChange as BlockNumber || new BN(0))
.sub((lastLengthChange as Option<BlockNumber>).unwrapOr(new BN(0)))
.add(sessionLength as BlockNumber || new BN(1))
.mod(sessionLength as BlockNumber || new BN(1))
),
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/api",
"version": "0.44.1",
"version": "0.45.0",
"description": "Promise and RxJS wrappers around the Polkadot JS RPC",
"main": "index.js",
"keywords": [
@@ -30,12 +30,12 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/api#readme",
"dependencies": {
"@babel/runtime": "^7.3.1",
"@polkadot/api-derive": "^0.44.1",
"@polkadot/extrinsics": "^0.44.1",
"@polkadot/rpc-provider": "^0.44.1",
"@polkadot/rpc-rx": "^0.44.1",
"@polkadot/storage": "^0.44.1",
"@polkadot/types": "^0.44.1",
"@polkadot/api-derive": "^0.45.0",
"@polkadot/extrinsics": "^0.45.0",
"@polkadot/rpc-provider": "^0.45.0",
"@polkadot/rpc-rx": "^0.45.0",
"@polkadot/storage": "^0.45.0",
"@polkadot/types": "^0.45.0",
"@polkadot/util-crypto": "^0.34.20"
}
}
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/rpc-core",
"version": "0.44.1",
"version": "0.45.0",
"description": "A JavaScript wrapper for the Polkadot JsonRPC interface",
"main": "index.js",
"keywords": [
@@ -30,9 +30,9 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/rpc-core#readme",
"dependencies": {
"@babel/runtime": "^7.3.1",
"@polkadot/jsonrpc": "^0.44.1",
"@polkadot/rpc-provider": "^0.44.1",
"@polkadot/types": "^0.44.1",
"@polkadot/jsonrpc": "^0.45.0",
"@polkadot/rpc-provider": "^0.45.0",
"@polkadot/types": "^0.45.0",
"@polkadot/util": "^0.34.20"
}
}
@@ -2,6 +2,7 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import BN from 'bn.js';
import storage from '@polkadot/storage/static';
import Api from './index';
@@ -13,8 +14,8 @@ const ENC_ONE = '0x24988a82b3bbaae58680bc9de5dea5aa';
const ENC_TWO = '0xdb190750267d8849c35316ec9d342111';
describe('formatting', () => {
let api;
let provider;
let api: Api;
let provider: any;
beforeEach(() => {
provider = {
@@ -51,30 +52,29 @@ describe('formatting', () => {
});
it('encodes multiple keys, decoding multiple results', (done) => {
api.state
.subscribeStorage(
[
[storage.balances.freeBalance, ADDR_ONE],
[storage.balances.freeBalance, ADDR_TWO]
],
(value) => {
console.error(value);
return api.state.subscribeStorage(
[
[storage.balances.freeBalance, ADDR_ONE],
[storage.balances.freeBalance, ADDR_TWO]
],
(value: any) => {
console.error(value);
expect(
provider.subscribe
).toHaveBeenCalledWith(
'state_storage',
'state_subscribeStorage',
[[ENC_ONE, ENC_TWO]],
expect.anything()
);
expect(
value.map((balance) =>
balance.toNumber()
)
).toEqual([513, 258]);
expect(
provider.subscribe
).toHaveBeenCalledWith(
'state_storage',
'state_subscribeStorage',
[[ENC_ONE, ENC_TWO]],
expect.anything()
);
expect(
value.map((balance: BN) =>
balance.toNumber()
)
).toEqual([513, 258]);
done();
});
done();
});
});
});
@@ -5,13 +5,13 @@
import Api from './index';
describe('Api', () => {
let api;
let provider;
let sendSpy;
let api: Api;
let provider: any;
let sendSpy: any;
beforeEach(() => {
provider = {
send: (method, params) => {
send: (method: any, params: Array<any>) => {
return Promise.resolve(params[0]);
}
};
@@ -25,7 +25,7 @@ describe('Api', () => {
it('requires a provider with a send method', () => {
expect(
() => new Api({})
() => new Api({} as any)
).toThrow(/Expected Provider/);
});
+23 -13
View File
@@ -9,8 +9,8 @@ import { RpcInterface, RpcInterface$Method, RpcInterface$Section } from './types
import interfaces from '@polkadot/jsonrpc/index';
import WsProvider from '@polkadot/rpc-provider/ws';
import { Codec } from '@polkadot/types/types';
import { StorageChangeSet, StorageKey, Vector, createType } from '@polkadot/types/index';
import { ExtError, assert, isFunction, logger } from '@polkadot/util';
import { Option, StorageChangeSet, StorageKey, Vector, createClass, createType } from '@polkadot/types/index';
import { ExtError, assert, isFunction, isNull, logger } from '@polkadot/util';
const l = logger('rpc-core');
@@ -189,24 +189,32 @@ export default class Rpc implements RpcInterface {
if (method.type === 'StorageData') {
// single return value (via state.getStorage), decode the value based on the
// outputType that we have specified. Fallback to Data on nothing
const type = (params[0] as StorageKey).outputType || 'Data';
const key = params[0] as StorageKey;
const type = key.outputType || 'Data';
const meta = key.meta || { default: undefined, modifier: { isOptional: true } };
const value = isNull(result)
? null
: base;
return createType(type, base);
return meta.modifier.isOptional
? new Option(createClass(type), value)
: createType(type, value);
} else if (method.type === 'StorageChangeSet') {
// multiple return values (via state.storage subscription), decode the values
// one at a time, all based on the query types. Three values can be returned -
// - Base - There is a valid value, non-empty
// - null - The storage key is empty (but in the resultset)
// - undefined - The storage value is not in the resultset
return (params[0] as Vector<StorageKey>).reduce((result, _key: StorageKey) => {
return (params[0] as Vector<StorageKey>).reduce((result, key: StorageKey) => {
// Fallback to Data (i.e. just the encoding) if we don't have a specific type
const type = _key.outputType || 'Data';
const type = key.outputType || 'Data';
// see if we have a result value for this specific key
const key = _key.toHex();
const hexKey = key.toHex();
const item = (base as StorageChangeSet).changes.find((item) =>
item.key.toHex() === key
item.key.toHex() === hexKey
);
const meta = key.meta || { default: undefined, modifier: { isOptional: true } };
// if we don't have a value, do not fill in the entry, it will be up to the
// caller to sort this out, either ignoring or having a cache for older values
@@ -214,11 +222,13 @@ export default class Rpc implements RpcInterface {
!item
? undefined
: (
// for `null` we fallback to the default value, or create an empty type,
// otherwise we return the actual value as retrieved
item.value.isNone
? createType(type, (_key.meta || { default: undefined }).default)
: createType(type, item.value.unwrap())
meta.modifier.isOptional
// create option either with the existing value, or empty when
// there is no value returned
? new Option(createClass(type), item.value.unwrapOr(null))
// for `null` we fallback to the default value, or create an empty type,
// otherwise we return the actual value as retrieved
: createType(type, item.value.unwrapOr(meta.default))
)
);
@@ -5,9 +5,9 @@
import Api from './index';
describe('methodSend', () => {
let api;
let methods;
let provider;
let api: Api;
let methods: any;
let provider: any;
beforeEach(() => {
methods = {
@@ -37,6 +37,7 @@ describe('methodSend', () => {
});
it('wraps errors with the call signature', () => {
// @ts-ignore private method
const method = api.createMethodSend(methods.blah);
return method().catch((error) => {
@@ -45,6 +46,7 @@ describe('methodSend', () => {
});
it('checks for mismatched parameters', () => {
// @ts-ignore private method
const method = api.createMethodSend(methods.bleh);
return method(1).catch((error) => {
@@ -53,6 +55,7 @@ describe('methodSend', () => {
});
it('calls the provider with the correct parameters', () => {
// @ts-ignore private method
const method = api.createMethodSend(methods.blah);
// Args are length-prefixed, because it's a Bytes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/rpc-provider",
"version": "0.44.1",
"version": "0.45.0",
"description": "Transport providers for the API",
"main": "index.js",
"keywords": [
@@ -31,7 +31,7 @@
"dependencies": {
"@babel/runtime": "^7.3.1",
"@polkadot/keyring": "^0.34.20",
"@polkadot/storage": "^0.44.1",
"@polkadot/storage": "^0.45.0",
"@polkadot/util": "^0.34.20",
"@polkadot/util-crypto": "^0.34.20",
"@types/nock": "^9.3.1",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/rpc-rx",
"version": "0.44.1",
"version": "0.45.0",
"description": "An RxJs wrapper around the Polkadot JS API",
"main": "index.js",
"keywords": [
@@ -30,8 +30,8 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/rpc-rx#readme",
"dependencies": {
"@babel/runtime": "^7.3.1",
"@polkadot/rpc-core": "^0.44.1",
"@polkadot/rpc-provider": "^0.44.1",
"@polkadot/rpc-core": "^0.45.0",
"@polkadot/rpc-provider": "^0.45.0",
"@types/memoizee": "^0.4.2",
"@types/rx": "^4.1.1",
"memoizee": "^0.4.14",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/extrinsics",
"version": "0.44.1",
"version": "0.45.0",
"main": "index.js",
"repository": "https://github.com/polkadot-js/api.git",
"author": "Jaco Greeff <jacogr@gmail.com>",
@@ -11,7 +11,7 @@
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.3.1",
"@polkadot/types": "^0.44.1",
"@polkadot/types": "^0.45.0",
"@polkadot/util": "^0.34.20"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/jsonrpc",
"version": "0.44.1",
"version": "0.45.0",
"description": "Method definitions for the Polkadot RPC layer",
"main": "index.js",
"engines": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/storage",
"version": "0.44.1",
"version": "0.45.0",
"description": "Generic wrapper wrapper around state storage",
"main": "index.js",
"engines": {
@@ -31,7 +31,7 @@
"dependencies": {
"@babel/runtime": "^7.3.1",
"@polkadot/keyring": "^0.34.20",
"@polkadot/types": "^0.44.1",
"@polkadot/types": "^0.45.0",
"@polkadot/util": "^0.34.20",
"@polkadot/util-crypto": "^0.34.20"
}
+1 -1
View File
@@ -20,7 +20,7 @@ const createRuntimeFunction = (method: string, key: string, { documentation, typ
new Text(method),
{
documentation: new Vector(Text, [documentation]),
modifier: new StorageFunctionModifier(0),
modifier: new StorageFunctionModifier(1), // default
type: new StorageFunctionType(type, 0),
toJSON: (): any =>
key
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/types",
"version": "0.44.1",
"version": "0.45.0",
"description": "Implementation of the Parity codec",
"main": "index.js",
"keywords": [
+8 -1
View File
@@ -124,7 +124,14 @@ export class ModuleMetadata extends Struct {
export class StorageFunctionModifier extends Enum {
constructor (value?: any) {
super(['None', 'Default', 'Required'], value);
super(['Optional', 'Default', 'Required'], value);
}
/**
* @description `true` if the storage entry is optional
*/
get isOptional (): boolean {
return this.toNumber() === 0;
}
}
+2 -2
View File
@@ -16,10 +16,10 @@ function storageV0 (mod: MetadataModuleV1): StorageMetadata | null {
return new StorageMetadata({
prefix: mod.prefix,
functions: mod.storage.unwrap().map(({ docs, fallback, name, type }) =>
functions: mod.storage.unwrap().map(({ docs, fallback, modifier, name, type }) =>
new StorageFunctionMetadata({
name,
modifier: 0, // unused, don't specify
modifier: modifier.toNumber(),
type,
default: fallback,
documentation: docs
+7
View File
@@ -65,6 +65,13 @@ export default abstract class AbstractInt extends BN implements Codec {
return this._bitLength / 8;
}
/**
* @description Checks if the value is a zero value (align elsewhere)
*/
get isEmpty (): boolean {
return this.isZero();
}
/**
* @description Returns the number of bits in the value
*/
+7
View File
@@ -73,6 +73,13 @@ export default class Compact extends Base<UInt | Moment> implements Codec {
return this.toU8a().length;
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
return this.raw.isEmpty;
}
/**
* @description Returns the number of bits in the value
*/
@@ -4,15 +4,16 @@
import Enum from './Enum';
const testDecode = (type, input, expected) =>
const testDecode = (type: string, input: any, expected: any) =>
it(`can decode from ${type}`, () => {
const e = new Enum(['foo', 'bar'], input);
expect(e.toString()).toBe(expected);
});
const testEncode = (to, expected) =>
const testEncode = (to: 'toJSON' | 'toNumber' | 'toString' | 'toU8a', expected: any) =>
it(`can encode ${to}`, () => {
const e = new Enum(['foo', 'bar'], 1);
expect(e[to]()).toEqual(expected);
});
+7
View File
@@ -65,6 +65,13 @@ export default class Enum extends Base<number> implements Codec {
return 1;
}
/**
* @description Checks if the value is an empty value (always false)
*/
get isEmpty (): boolean {
return false;
}
/**
* @description Compares the value of the input to see if there is a match
*/
+7
View File
@@ -122,6 +122,13 @@ export default class EnumType<T> extends Base<Codec> implements Codec {
return this._index;
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
return this.isEmpty;
}
/**
* @description Checks if the Enum points to a [[Null]] type
*/
+7
View File
@@ -53,6 +53,13 @@ export default class Option<T extends Codec> extends Base<T> implements Codec {
return 1 + this.raw.encodedLength;
}
/**
* @description Checks if the Option has no value
*/
get isEmpty (): boolean {
return this.isNone;
}
/**
* @description Checks if the Option has no value
*/
+15
View File
@@ -139,6 +139,21 @@ export default class Struct<
};
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
const items = this.toArray();
for (let i = 0; i < items.length; i++) {
if (!items[i].isEmpty) {
return false;
}
}
return true;
}
/**
* @description Returns the Type description to sthe structure
*/
+7
View File
@@ -62,6 +62,13 @@ export default class Tuple extends Array<Codec> implements Codec {
}, 0);
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
return this.length === 0;
}
/**
* @description The types definition of the tuple
*/
+10
View File
@@ -53,6 +53,16 @@ export default class Vector<
};
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
return this.length === 0;
}
/**
* @description The type for the items
*/
get Type (): string {
return this._Type.name;
}
+1 -1
View File
@@ -22,5 +22,5 @@ export { default as U8a } from './U8a';
export { default as UInt } from './UInt';
// Type management helper functions
export { default as createType, getTypeClass, getTypeDef, TypeDef, TypeDefInfo } from './createType';
export { default as createType, createClass, getTypeClass, getTypeDef, TypeDef, TypeDefInfo } from './createType';
export { default as typeRegistry } from './typeRegistry';
+7
View File
@@ -36,6 +36,13 @@ export default class Bool extends Boolean implements Codec {
return 1;
}
/**
* @description Checks if the value is an empty value (always false)
*/
get isEmpty (): boolean {
return false;
}
/**
* @description Compares the value of the input to see if there is a match
*/
+7
View File
@@ -52,6 +52,13 @@ export default class Moment extends Date implements Codec {
return BITLENGTH / 8;
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
return this.getTime() === 0;
}
/**
* @description Compares the value of the input to see if there is a match
*/
+7
View File
@@ -18,6 +18,13 @@ export default class Null implements Codec {
return 0;
}
/**
* @description Checks if the value is an empty value (always true)
*/
get isEmpty (): boolean {
return true;
}
/**
* @description Compares the value of the input to see if there is a match
*/
+7
View File
@@ -46,6 +46,13 @@ export default class Text extends String implements Codec {
return this.toU8a().length;
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
return this.length === 0;
}
/**
* @description The length of the value
*/
+4 -1
View File
@@ -145,7 +145,10 @@ function addStorage (metadata: MetadataV0) {
const methodName = stringLowerFirst(func.name.toString());
const arg = func.type.isMap ? ('`' + func.type.asMap.key.toString() + '`') : '';
const doc = func.documentation.reduce((md, doc) => `${md} ${doc}`, '');
const renderSignature = `${md}\n▸ **${methodName}**(${arg}): ` + '`' + func.type + '`';
const type = func.modifier.isOptional
? `Option<${func.type}>`
: func.type;
const renderSignature = `${md}\n▸ **${methodName}**(${arg}): ` + '`' + type + '`';
const renderSummary = `${doc ? `\n- **summary**: ${doc}\n` : '\n'}`;
return renderSignature + renderSummary;
+9 -1
View File
@@ -6,6 +6,7 @@ import BN from 'bn.js';
import { decodeAddress } from '@polkadot/keyring';
import { hexToU8a, isBn, isHex, isNumber, isU8a, u8aConcat, u8aToHex, u8aToU8a, u8aToBn } from '@polkadot/util';
import { Codec } from '../types';
import Base from '../codec/Base';
import AccountId from './AccountId';
import AccountIndex from './AccountIndex';
@@ -22,7 +23,7 @@ export const ACCOUNT_ID_PREFIX = new Uint8Array([0xff]);
* we extend from Base with an AccountId/AccountIndex wrapper. Basically the Address
* is encoded as `[ <prefix-byte>, ...publicKey/...bytes ]` as per spec
*/
export default class Address extends Base<AccountId | AccountIndex> {
export default class Address extends Base<AccountId | AccountIndex> implements Codec {
constructor (value: AnyAddress = new Uint8Array()) {
super(
Address.decodeAddress(value)
@@ -75,6 +76,13 @@ export default class Address extends Base<AccountId | AccountIndex> {
);
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
return this.raw.isEmpty;
}
/**
* @description The length of the raw value, either AccountIndex or AccountId
*/
@@ -30,6 +30,13 @@ export default class ChainProperties extends Map<string, any> implements Codec {
return 0;
}
/**
* @description Checks if the value is an empty value
*/
get isEmpty (): boolean {
return [...this.keys()].length === 0;
}
/**
* @description The token decimals, if defined (de-facto standard only)
*/
+8 -1
View File
@@ -33,6 +33,11 @@ export interface Codec {
*/
encodedLength: number;
/**
* @description Checks if the value is an empty value
*/
isEmpty: boolean;
/**
* @description Compares the value of the input to see if there is a match
*/
@@ -62,7 +67,9 @@ export interface Codec {
export type CodecTo = 'toHex' | 'toJSON' | 'toString' | 'toU8a';
export type Constructor<T = Codec> = { new(value?: any): T };
export interface Constructor<T = Codec> {
new(...value: Array<any>): T;
}
export type ConstructorDef<T = Codec> = { [index: string]: Constructor<T> };