Add paymentInfo to submittables (#1582)

* Add paymentInfo to submittables

* Cleanup paymentInfo call

* Use 0x42...42 dummy sigs

* Re-add paymentInfo to submittable

* Add correct signFake interfaces

* CHANGELOG
This commit is contained in:
Jaco Greeff
2020-01-09 14:40:25 +01:00
committed by GitHub
parent d1bc48e936
commit 7057cf365b
13 changed files with 177 additions and 23 deletions
+1
View File
@@ -7,6 +7,7 @@
- Add support for `BTreeSet` (Thanks to https://github.com/satellitex)
- Add support for Metadata v10
- Add support for latest Polkadot/Substrate types
- Add `paymentInfo` to submittables to calculate fees
# 0.99.1
+25 -2
View File
@@ -3,10 +3,10 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { AccountId, Address, Call, ExtrinsicEra, ExtrinsicStatus, Hash, Header, Index } from '@polkadot/types/interfaces';
import { AccountId, Address, Call, ExtrinsicEra, ExtrinsicStatus, Hash, Header, Index, RuntimeDispatchInfo } from '@polkadot/types/interfaces';
import { Callback, Codec, Constructor, IKeyringPair, Registry, SignatureOptions } from '@polkadot/types/types';
import { ApiInterfaceRx, ApiTypes, SignerResult } from '../types';
import { SignerOptions, SubmittableExtrinsic, SubmittableResultImpl, SubmittableResultResult, SubmittableResultSubscription, SubmittableThis } from './types';
import { SignerOptions, SubmittableExtrinsic, SubmittablePaymentResult, SubmittableResultImpl, SubmittableResultResult, SubmittableResultSubscription, SubmittableThis } from './types';
import { Observable, combineLatest, from, of } from 'rxjs';
import { first, map, mapTo, mergeMap, switchMap, tap } from 'rxjs/operators';
@@ -49,6 +49,29 @@ export default function createClass <ApiType extends ApiTypes> ({ api, apiType,
this._ignoreStatusCb = apiType === 'rxjs';
}
// calculate the payment info for this transaction (if signed and submitted)
public paymentInfo (account: IKeyringPair | string | AccountId | Address, options?: Partial<SignerOptions>): SubmittablePaymentResult<ApiType> {
const [allOptions] = this._makeSignAndSendOptions(options);
const address = isKeyringPair(account) ? account.address : account.toString();
return this._decorateMethod(
(): Observable<RuntimeDispatchInfo> =>
this._getPrelimState(address, allOptions).pipe(
first(),
switchMap(([nonce, header]): Observable<RuntimeDispatchInfo> => {
// setup our options (same way as in signAndSend)
const eraOptions = this._makeEraOptions(allOptions, { header, nonce });
const signOptions = this._makeSignOptions(eraOptions, {});
// add a fake signature to the extrinsic
this.signFake(address, signOptions);
return this._api.rpc.payment.queryInfo(this.toHex());
})
)
)();
}
// sign a transaction, returning the this to allow chaining, i.e. .sign(...).send()
public sign (account: IKeyringPair, optionsOrNonce: Partial<SignerOptions>): this {
super.sign(account, this._makeSignOptions(this._optionsOrNonce(optionsOrNonce), {}));
+8 -1
View File
@@ -2,7 +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 { AccountId, Address, ExtrinsicStatus, EventRecord, Hash } from '@polkadot/types/interfaces';
import { AccountId, Address, ExtrinsicStatus, EventRecord, Hash, RuntimeDispatchInfo } from '@polkadot/types/interfaces';
import { AnyNumber, AnyU8a, Callback, IExtrinsic, IExtrinsicEra, IKeyringPair, SignatureOptions } from '@polkadot/types/types';
import { ApiTypes } from '../types';
@@ -24,6 +24,11 @@ export interface SubmittableResultValue {
status: ExtrinsicStatus;
}
export type SubmittablePaymentResult<ApiType extends ApiTypes> =
ApiType extends 'rxjs'
? Observable<RuntimeDispatchInfo>
: Promise<RuntimeDispatchInfo>;
export type SubmittableResultResult<ApiType extends ApiTypes> =
ApiType extends 'rxjs'
? Observable<SubmittableResultImpl>
@@ -47,6 +52,8 @@ export interface SignerOptions {
}
export interface SubmittableExtrinsic<ApiType extends ApiTypes> extends IExtrinsic {
paymentInfo (account: IKeyringPair | string | AccountId | Address, options?: Partial<SignerOptions>): SubmittablePaymentResult<ApiType>;
send(): SubmittableResultResult<ApiType>;
send(statusCb: Callback<SubmittableResultImpl>): SubmittableResultSubscription<ApiType>;
@@ -252,11 +252,20 @@ export default class Extrinsic extends Base<ExtrinsicVx | ExtrinsicUnknown> impl
return this;
}
/**
* @describe Adds a fake signature to the extrinsic
*/
public signFake (signer: Address | Uint8Array | string, options: SignatureOptions): Extrinsic {
(this.raw as ExtrinsicVx).signFake(signer, options);
return this;
}
/**
* @description Returns a hex string representation of the value
*/
public toHex (): string {
return u8aToHex(this.toU8a());
public toHex (isBare?: boolean): string {
return u8aToHex(this.toU8a(isBare));
}
/**
@@ -275,10 +284,12 @@ export default class Extrinsic extends Base<ExtrinsicVx | ExtrinsicUnknown> impl
/**
* @description Encodes the value as a Uint8Array as per the SCALE specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
* @param isBare true when the value is not length-prefixed
*/
public toU8a (isBare?: boolean): Uint8Array {
const encoded = u8aConcat(new Uint8Array([this.version]), this.raw.toU8a(isBare));
// we do not apply bare to the internal values, rather this only determines out length addition,
// where we strip all lengths this creates an un-decodable extrinsic
const encoded = u8aConcat(new Uint8Array([this.version]), this.raw.toU8a());
return isBare
? encoded
@@ -96,4 +96,13 @@ export default class ExtrinsicV1 extends Struct implements IExtrinsicImpl {
return this;
}
/**
* @describe Adds a fake signature to the extrinsic
*/
public signFake (signer: Address | Uint8Array | string, options: SignatureOptions): ExtrinsicV1 {
this.signature.signFake(this.method, signer, options);
return this;
}
}
@@ -116,11 +116,10 @@ export default class ExtrinsicSignatureV1 extends Struct implements IExtrinsicSi
}
/**
* @description Generate a payload and applies the signature from a keypair
* @description Creates a payload from the supplied options
*/
public sign (method: Call, account: IKeyringPair, { blockHash, era, genesisHash, nonce }: SignatureOptions): IExtrinsicSignature {
const signer = createType(this.registry, 'Address', account.publicKey);
const payload = new ExtrinsicPayloadV1(this.registry, {
public createPayload (method: Call, { blockHash, era, genesisHash, nonce }: SignatureOptions): ExtrinsicPayloadV1 {
return new ExtrinsicPayloadV1(this.registry, {
blockHash,
era: era || IMMORTAL_ERA,
genesisHash,
@@ -129,11 +128,30 @@ export default class ExtrinsicSignatureV1 extends Struct implements IExtrinsicSi
specVersion: 0, // unused for v1, added for compat
tip: 0
});
}
/**
* @description Generate a payload and applies the signature from a keypair
*/
public sign (method: Call, account: IKeyringPair, options: SignatureOptions): IExtrinsicSignature {
const signer = createType(this.registry, 'Address', account.publicKey);
const payload = this.createPayload(method, options);
const signature = createType(this.registry, 'Signature', payload.sign(account));
return this.injectSignature(signer, signature, payload);
}
/**
* @description Generate a payload and applies a fake signature
*/
public signFake (method: Call, address: Address | Uint8Array | string, options: SignatureOptions): IExtrinsicSignature {
const signer = createType(this.registry, 'Address', address);
const payload = this.createPayload(method, options);
const signature = createType(this.registry, 'Signature', new Uint8Array(64).fill(0x42));
return this.injectSignature(signer, signature, payload);
}
/**
* @description Encodes the value as a Uint8Array as per the SCALE specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
@@ -98,4 +98,13 @@ export default class ExtrinsicV2 extends Struct implements IExtrinsicImpl {
return this;
}
/**
* @describe Adds a fake signature to the extrinsic
*/
public signFake (signer: Address | Uint8Array | string, options: SignatureOptions): ExtrinsicV2 {
this.signature.signFake(this.method, signer, options);
return this;
}
}
@@ -113,11 +113,10 @@ export default class ExtrinsicSignatureV2 extends Struct implements IExtrinsicSi
}
/**
* @description Generate a payload and applies the signature from a keypair
* @description Creates a payload from the supplied options
*/
public sign (method: Call, account: IKeyringPair, { blockHash, era, genesisHash, nonce, tip }: SignatureOptions): IExtrinsicSignature {
const signer = createType(this.registry, 'Address', account.publicKey);
const payload = new ExtrinsicPayloadV2(this.registry, {
public createPayload (method: Call, { blockHash, era, genesisHash, nonce, tip }: SignatureOptions): ExtrinsicPayloadV2 {
return new ExtrinsicPayloadV2(this.registry, {
blockHash,
era: era || IMMORTAL_ERA,
genesisHash,
@@ -126,11 +125,30 @@ export default class ExtrinsicSignatureV2 extends Struct implements IExtrinsicSi
specVersion: 0, // unused for v2
tip: tip || 0
});
}
/**
* @description Generate a payload and applies the signature from a keypair
*/
public sign (method: Call, account: IKeyringPair, options: SignatureOptions): IExtrinsicSignature {
const signer = createType(this.registry, 'Address', account.publicKey);
const payload = this.createPayload(method, options);
const signature = createType(this.registry, 'Signature', payload.sign(account));
return this.injectSignature(signer, signature, payload);
}
/**
* @description Generate a payload and applies a fake signature
*/
public signFake (method: Call, address: Address | Uint8Array | string, options: SignatureOptions): IExtrinsicSignature {
const signer = createType(this.registry, 'Address', address);
const payload = this.createPayload(method, options);
const signature = createType(this.registry, 'Signature', new Uint8Array(64).fill(0x42));
return this.injectSignature(signer, signature, payload);
}
/**
* @description Encodes the value as a Uint8Array as per the SCALE specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
@@ -98,4 +98,13 @@ export default class ExtrinsicV3 extends Struct implements IExtrinsicImpl {
return this;
}
/**
* @describe Adds a fake signature to the extrinsic
*/
public signFake (signer: Address | Uint8Array | string, options: SignatureOptions): ExtrinsicV3 {
this.signature.signFake(this.method, signer, options);
return this;
}
}
@@ -28,11 +28,10 @@ export default class ExtrinsicSignatureV3 extends ExtrinsicSignatureV2 {
}
/**
* @description Generate a payload and pplies the signature from a keypair
* @description Creates a payload from the supplied options
*/
public sign (method: Call, account: IKeyringPair, { blockHash, era, genesisHash, nonce, runtimeVersion: { specVersion }, tip }: SignatureOptions): IExtrinsicSignature {
const signer = createType(this.registry, 'Address', account.publicKey);
const payload = new ExtrinsicPayloadV3(this.registry, {
public createPayload (method: Call, { blockHash, era, genesisHash, nonce, runtimeVersion: { specVersion }, tip }: SignatureOptions): ExtrinsicPayloadV3 {
return new ExtrinsicPayloadV3(this.registry, {
blockHash,
era: era || IMMORTAL_ERA,
genesisHash,
@@ -41,8 +40,27 @@ export default class ExtrinsicSignatureV3 extends ExtrinsicSignatureV2 {
specVersion,
tip: tip || 0
});
}
/**
* @description Generate a payload and applies the signature from a keypair
*/
public sign (method: Call, account: IKeyringPair, options: SignatureOptions): IExtrinsicSignature {
const signer = createType(this.registry, 'Address', account.publicKey);
const payload = this.createPayload(method, options);
const signature = createType(this.registry, 'Signature', payload.sign(account));
return this.injectSignature(signer, signature, payload);
}
/**
* @description Generate a payload and applies a fake signature
*/
public signFake (method: Call, address: Address | Uint8Array | string, options: SignatureOptions): IExtrinsicSignature {
const signer = createType(this.registry, 'Address', address);
const payload = this.createPayload(method, options);
const signature = createType(this.registry, 'Signature', new Uint8Array(64).fill(0x42));
return this.injectSignature(signer, signature, payload);
}
}
@@ -98,4 +98,13 @@ export default class ExtrinsicV4 extends Struct implements IExtrinsicImpl {
return this;
}
/**
* @describe Adds a fake signature to the extrinsic
*/
public signFake (signer: Address | Uint8Array | string, options: SignatureOptions): ExtrinsicV4 {
this.signature.signFake(this.method, signer, options);
return this;
}
}
@@ -6,6 +6,8 @@ import { Address, Balance, Call, EcdsaSignature, Ed25519Signature, ExtrinsicEra,
import { ExtrinsicPayloadValue, IExtrinsicSignature, IKeyringPair, Registry, SignatureOptions } from '../../../types';
import { ExtrinsicSignatureOptions } from '../types';
import { u8aConcat } from '@polkadot/util';
import { createType } from '../../../codec/create';
import Compact from '../../../codec/Compact';
import Struct from '../../../codec/Struct';
@@ -120,11 +122,10 @@ export default class ExtrinsicSignatureV4 extends Struct implements IExtrinsicSi
}
/**
* @description Generate a payload and applies the signature from a keypair
* @description Creates a payload from the supplied options
*/
public sign (method: Call, account: IKeyringPair, { blockHash, era, genesisHash, nonce, runtimeVersion: { specVersion }, tip }: SignatureOptions): IExtrinsicSignature {
const signer = createType(this.registry, 'Address', account.publicKey);
const payload = new ExtrinsicPayloadV4(this.registry, {
public createPayload (method: Call, { blockHash, era, genesisHash, nonce, runtimeVersion: { specVersion }, tip }: SignatureOptions): ExtrinsicPayloadV4 {
return new ExtrinsicPayloadV4(this.registry, {
blockHash,
era: era || IMMORTAL_ERA,
genesisHash,
@@ -133,11 +134,30 @@ export default class ExtrinsicSignatureV4 extends Struct implements IExtrinsicSi
specVersion,
tip: tip || 0
});
}
/**
* @description Generate a payload and applies the signature from a keypair
*/
public sign (method: Call, account: IKeyringPair, options: SignatureOptions): IExtrinsicSignature {
const signer = createType(this.registry, 'Address', account.publicKey);
const payload = this.createPayload(method, options);
const signature = createType(this.registry, 'MultiSignature', payload.sign(account));
return this.injectSignature(signer, signature, payload);
}
/**
* @description Generate a payload and applies a fake signature
*/
public signFake (method: Call, address: Address | Uint8Array | string, options: SignatureOptions): IExtrinsicSignature {
const signer = createType(this.registry, 'Address', address);
const payload = this.createPayload(method, options);
const signature = createType(this.registry, 'MultiSignature', u8aConcat(new Uint8Array([1]), new Uint8Array(64).fill(0x42)));
return this.injectSignature(signer, signature, payload);
}
/**
* @description Encodes the value as a Uint8Array as per the SCALE specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
+2
View File
@@ -210,6 +210,7 @@ export interface ExtrinsicPayloadValue {
export interface IExtrinsicSignature extends ExtrinsicSignatureBase, Codec {
addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: Uint8Array | string): IExtrinsicSignature;
sign (method: Call, account: IKeyringPair, options: SignatureOptions): IExtrinsicSignature;
signFake (method: Call, address: Address | Uint8Array | string, options: SignatureOptions): IExtrinsicSignature;
}
// eslint-disable-next-line @typescript-eslint/interface-name-prefix
@@ -222,6 +223,7 @@ export interface IExtrinsicEra extends Codec {
interface IExtrinsicSignable<T> {
addSignature (signer: Address | Uint8Array | string, signature: Uint8Array | string, payload: ExtrinsicPayloadValue | Uint8Array | string): T;
sign (account: IKeyringPair, options: SignatureOptions): T;
signFake (address: Address | Uint8Array | string, options: SignatureOptions): T;
}
// eslint-disable-next-line @typescript-eslint/interface-name-prefix