Intelligent constructor (Part 2) (#252)

* Fix AccountId

* Fix AccountIndex

* Add Bool

* Fix Address

* Small fixes

* Fix Bytes

* Fix ExtrinsicSignature

* Small fixes

* Sorta fix Method

* Fix Extrinsic

* Fix method

* Fix Bytes and KeyValue

* Fix Moment

* Length-prefix the storage key

* Fix Bytes prefix

* Add comment
This commit is contained in:
Amaury Martiny
2018-10-11 13:39:27 +02:00
committed by Jaco Greeff
parent e409be6dbd
commit f5948fee52
20 changed files with 326 additions and 229 deletions
+2 -1
View File
@@ -53,7 +53,8 @@ describe('methodSend', () => {
it('calls the provider with the correct parameters', () => {
const method = api.createMethodSend('test_blah', methods.blah);
return method(new Uint8Array([0x12, 0x34])).then(() => {
// Args are length-prefixed, because it's a Bytes
return method(new Uint8Array([2 << 2, 0x12, 0x34])).then(() => {
expect(provider.send).toHaveBeenCalledWith('test_blah', ['0x1234']);
});
});
@@ -21,9 +21,9 @@ describe('fromMetadata', () => {
});
// FIXME check again when we have a valid chain/UI
it('should return the correct storage key', () => {
it('should return the correct length-prefixed storage key', () => {
expect(newStorage.balances.freeBalance('5GwPuAgYgP6q58uWTXp4uSg6FwfzQv9HfFZwAFEREUrQjCvy')).toEqual(
Uint8Array.from([52, 103, 23, 10, 157, 95, 244, 9, 70, 215, 120, 149, 1, 238, 109, 69])
Uint8Array.from([64, 52, 103, 23, 10, 157, 95, 244, 9, 70, 215, 120, 149, 1, 238, 109, 69])
);
});
});
@@ -2,6 +2,7 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import Compact, { DEFAULT_LENGTH_BITS } from '@polkadot/types/codec/Compact';
import { createType } from '@polkadot/types/codec';
import { StorageFunctionMetadata } from '@polkadot/types/Metadata';
import { StorageFunction } from '@polkadot/types/StorageKey';
@@ -59,13 +60,19 @@ export default function createFunction (
const type = meta.type.asMap.key.toString(); // Argument type, as string
return xxhash(
const methodAndArgs = xxhash(
u8aConcat(
u8aFromUtf8(`${section.toString()} ${method.toString()}`),
createType(type, arg).toU8a(true)
),
128
);
// StorageKey is a Bytes, so is length-prefixed
return u8aConcat(
Compact.encodeU8a(methodAndArgs.length, DEFAULT_LENGTH_BITS),
methodAndArgs
);
};
}
+45 -44
View File
@@ -3,52 +3,53 @@
// of the ISC license. See the LICENSE file for details.
import AccountId from './AccountId';
import U8a from './codec/U8a';
const testDecode = (type, input, expected) =>
it(`can decode from ${type}`, () => {
const a = new AccountId(input);
expect(a.toString()).toBe(expected);
});
const testEncode = (to, expected) =>
it(`can encode ${to}`, () => {
const a = new AccountId('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
expect(a[to]()).toEqual(expected);
});
describe('AccountId', () => {
it('decodes from u8a', () => {
expect(
new AccountId()
.fromU8a(
new Uint8Array([
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
])
)
.toString()
).toEqual(
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
});
it('decodes from address', () => {
expect(
new AccountId()
.fromJSON('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s')
.toHex()
).toEqual(
'0x0102030405060708010203040506070801020304050607080102030405060708'
);
});
testDecode(
'AccountId',
new AccountId('0x0102030405060708010203040506070801020304050607080102030405060708'),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode('hex', '0x0102030405060708010203040506070801020304050607080102030405060708', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
testDecode('string', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
testDecode(
'U8a',
new U8a([
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
]),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'Uint8Array',
Uint8Array.from([
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
]),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
it('decodes from hex', () => {
expect(
new AccountId()
.fromJSON('0x0102030405060708010203040506070801020304050607080102030405060708')
.toJSON()
).toEqual(
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
});
it('allows creation from another', () => {
expect(
new AccountId(
new AccountId(
'0x0102030405060708010203040506070801020304050607080102030405060708'
)
).toString()
).toEqual(
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
});
testEncode('toHex', '0x0102030405060708010203040506070801020304050607080102030405060708');
testEncode('toJSON', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
testEncode('toString', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
testEncode('toU8a', Uint8Array.from([
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
]));
});
+6 -6
View File
@@ -20,9 +20,7 @@ import U8aFixed from './codec/U8aFixed';
export default class AccountId extends U8aFixed {
constructor (value: AnyU8a = new Uint8Array()) {
super(
value instanceof U8a
? value.raw
: AccountId.decode(value),
AccountId.decodeAccountId(value),
256
);
}
@@ -31,8 +29,10 @@ export default class AccountId extends U8aFixed {
return encodeAddress(value);
}
static decode (value: string | Uint8Array | Array<number>): Uint8Array {
if (isU8a(value) || Array.isArray(value)) {
static decodeAccountId (value: AnyU8a): Uint8Array {
if (value instanceof U8a) {
return value.raw;
} else if (isU8a(value) || Array.isArray(value)) {
return u8aToU8a(value);
} else if (isHex(value)) {
return hexToU8a(value);
@@ -42,7 +42,7 @@ export default class AccountId extends U8aFixed {
}
fromJSON (input: any): AccountId {
super.fromJSON(AccountId.decode(input));
super.fromJSON(AccountId.decodeAccountId(input));
return this;
}
+13 -7
View File
@@ -2,11 +2,11 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { AnyU8a } from './types';
import isU8a from '@polkadot/util/is/u8a';
import u8aToHex from '@polkadot/util/u8a/toHex';
import u8aToU8a from '@polkadot/util/u8a/toU8a';
import { AnyU8a } from './types';
import U8a from './codec/U8a';
// A wrapper around an AccountIndex, which is a shortened, variable-length encoding
@@ -15,9 +15,7 @@ import U8a from './codec/U8a';
export default class AccountIndex extends U8a {
constructor (value: AnyU8a = new Uint8Array()) {
super(
value instanceof U8a
? value.raw
: AccountIndex.decode(value)
AccountIndex.decodeAccountIndex(value)
);
}
@@ -27,7 +25,15 @@ export default class AccountIndex extends U8a {
return u8aToHex(value);
}
static decode (value: string | Uint8Array | Array<number>): Uint8Array {
static decodeAccountIndex (value: AnyU8a): Uint8Array {
if (value instanceof U8a) {
return value.raw;
} else if (isU8a(value)) {
if (!value.length) {
return value;
}
return value.subarray(0, AccountIndex.readLength(value));
}
return u8aToU8a(value);
}
@@ -49,7 +55,7 @@ export default class AccountIndex extends U8a {
}
fromJSON (input: any): AccountIndex {
super.fromJSON(AccountIndex.decode(input));
super.fromJSON(AccountIndex.decodeAccountIndex(input));
return this;
}
+84 -88
View File
@@ -2,96 +2,92 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import AccountId from './AccountId';
import AccountIndex from './AccountIndex';
import Address from './Address';
const testDecode = (type, input, expected) =>
it(`can decode from ${type}`, () => {
const a = new Address(input);
expect(a.toString()).toBe(expected);
});
const testEncode = (to, expected) =>
it(`can encode ${to}`, () => {
const a = new Address('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
expect(a[to]()).toEqual(expected);
});
describe('Address', () => {
it('decodes with a prefix (255)', () => {
expect(
new Address()
.fromU8a(
new Uint8Array([
255,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
])
)
.toString()
).toEqual('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
});
testDecode(
'Address',
new Address('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'AccountId',
new AccountId('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'AccountIndex',
new AccountIndex('0x0102030405060708010203040506070801020304050607080102030405060708'),
'0x0102030405060708010203040506070801020304050607080102030405060708'
);
testDecode(
'Array',
[
255,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
],
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'Uint8Array (with prefix 255)',
Uint8Array.from([
255,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
]),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'Uint8Array (with prefix 1 byte)',
Uint8Array.from([17]),
'0x11'
);
testDecode(
'Uint8Array (with prefix 2 bytes)',
Uint8Array.from([0xfc, 17, 18]),
'0xfc1112'
);
testDecode(
'Uint8Array (with prefix 4 bytes)',
Uint8Array.from([0xfd, 17, 18, 19, 20]),
'0xfd11121314'
);
testDecode(
'Uint8Array (with prefix 8 bytes)',
Uint8Array.from([0xfe, 17, 18, 19, 20, 21, 22, 23, 24]),
'0xfe1112131415161718'
);
it('encodes with a prefix', () => {
expect(
new Address()
.fromJSON('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s')
.toU8a()
).toEqual(
new Uint8Array([
255,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
])
);
});
it('encodes address to ss-58 format', () => {
expect(
new Address()
.fromU8a(
new Uint8Array([
255,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
])
)
.toString()
).toEqual('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
});
it('decodes with a prefix (1-byte)', () => {
expect(
new Address()
.fromU8a(
new Uint8Array([
17
])
)
.toString()
).toEqual('0x11');
});
it('decodes with a prefix (2-bytes)', () => {
expect(
new Address()
.fromU8a(
new Uint8Array([
0xfc, 17, 18
])
)
.toString()
).toEqual('0xfc1112');
});
it('decodes with a prefix (4-bytes)', () => {
expect(
new Address()
.fromU8a(
new Uint8Array([
0xfd, 17, 18, 19, 20
])
)
.toString()
).toEqual('0xfd11121314');
});
it('decodes with a prefix (8-bytes)', () => {
expect(
new Address()
.fromU8a(
new Uint8Array([
0xfe, 17, 18, 19, 20, 21, 22, 23, 24
])
)
.toString()
).toEqual('0xfe1112131415161718');
});
// FIXME Add this
// testEncode(
// 'toHex',
// '0x0102030405060708010203040506070801020304050607080102030405060708'
// );
testEncode(
'toString',
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testEncode(
'toU8a',
Uint8Array.from([
255,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
])
);
});
+17 -11
View File
@@ -7,9 +7,10 @@ import isU8a from '@polkadot/util/is/u8a';
import u8aConcat from '@polkadot/util/u8a/concat';
import u8aToU8a from '@polkadot/util/u8a/toU8a';
import Base from './codec/Base';
import AccountId from './AccountId';
import AccountIndex from './AccountIndex';
import { AnyU8a } from './types';
import Base from './codec/Base';
const ACCOUNT_ID_PREFIX = new Uint8Array([0xff]);
@@ -19,21 +20,26 @@ const ACCOUNT_ID_PREFIX = new Uint8Array([0xff]);
// is encoded as
// [ <prefix-byte>, ...publicKey/...bytes ]
export default class Address extends Base<AccountId | AccountIndex> {
constructor (value: Address | AccountId | AccountIndex | string | Uint8Array = new Uint8Array()) {
constructor (value: Address | AccountId | AccountIndex | AnyU8a = new Uint8Array()) {
super(
value instanceof Address
? value.raw
: Address.decode(value)
Address.decodeAddress(value)
);
}
static decode (value: AccountId | AccountIndex | string | Uint8Array | Array<number>): AccountId | AccountIndex {
if (value instanceof AccountId || value instanceof AccountIndex) {
static decodeAddress (value: Address | AccountId | AccountIndex | AnyU8a): AccountId | AccountIndex {
if (value instanceof Address) {
return value.raw;
} else if (value instanceof AccountId || value instanceof AccountIndex) {
return value;
} else if (isU8a(value) || Array.isArray(value)) {
} else if (Array.isArray(value)) {
return Address.decodeAddress(u8aToU8a(value));
} else if (isU8a(value)) {
if (value.length === 33 && value[0] === 0xff) {
return new AccountId(value.subarray(1));
}
return value.length === 32
? new AccountId(u8aToU8a(value))
: new AccountIndex(u8aToU8a(value));
? new AccountId(value)
: new AccountIndex(value);
} else if (isHex(value)) {
return value.length === 66
? new AccountId(value)
@@ -54,7 +60,7 @@ export default class Address extends Base<AccountId | AccountIndex> {
}
fromJSON (input: any): Address {
this.raw = Address.decode(input);
this.raw = Address.decodeAddress(input);
return this;
}
+12 -3
View File
@@ -2,17 +2,26 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import isU8a from '@polkadot/util/is/u8a';
import Base from './codec/Base';
export default class Bool extends Base<boolean> {
constructor (value: Bool | boolean = false) {
super(
value instanceof Bool
? value.raw
: value
Bool.decodeBool(value)
);
}
static decodeBool (value: any): boolean {
if (value instanceof Bool) {
return value.raw;
} else if (isU8a(value)) {
return value[0] === 1;
}
return !!value;
}
byteLength (): number {
return 1;
}
+31
View File
@@ -2,8 +2,13 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import hexToU8a from '@polkadot/util/hex/toU8a';
import isHex from '@polkadot/util/is/hex';
import isU8a from '@polkadot/util/is/u8a';
import toU8a from '@polkadot/util/u8a/toU8a';
import u8aConcat from '@polkadot/util/u8a/concat';
import { AnyU8a } from './types';
import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact';
import U8a from './codec/U8a';
@@ -11,6 +16,32 @@ import U8a from './codec/U8a';
// this version allows for length-encoding. (i.e. it is a variable-item codec, the same
// as what is found in Text and Vector)
export default class Bytes extends U8a {
constructor (value: AnyU8a) {
super(Bytes.decodeBytes(value));
}
static decodeBytes (value: AnyU8a): Uint8Array {
if (value instanceof U8a) {
return value.raw;
} else if (isHex(value)) {
// FIXME We manually add the length prefix for hex for now
// https://github.com/paritytech/substrate/issues/889
// Instead of the block below, it should simply be:
// return Bytes.decodeBytes(hexToU8a(value as string));
const u8a = hexToU8a(value as string);
return Bytes.decodeBytes(
u8aConcat(
Compact.encodeU8a(u8a.length, DEFAULT_LENGTH_BITS),
u8a
)
);
} else if (isU8a(value)) {
const [offset, length] = Compact.decodeU8a(value, DEFAULT_LENGTH_BITS);
return value.subarray(offset, offset + length.toNumber());
}
return Bytes.decodeBytes(toU8a(value));
}
get length (): number {
return this.raw.length;
}
+2 -2
View File
@@ -8,12 +8,12 @@ import Extrinsic from './Extrinsic';
import Method from './Method';
describe('Extrinsic', () => {
beforeEach(() => {
beforeAll(() => {
Method.injectExtrinsics(extrinsics);
});
it('decodes a non-signed properly via fromJSON', () => {
const extrinsic = new Extrinsic().fromJSON('0x010200ea51b75b00000000');
const extrinsic = new Extrinsic('0x010200ea51b75b00000000');
expect(extrinsic.isSigned).toEqual(false);
expect(extrinsic.callIndex).toEqual(new Uint8Array([2, 0]));
+28 -2
View File
@@ -7,6 +7,7 @@ import { AnyNumber, AnyU8a } from './types';
import blake2Asu8a from '@polkadot/util-crypto/blake2/asU8a';
import hexToU8a from '@polkadot/util/hex/toU8a';
import isU8a from '@polkadot/util/is/u8a';
import u8aConcat from '@polkadot/util/u8a/concat';
import u8aToHex from '@polkadot/util/u8a/toHex';
@@ -16,6 +17,7 @@ import ExtrinsicSignature from './ExtrinsicSignature';
import Hash from './Hash';
import { FunctionMetadata } from './Metadata';
import Method from './Method';
import isHex from '@polkadot/util/is/hex';
type ExtrinsicValue = {
method?: Method
@@ -33,11 +35,35 @@ type ExtrinsicValue = {
* - left as is, to create an inherent
*/
export default class Extrinsic extends Struct {
constructor (value?: ExtrinsicValue) {
constructor (value?: ExtrinsicValue | AnyU8a) {
super({
signature: ExtrinsicSignature,
method: Method
}, value);
}, Extrinsic.decodeExtrinsic(value));
}
static decodeExtrinsic (value?: ExtrinsicValue | AnyU8a): object | Uint8Array {
if (!value) {
return {};
} else if (value instanceof Extrinsic) {
return value.raw;
} else if (isHex(value)) {
// FIXME We manually add the length prefix for hex for now
// https://github.com/paritytech/substrate/issues/889
// Instead of the block below, it should simply be:
// return Extrinsic.decodeExtrinsic(hexToU8a(value as string));
const u8a = hexToU8a(value as string);
return Extrinsic.decodeExtrinsic(
u8aConcat(
Compact.encodeU8a(u8a.length, DEFAULT_LENGTH_BITS),
u8a
)
);
} else if (isU8a(value)) {
const [offset, length] = Compact.decodeU8a(value, DEFAULT_LENGTH_BITS);
return value.subarray(offset, offset + length.toNumber());
}
return value as any;
}
// the actual [sectionIndex, methodIndex] as used
+20 -2
View File
@@ -5,6 +5,7 @@
import { KeyringPair } from '@polkadot/util-keyring/types';
import { AnyNumber, AnyU8a } from './types';
import isU8a from '@polkadot/util/is/u8a';
import u8aConcat from '@polkadot/util/u8a/concat';
import Struct from './codec/Struct';
@@ -40,7 +41,24 @@ export default class ExtrinsicSignature extends Struct {
signature: Signature,
nonce: Nonce,
era: ExtrinsicEra
}, value);
}, ExtrinsicSignature.decodeExtrinsicSignature(value));
}
static decodeExtrinsicSignature (value: ExtrinsicSignature | ExtrinsicSignatureValue | AnyU8a | undefined): object | Uint8Array {
if (!value) {
return {};
} else if (value instanceof Struct) {
return value.raw;
} else if (isU8a(value)) {
const version = value[0];
if ((version & BIT_SIGNED) === BIT_SIGNED) {
return value.subarray(1);
} else {
return {};
}
}
return value as any;
}
byteLength (): number {
@@ -73,7 +91,7 @@ export default class ExtrinsicSignature extends Struct {
}
get version (): number {
// Version Information.
// Version Information.
// 1 byte: version information:
// - 7 low bits: version identifier (should be 0b0000001).
// - 1 high bit: signed flag: 1 if this is a transaction (e.g. contains a signature).
+3 -3
View File
@@ -7,8 +7,8 @@ import KeyValue, { KeyValueOption } from './KeyValue';
describe('KeyValue', () => {
it('decodes KeyValue from u8a', () => {
expect(
new KeyValue().fromU8a(
new Uint8Array([
new KeyValue(
Uint8Array.from([
4 << 2,
0x11, 0x22, 0x33, 0x44,
9 << 2,
@@ -23,7 +23,7 @@ describe('KeyValue', () => {
it('encodes KeyValue from JSON', () => {
expect(
new KeyValue().fromJSON({
new KeyValue({
key: '0x11223344',
value: '0x998877665544332211'
}).toU8a()
+9
View File
@@ -5,6 +5,7 @@
import { AnyU8a } from './types';
import { ExtrinsicFunction, Extrinsics } from '@polkadot/extrinsics/types';
import isU8a from '@polkadot/util/is/u8a';
import u8aConcat from '@polkadot/util/u8a/concat';
import createType from './codec/createType';
@@ -22,6 +23,7 @@ const extrinsicFns: { [index: string]: ExtrinsicFunction } = {};
/**
* Extrinsic function descriptor, as defined in
* {@link https://github.com/paritytech/wiki/blob/master/Extrinsic.md#the-extrinsic-format-for-node}.
* // FIXME This class should extend Struct({ callIndex, method })
*/
export default class Method extends MethodIndex {
protected _args: Array<Base>;
@@ -40,6 +42,13 @@ export default class Method extends MethodIndex {
}
this._data = Method.encode(this._meta, this._args);
// FIXME this is really not clean, we're saying that if the callIndex is
// longer than 2 bytes, then we need to decode the rest too.
if (isU8a(index) && index.length > 2) {
this.fromU8a(index);
}
}
static decodeMethod (meta: FunctionMetadata, data: Uint8Array): Array<Base> {
+21 -36
View File
@@ -2,45 +2,30 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import BN from 'bn.js';
import Moment from './Moment';
import U64 from './U64';
const testDecode = (type, input, expected, toJSON = false) =>
it(`can decode from ${type}`, () => {
expect(new Moment(input)[toJSON ? 'toJSON' : 'toString']()).toBe(expected);
});
const testEncode = (to, expected) =>
it(`can encode ${to}`, () => {
expect(new Moment(421)[to]()).toEqual(expected);
});
describe('Moment', () => {
it('constructs via Date', () => {
expect(
new Moment(
new Date(1537968546280)
).toString()
).toEqual('2018-09-26T13:29:07.000Z');
});
testDecode('Date', new Date(1537968546280), '2018-09-26T13:29:07.000Z');
testDecode('Moment', new Moment(1234), 1234, true);
testDecode('number', 1234, 1234, true);
testDecode('U64', new U64(69), 69, true);
it('wraps from another Moment', () => {
expect(
new Moment(
new Moment(1234)
).toJSON()
).toEqual(1234);
});
it('reads values from JSON', () => {
expect(
new Moment().fromJSON(42).toNumber()
).toEqual(42);
});
it('reads values from u8a', () => {
expect(
new Moment().fromU8a(
new U64(69).toU8a()
).toNumber()
).toEqual(69);
});
it('creates valid U64 u8a outputs', () => {
expect(
new U64().fromU8a(
new Moment(421).toU8a()
).toNumber()
).toEqual(421);
});
testEncode('toBn', new BN(421));
testEncode('toJSON', 421);
testEncode('toString', '1970-01-01T00:07:01.000Z');
testEncode('toNumber', 421);
testEncode('toU8a', Uint8Array.from([165, 1, 0, 0, 0, 0, 0, 0]));
});
+16 -9
View File
@@ -5,10 +5,13 @@
import BN from 'bn.js';
import bnToBn from '@polkadot/util/bn/toBn';
import bnToU8a from '@polkadot/util/bn/toU8a';
import isString from '@polkadot/util/is/string';
import isU8a from '@polkadot/util/is/u8a';
import u8aToBn from '@polkadot/util/u8a/toBn';
import { AnyNumber } from './types';
import Base from './codec/Base';
import U64 from './U64';
const BITLENGTH = 64;
@@ -19,21 +22,25 @@ const BITLENGTH = 64;
export default class Moment extends Base<Date> {
constructor (value: Uint8Array | Moment | Date | BN | number = 0) {
super(
value instanceof Date
? new Date(Math.ceil(value.getTime() / 1000) * 1000)
: Moment.decode(value)
Moment.decodeMoment(value)
);
}
static decode (value: Uint8Array | Moment | number | BN): Date {
static decodeMoment (value: Moment | Date | AnyNumber): Date {
if (value instanceof Moment) {
return value.raw;
} if (isU8a(value)) {
value = u8aToBn(value, true);
} else if (value instanceof Date) {
return new Date(Math.ceil(value.getTime() / 1000) * 1000);
} else if (value instanceof U64) {
value = value.toBn();
} else if (isU8a(value)) {
value = u8aToBn(value.subarray(0, BITLENGTH / 8), true);
} else if (isString(value)) {
value = new BN(value, 10, 'le');
}
return new Date(
bnToBn(value).toNumber() * 1000
bnToBn(value as BN).toNumber() * 1000
);
}
@@ -42,13 +49,13 @@ export default class Moment extends Base<Date> {
}
fromJSON (input: any): Moment {
this.raw = Moment.decode(input);
this.raw = Moment.decodeMoment(input);
return this;
}
fromU8a (input: Uint8Array): Moment {
this.raw = Moment.decode(
this.raw = Moment.decodeMoment(
u8aToBn(input.subarray(0, this.byteLength()), true)
);
+4 -9
View File
@@ -5,9 +5,7 @@
import { AnyU8a } from './types';
import isFunction from '@polkadot/util/is/function';
import toU8a from '@polkadot/util/u8a/toU8a';
import U8a from './codec/U8a';
import Bytes from './Bytes';
import { StorageFunctionMetadata } from './Metadata';
@@ -25,15 +23,13 @@ export default class StorageKey extends Bytes {
private _outputType: string | null;
constructor (value: AnyU8a | StorageKey | StorageFunction | [StorageFunction, any]) {
super(StorageKey.encode(value));
super(StorageKey.decodeStorageKey(value));
this._outputType = StorageKey.getType(value as StorageKey);
}
static encode (value: AnyU8a | StorageKey | StorageFunction | [StorageFunction, any]): Uint8Array {
if (value instanceof StorageKey || value instanceof U8a) {
return value.raw;
} else if (isFunction(value)) {
static decodeStorageKey (value: AnyU8a | StorageKey | StorageFunction | [StorageFunction, any]): Uint8Array {
if (isFunction(value)) {
return value();
} else if (Array.isArray(value)) {
const [fn, arg] = value;
@@ -42,8 +38,7 @@ export default class StorageKey extends Bytes {
return fn(arg);
}
}
return toU8a(value);
return value as Uint8Array;
}
static getType (value: StorageKey | StorageFunction | [StorageFunction, any]): string | null {
+1 -1
View File
@@ -60,7 +60,7 @@ export default class Struct<
return Object
.keys(Types)
.reduce((raw: T, key, index) => {
.reduce((raw: T, key) => {
if (isU8a(value)) {
// @ts-ignore FIXME See below
raw[key] = new Types[key](
+2 -2
View File
@@ -23,10 +23,10 @@ export default class Tuple<
V = { [K in keyof S]: any }
> extends Struct<S, T, V> {
constructor (Types: S, value?: V | AnyU8a, jsonMap?: Map<keyof S, string>) {
super(Types, Tuple.decodeU8a(Types, value), jsonMap);
super(Types, Tuple.decodeTuple(Types, value), jsonMap);
}
static decodeU8a<S, V> (Types: S, _value: V | AnyU8a): V {
static decodeTuple<S, V> (Types: S, _value: V | AnyU8a): V {
if (!isU8a(_value) && !isString(_value) && !Array.isArray(_value)) {
return _value as V;
}