diff --git a/CHANGELOG.md b/CHANGELOG.md index ca455718e1..024eefcf84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - **Breaking change** The api now uses the module name instead of the prefix to generate the storage methods. The methods of the grandpa module changed from `api.query.grandpaFinality` to `api.query.grandpa`. - Update with latest substrate 2.x types - **Breaking Change** StorageFunction has been renamed to StorageEntry. +- **Breaking Change** Vote interface extends U8a instead of I8. Vote properties can be accessed via the `isAye`, `isNay`, and `conviction` getters. +Votes can still be constructed as before with a raw JS boolean, a SCALE encoded Boolean, an i8 number, or a JS object with properties `aye` and `conviction` defined. - Support latest substrate 2.x v6 metadata with module constants. # 0.81.1 diff --git a/packages/types/src/index.spec.ts b/packages/types/src/index.spec.ts index 981042bdef..cf60f90483 100644 --- a/packages/types/src/index.spec.ts +++ b/packages/types/src/index.spec.ts @@ -8,7 +8,7 @@ import { Constructor } from './types'; import * as Classes from './index.types'; const Types = Classes as { [index: string]: Constructor }; -const UNCONSTRUCTABLE = ['origin', 'usize']; +const UNCONSTRUCTABLE = ['origin', 'usize', 'vote']; describe('types', () => { describe('default creation', () => { diff --git a/packages/types/src/type/Vote.spec.ts b/packages/types/src/type/Vote.spec.ts index 7db8ea6849..7b22b86871 100644 --- a/packages/types/src/type/Vote.spec.ts +++ b/packages/types/src/type/Vote.spec.ts @@ -2,30 +2,116 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import Boolean from '../primitive/Bool'; +import Conviction from './Conviction'; import Vote from './Vote'; describe('Vote', () => { it('constructs via boolean true', () => { - expect(new Vote(true).toNumber()).toEqual(-1); + expect(new Vote(true).toU8a()).toEqual(new Uint8Array([128])); + expect(new Vote(true).isAye).toBe(true); + expect(new Vote(true).isNay).toBe(false); }); it('constructs via boolean false', () => { - expect(new Vote(false).toNumber()).toEqual(0); + expect(new Vote(false).toU8a()).toEqual(new Uint8Array([0])); + expect(new Vote(false).isNay).toBe(true); + expect(new Vote(false).isAye).toBe(false); }); it('has isYay for positive', () => { expect(new Vote(new Boolean(true)).isAye).toBe(true); }); - it('has isYay for negative', () => { + it('has isNay for negative', () => { expect(new Vote(new Boolean(false)).isNay).toBe(true); }); - it('is positive for negative numbers', () => { - expect(new Vote(-999).isAye).toBe(true); + it('is Aye for negative numbers', () => { + expect(new Vote(-128).isAye).toBe(true); }); - it('is negative for positive numbers', () => { - expect(new Vote(999).isNay).toBe(true); + it('is Nay for positive numbers', () => { + expect(new Vote(127).isNay).toBe(true); + }); + + it('constructs V2 Vote with raw boolean', () => { + expect( + new Vote({ + aye: true, + conviction: new Conviction('Locked1x') + }) + .toU8a() + ) + .toEqual(new Uint8Array([0b10000001])); + }); + + it('constructs with V2 Vote aye is false, conviction is None', () => { + expect( + new Vote({ + aye: new Boolean(false), + conviction: new Conviction('None') + }) + .toU8a() + ) + .toEqual(new Uint8Array([0b00000000])); + }); + + it('constructs with Vote aye is true, conviction is Locked4x', () => { + expect( + new Vote({ + aye: new Boolean(true), + conviction: new Conviction('Locked4x') + }) + .toU8a() + ) + .toEqual(new Uint8Array([0b10000100])); + }); + + it('Conviction getter works', () => { + expect( + new Vote({ + aye: new Boolean(true), + conviction: new Conviction('Locked2x') + }).conviction.toString() + ) + .toEqual('Locked2x'); + }); + + it('Conviction getter works with raw boolean and string conviction', () => { + expect( + new Vote({ + aye: true, + conviction: 'Locked2x' + }).conviction.toString() + ) + .toEqual('Locked2x'); + }); + it('Conviction getter works with raw boolean and conviction index', () => { + expect( + new Vote({ + aye: true, + conviction: 2 + }).conviction.toString() + ) + .toEqual('Locked2x'); + }); + + it('isAye getter works', () => { + expect( + new Vote({ + aye: new Boolean(true), + conviction: new Conviction('None') + }).isAye) + .toEqual(true); + }); + + it('isNay getter works', () => { + expect( + new Vote({ + aye: new Boolean(true), + conviction: new Conviction('None') + }).isNay) + .toEqual(false); }); }); diff --git a/packages/types/src/type/Vote.ts b/packages/types/src/type/Vote.ts index b17b742275..61bf7ebc26 100644 --- a/packages/types/src/type/Vote.ts +++ b/packages/types/src/type/Vote.ts @@ -2,39 +2,77 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { isBoolean } from '@polkadot/util'; +import { isBoolean, isNumber, isObject, isU8a, isUndefined } from '@polkadot/util'; -import I8 from '../primitive/I8'; +import Conviction from './Conviction'; +import U8a from '../codec/U8a'; +import Bool from '../primitive/Bool'; /** * @name Vote * @description * A number of lock periods, plus a vote, one way or the other. */ -export default class Vote extends I8 { +export default class Vote extends U8a { + private _aye: Bool; + private _conviction: Conviction; // for V1, default to None + constructor (value?: any) { - super(Vote.decodeVote(value)); + // decoded is just 1 byte + // Aye: Most Significant Bit + // Conviction: 0000 - 0101 + const decoded = Vote.decodeVote(value); + + super(decoded); + + const msb = decoded[0] >> 7; + const conviction = decoded[0] & 0b01111111; + + this._aye = new Bool(msb); + this._conviction = new Conviction(conviction); } - private static decodeVote (value?: any): any { + private static decodeVote (value?: any): Uint8Array { if (isBoolean(value)) { - return value ? -1 : 0; - } else if (value instanceof Boolean) { + return value + ? new Uint8Array([0b10000000]) + : new Uint8Array([0b0]); + } else if (value instanceof Bool) { return Vote.decodeVote(value.valueOf()); + } else if (isNumber(value)) { + return value < 0 + ? new Uint8Array([0b10000000]) + : new Uint8Array([0b0]); + } else if (isObject(value) && !isUndefined(value.aye) && !isUndefined(value.conviction)) { + const aye = new Bool(value.aye); + const convictionIndex = new Conviction(value.conviction).index; + + const result = convictionIndex | (aye.eq(true) ? 0b1 << 7 : 0b0); + + return new Uint8Array([result]); + } else if (isU8a(value) && value.length > 0) { + return value; } - return value; + throw new Error(`Unable to convert input ${value} to Vote`); } /** - * @description true is the wrapped value is a positive vote + * @description returns a V2 conviction + */ + get conviction (): Conviction { + return this._conviction; + } + + /** + * @description true if the wrapped value is a positive vote */ get isAye (): boolean { - return this.ltn(0); + return this._aye.eq(true); } /** - * @description true is the wrapped value is a negative vote + * @description true if the wrapped value is a negative vote */ get isNay (): boolean { return !this.isAye;