Vector from u8a, StorageChangeSet with gaps (#228)
This commit is contained in:
+16
-10
@@ -169,37 +169,43 @@ export default class Api implements ApiInterface {
|
||||
);
|
||||
}
|
||||
|
||||
private formatOutput (method: RpcMethod, params: Array<Base>, result?: any): Base {
|
||||
private formatOutput (method: RpcMethod, params: Array<Base>, result?: any): Base | Array<Base | undefined> {
|
||||
const base = createType(method.type as string).fromJSON(result);
|
||||
|
||||
if (method.type === 'StorageData') {
|
||||
// single return value (via state.getStorage), decode the value based on the
|
||||
// outputType that we have specified
|
||||
const type = (params[0] as StorageKey).outputType;
|
||||
|
||||
if (type) {
|
||||
return createType(type, base.raw);
|
||||
}
|
||||
} else if (method.type === 'StorageChangeSet') {
|
||||
return (params[0] as Vector<StorageKey>).reduce((vector, _key: StorageKey) => {
|
||||
// multiple return values (via state.storage subscription), decode the values
|
||||
// one at a time, all based on the query types
|
||||
return (params[0] as Vector<StorageKey>).reduce((result, _key: StorageKey) => {
|
||||
const type = _key.outputType;
|
||||
|
||||
if (!type) {
|
||||
throw new Error('Cannot format StorageChangeSet, output type missing for key');
|
||||
}
|
||||
|
||||
// see if we have a result value for this specific key
|
||||
const key = _key.toHex();
|
||||
|
||||
const item = (base as StorageChangeSet).changes.find((item) =>
|
||||
item.key.toHex() === key
|
||||
);
|
||||
|
||||
if (!item) {
|
||||
throw new Error('No result found for key in StorageChangeSet');
|
||||
}
|
||||
// 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
|
||||
result.push(
|
||||
item
|
||||
? createType(type, item.value.value)
|
||||
: undefined
|
||||
);
|
||||
|
||||
vector.push(createType(type, item.value.value));
|
||||
|
||||
return vector;
|
||||
}, new (Vector.with(Base))());
|
||||
return result;
|
||||
}, [] as Array<Base | undefined>);
|
||||
}
|
||||
|
||||
return base;
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import extrinsics from '@polkadot/extrinsics/static';
|
||||
|
||||
import createType from './createType';
|
||||
import Method from '../Method';
|
||||
import Text from '../Text';
|
||||
import Vector from './Vector';
|
||||
|
||||
@@ -10,6 +14,8 @@ describe('Vector', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
array = new Vector(Text, [ '1', '23', '345', '4567', new Text('56789') ]);
|
||||
|
||||
Method.injectExtrinsics(extrinsics);
|
||||
});
|
||||
|
||||
it('wraps a sequence of values', () => {
|
||||
@@ -41,6 +47,17 @@ describe('Vector', () => {
|
||||
expect(array.Type).toEqual('Text');
|
||||
});
|
||||
|
||||
it('decodes a complex type via construction', () => {
|
||||
const test = createType('Vec<(PropIndex, Proposal, AccountId)>', new Uint8Array([
|
||||
4, 10, 0, 0, 0, 0, 3, 80, 123, 10, 9, 34, 48, 120, 52, 50, 34, 58, 32, 34, 48, 120, 52, 51, 34, 10, 125, 10, 209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79
|
||||
]));
|
||||
const first = test.get(0);
|
||||
|
||||
expect(first.get(0).toNumber()).toEqual(10);
|
||||
expect(first.get(1).callIndex).toEqual(new Uint8Array([0, 3]));
|
||||
expect(first.get(2).toString()).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ');
|
||||
});
|
||||
|
||||
describe('array-like functions', () => {
|
||||
it('allows retrieval of a specific item', () => {
|
||||
expect(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
import toU8a from '@polkadot/util/u8a/toU8a';
|
||||
|
||||
import Base from './Base';
|
||||
import Compact, { DEFAULT_LENGTH_BITS } from './Compact';
|
||||
@@ -17,18 +18,41 @@ export default class Vector <
|
||||
> extends Base<Array<T>> {
|
||||
private _Type: { new(value?: any): T };
|
||||
|
||||
constructor (Type: { new(value?: any): T }, value: Array<any> = [] as Array<any>) {
|
||||
constructor (Type: { new(value?: any): T }, value: Uint8Array | string | Array<any> = [] as Array<any>) {
|
||||
super(
|
||||
value.map((entry) =>
|
||||
entry instanceof Type
|
||||
? entry
|
||||
: new Type(entry)
|
||||
)
|
||||
Vector.decode(Type, value)
|
||||
);
|
||||
|
||||
this._Type = Type;
|
||||
}
|
||||
|
||||
static decode <T> (Type: { new(value?: any): T }, value: Uint8Array | string | Array<any>): Array<T> {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((entry) =>
|
||||
entry instanceof Type
|
||||
? entry
|
||||
: new Type(entry)
|
||||
);
|
||||
}
|
||||
|
||||
const u8a = toU8a(value);
|
||||
|
||||
let [offset, _length] = Compact.decodeU8a(value, DEFAULT_LENGTH_BITS);
|
||||
const length = _length.toNumber();
|
||||
|
||||
const result = [];
|
||||
|
||||
for (let index = 0; index < length; index++) {
|
||||
// @ts-ignore Not sure why we get "Property 'fromU8a' does not exist on type 'T'.", T extends Base in def?
|
||||
const decoded = new Type().fromU8a(u8a.subarray(offset));
|
||||
|
||||
result.push(decoded as T);
|
||||
offset += decoded.byteLength();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static with <O extends Base> (Type: { new(value?: any): O }): { new(value?: any): Vector<O> } {
|
||||
return class extends Vector<O> {
|
||||
constructor (value?: Array<any>) {
|
||||
@@ -64,7 +88,8 @@ export default class Vector <
|
||||
}
|
||||
|
||||
fromJSON (input: any): Vector<T> {
|
||||
this.raw = input.map((input: any) =>
|
||||
// input could be null/undefined to indicate empty
|
||||
this.raw = (input || []).map((input: any) =>
|
||||
new this._Type().fromJSON(input)
|
||||
);
|
||||
|
||||
@@ -72,17 +97,7 @@ export default class Vector <
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Vector<T> {
|
||||
let [offset, _length] = Compact.decodeU8a(input, DEFAULT_LENGTH_BITS);
|
||||
const length = _length.toNumber();
|
||||
|
||||
this.raw = [];
|
||||
|
||||
for (let index = 0; index < length; index++) {
|
||||
const raw = new this._Type().fromU8a(input.subarray(offset));
|
||||
|
||||
this.raw.push(raw as T);
|
||||
offset += raw.byteLength();
|
||||
}
|
||||
this.raw = Vector.decode(this._Type, input);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -111,6 +111,33 @@ describe('getTypeValue', () => {
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a type structure (actual)', () => {
|
||||
expect(
|
||||
getTypeDef('Vec<(PropIndex, Proposal, AccountId)>')
|
||||
).toEqual({
|
||||
info: TypeDefInfo.Vector,
|
||||
type: 'Vec<(PropIndex, Proposal, AccountId)>',
|
||||
sub: {
|
||||
info: TypeDefInfo.Tuple,
|
||||
type: '(PropIndex, Proposal, AccountId)',
|
||||
sub: [
|
||||
{
|
||||
info: TypeDefInfo.Plain,
|
||||
type: 'PropIndex'
|
||||
},
|
||||
{
|
||||
info: TypeDefInfo.Plain,
|
||||
type: 'Proposal'
|
||||
},
|
||||
{
|
||||
info: TypeDefInfo.Plain,
|
||||
type: 'AccountId'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTypeClass', () => {
|
||||
|
||||
Reference in New Issue
Block a user