UNPKG

10.3 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.createClass = createClass;
7
8var _rxjs = require("rxjs");
9
10var _util = require("@polkadot/util");
11
12var _util2 = require("../util");
13
14var _Result = require("./Result");
15
16// Copyright 2017-2022 @polkadot/api authors & contributors
17// SPDX-License-Identifier: Apache-2.0
18
19/* eslint-disable no-dupe-class-members */
20const identity = input => input;
21
22function makeEraOptions(api, registry, partialOptions, _ref) {
23 let {
24 header,
25 mortalLength,
26 nonce
27 } = _ref;
28
29 if (!header) {
30 if ((0, _util.isNumber)(partialOptions.era)) {
31 // since we have no header, it is immortal, remove any option overrides
32 // so we only supply the genesisHash and no era to the construction
33 delete partialOptions.era;
34 delete partialOptions.blockHash;
35 }
36
37 return makeSignOptions(api, partialOptions, {
38 nonce
39 });
40 }
41
42 return makeSignOptions(api, partialOptions, {
43 blockHash: header.hash,
44 era: registry.createTypeUnsafe('ExtrinsicEra', [{
45 current: header.number,
46 period: partialOptions.era || mortalLength
47 }]),
48 nonce
49 });
50}
51
52function makeSignAndSendOptions(partialOptions, statusCb) {
53 let options = {};
54
55 if ((0, _util.isFunction)(partialOptions)) {
56 statusCb = partialOptions;
57 } else {
58 options = (0, _util.objectSpread)({}, partialOptions);
59 }
60
61 return [options, statusCb];
62}
63
64function makeSignOptions(api, partialOptions, extras) {
65 return (0, _util.objectSpread)({
66 blockHash: api.genesisHash,
67 genesisHash: api.genesisHash
68 }, partialOptions, extras, {
69 runtimeVersion: api.runtimeVersion,
70 signedExtensions: api.registry.signedExtensions,
71 version: api.extrinsicType
72 });
73}
74
75function optionsOrNonce() {
76 let partialOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
77 return (0, _util.isBn)(partialOptions) || (0, _util.isNumber)(partialOptions) ? {
78 nonce: partialOptions
79 } : partialOptions;
80}
81
82function createClass(_ref2) {
83 let {
84 api,
85 apiType,
86 blockHash,
87 decorateMethod
88 } = _ref2;
89 // an instance of the base extrinsic for us to extend
90 const ExtrinsicBase = api.registry.createClass('Extrinsic');
91
92 class Submittable extends ExtrinsicBase {
93 #ignoreStatusCb;
94 #transformResult = identity;
95
96 constructor(registry, extrinsic) {
97 super(registry, extrinsic, {
98 version: api.extrinsicType
99 });
100 this.#ignoreStatusCb = apiType === 'rxjs';
101 } // dry run an extrinsic
102
103
104 dryRun(account, optionsOrHash) {
105 if (blockHash || (0, _util.isString)(optionsOrHash) || (0, _util.isU8a)(optionsOrHash)) {
106 // eslint-disable-next-line @typescript-eslint/no-unsafe-return
107 return decorateMethod(() => api.rpc.system.dryRun(this.toHex(), blockHash || optionsOrHash));
108 } // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call
109
110
111 return decorateMethod(() => this.#observeSign(account, optionsOrHash).pipe((0, _rxjs.switchMap)(() => api.rpc.system.dryRun(this.toHex()))))();
112 } // calculate the payment info for this transaction (if signed and submitted)
113
114
115 paymentInfo(account, optionsOrHash) {
116 if (blockHash || (0, _util.isString)(optionsOrHash) || (0, _util.isU8a)(optionsOrHash)) {
117 // eslint-disable-next-line @typescript-eslint/no-unsafe-return
118 return decorateMethod(() => api.rpc.payment.queryInfo(this.toHex(), blockHash || optionsOrHash));
119 }
120
121 const [allOptions] = makeSignAndSendOptions(optionsOrHash);
122 const address = (0, _util2.isKeyringPair)(account) ? account.address : account.toString(); // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call
123
124 return decorateMethod(() => api.derive.tx.signingInfo(address, allOptions.nonce, allOptions.era).pipe((0, _rxjs.first)(), (0, _rxjs.switchMap)(signingInfo => {
125 // setup our options (same way as in signAndSend)
126 const eraOptions = makeEraOptions(api, this.registry, allOptions, signingInfo);
127 const signOptions = makeSignOptions(api, eraOptions, {});
128 return api.rpc.payment.queryInfo(this.isSigned ? api.tx(this).signFake(address, signOptions).toHex() : this.signFake(address, signOptions).toHex());
129 })))();
130 } // send with an immediate Hash result
131
132
133 // send implementation for both immediate Hash and statusCb variants
134 send(statusCb) {
135 const isSubscription = api.hasSubscriptions && (this.#ignoreStatusCb || !!statusCb); // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call
136
137 return decorateMethod(isSubscription ? this.#observeSubscribe : this.#observeSend)(statusCb);
138 }
139 /**
140 * @description Sign a transaction, returning the this to allow chaining, i.e. .sign(...).send(). When options, e.g. nonce/blockHash are not specified, it will be inferred. To retrieve eg. nonce use `signAsync` (the preferred interface, this is provided for backwards compatibility)
141 * @deprecated
142 */
143
144
145 sign(account, partialOptions) {
146 super.sign(account, makeSignOptions(api, optionsOrNonce(partialOptions), {}));
147 return this;
148 }
149 /**
150 * @description Signs a transaction, returning `this` to allow chaining. E.g.: `sign(...).send()`. Like `.signAndSend` this will retrieve the nonce and blockHash to send the tx with.
151 */
152
153
154 signAsync(account, partialOptions) {
155 // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call
156 return decorateMethod(() => this.#observeSign(account, partialOptions).pipe((0, _rxjs.mapTo)(this)))();
157 } // signAndSend with an immediate Hash result
158
159
160 // signAndSend implementation for all 3 cases above
161 signAndSend(account, partialOptions, optionalStatusCb) {
162 const [options, statusCb] = makeSignAndSendOptions(partialOptions, optionalStatusCb);
163 const isSubscription = api.hasSubscriptions && (this.#ignoreStatusCb || !!statusCb); // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call
164
165 return decorateMethod(() => this.#observeSign(account, options).pipe((0, _rxjs.switchMap)(info => isSubscription ? this.#observeSubscribe(info) : this.#observeSend(info))) // FIXME This is wrong, SubmittableResult is _not_ a codec
166 )(statusCb);
167 } // adds a transform to the result, applied before result is returned
168
169
170 withResultTransform(transform) {
171 this.#transformResult = transform;
172 return this;
173 }
174
175 #observeSign = (account, partialOptions) => {
176 const address = (0, _util2.isKeyringPair)(account) ? account.address : account.toString();
177 const options = optionsOrNonce(partialOptions);
178 return api.derive.tx.signingInfo(address, options.nonce, options.era).pipe((0, _rxjs.first)(), (0, _rxjs.mergeMap)(async signingInfo => {
179 const eraOptions = makeEraOptions(api, this.registry, options, signingInfo);
180 let updateId = -1;
181
182 if ((0, _util2.isKeyringPair)(account)) {
183 this.sign(account, eraOptions);
184 } else {
185 updateId = await this.#signViaSigner(address, eraOptions, signingInfo.header);
186 }
187
188 return {
189 options: eraOptions,
190 updateId
191 };
192 }));
193 };
194 #observeStatus = (txHash, status) => {
195 if (!status.isFinalized && !status.isInBlock) {
196 return (0, _rxjs.of)(this.#transformResult(new _Result.SubmittableResult({
197 status,
198 txHash
199 })));
200 }
201
202 const blockHash = status.isInBlock ? status.asInBlock : status.asFinalized;
203 return api.derive.tx.events(blockHash).pipe((0, _rxjs.map)(_ref3 => {
204 let {
205 block,
206 events
207 } = _ref3;
208 return this.#transformResult(new _Result.SubmittableResult({ ...(0, _util2.filterEvents)(txHash, block, events, status),
209 status,
210 txHash
211 }));
212 }), (0, _rxjs.catchError)(internalError => (0, _rxjs.of)(this.#transformResult(new _Result.SubmittableResult({
213 internalError,
214 status,
215 txHash
216 })))));
217 };
218 #observeSend = info => {
219 return api.rpc.author.submitExtrinsic(this).pipe((0, _rxjs.tap)(hash => {
220 this.#updateSigner(hash, info);
221 }));
222 };
223 #observeSubscribe = info => {
224 const txHash = this.hash;
225 return api.rpc.author.submitAndWatchExtrinsic(this).pipe((0, _rxjs.switchMap)(status => this.#observeStatus(txHash, status)), (0, _rxjs.tap)(status => {
226 this.#updateSigner(status, info);
227 }));
228 };
229 #signViaSigner = async (address, options, header) => {
230 const signer = options.signer || api.signer;
231 (0, _util.assert)(signer, 'No signer specified, either via api.setSigner or via sign options. You possibly need to pass through an explicit keypair for the origin so it can be used for signing.');
232 const payload = this.registry.createTypeUnsafe('SignerPayload', [(0, _util.objectSpread)({}, options, {
233 address,
234 blockNumber: header ? header.number : 0,
235 method: this.method
236 })]);
237 let result;
238
239 if ((0, _util.isFunction)(signer.signPayload)) {
240 result = await signer.signPayload(payload.toPayload());
241 } else if ((0, _util.isFunction)(signer.signRaw)) {
242 result = await signer.signRaw(payload.toRaw());
243 } else {
244 throw new Error('Invalid signer interface, it should implement either signPayload or signRaw (or both)');
245 } // Here we explicitly call `toPayload()` again instead of working with an object
246 // (reference) as passed to the signer. This means that we are sure that the
247 // payload data is not modified from our inputs, but the signer
248
249
250 super.addSignature(address, result.signature, payload.toPayload());
251 return result.id;
252 };
253 #updateSigner = (status, info) => {
254 if (info && info.updateId !== -1) {
255 const {
256 options,
257 updateId
258 } = info;
259 const signer = options.signer || api.signer;
260
261 if (signer && (0, _util.isFunction)(signer.update)) {
262 signer.update(updateId, status);
263 }
264 }
265 };
266 }
267
268 return Submittable;
269}
\No newline at end of file