UNPKG

11.9 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.createClass = void 0;
4const rxjs_1 = require("rxjs");
5const util_1 = require("@polkadot/util");
6const index_js_1 = require("../util/index.js");
7const Result_js_1 = require("./Result.js");
8function makeEraOptions(api, registry, partialOptions, { header, mortalLength, nonce }) {
9 if (!header) {
10 if (partialOptions.era && !partialOptions.blockHash) {
11 throw new Error('Expected blockHash to be passed alongside non-immortal era options');
12 }
13 if ((0, util_1.isNumber)(partialOptions.era)) {
14 // since we have no header, it is immortal, remove any option overrides
15 // so we only supply the genesisHash and no era to the construction
16 delete partialOptions.era;
17 delete partialOptions.blockHash;
18 }
19 return makeSignOptions(api, partialOptions, { nonce });
20 }
21 return makeSignOptions(api, partialOptions, {
22 blockHash: header.hash,
23 era: registry.createTypeUnsafe('ExtrinsicEra', [{
24 current: header.number,
25 period: partialOptions.era || mortalLength
26 }]),
27 nonce
28 });
29}
30function makeSignAndSendOptions(partialOptions, statusCb) {
31 let options = {};
32 if ((0, util_1.isFunction)(partialOptions)) {
33 statusCb = partialOptions;
34 }
35 else {
36 options = (0, util_1.objectSpread)({}, partialOptions);
37 }
38 return [options, statusCb];
39}
40function makeSignOptions(api, partialOptions, extras) {
41 return (0, util_1.objectSpread)({ blockHash: api.genesisHash, genesisHash: api.genesisHash }, partialOptions, extras, { runtimeVersion: api.runtimeVersion, signedExtensions: api.registry.signedExtensions, version: api.extrinsicType });
42}
43function optionsOrNonce(partialOptions = {}) {
44 return (0, util_1.isBn)(partialOptions) || (0, util_1.isNumber)(partialOptions)
45 ? { nonce: partialOptions }
46 : partialOptions;
47}
48function createClass({ api, apiType, blockHash, decorateMethod }) {
49 // an instance of the base extrinsic for us to extend
50 const ExtrinsicBase = api.registry.createClass('Extrinsic');
51 class Submittable extends ExtrinsicBase {
52 __internal__ignoreStatusCb;
53 __internal__transformResult = (util_1.identity);
54 constructor(registry, extrinsic) {
55 super(registry, extrinsic, { version: api.extrinsicType });
56 this.__internal__ignoreStatusCb = apiType === 'rxjs';
57 }
58 get hasDryRun() {
59 return (0, util_1.isFunction)(api.rpc.system?.dryRun);
60 }
61 get hasPaymentInfo() {
62 return (0, util_1.isFunction)(api.call.transactionPaymentApi?.queryInfo);
63 }
64 // dry run an extrinsic
65 dryRun(account, optionsOrHash) {
66 if (!this.hasDryRun) {
67 throw new Error('The system.dryRun RPC call is not available in your environment');
68 }
69 if (blockHash || (0, util_1.isString)(optionsOrHash) || (0, util_1.isU8a)(optionsOrHash)) {
70 // eslint-disable-next-line @typescript-eslint/no-unsafe-return
71 return decorateMethod(() => api.rpc.system.dryRun(this.toHex(), blockHash || optionsOrHash));
72 }
73 // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call
74 return decorateMethod(() => this.__internal__observeSign(account, optionsOrHash).pipe((0, rxjs_1.switchMap)(() => api.rpc.system.dryRun(this.toHex()))))();
75 }
76 // calculate the payment info for this transaction (if signed and submitted)
77 paymentInfo(account, optionsOrHash) {
78 if (!this.hasPaymentInfo) {
79 throw new Error('The transactionPaymentApi.queryInfo runtime call is not available in your environment');
80 }
81 if (blockHash || (0, util_1.isString)(optionsOrHash) || (0, util_1.isU8a)(optionsOrHash)) {
82 // eslint-disable-next-line @typescript-eslint/no-unsafe-return
83 return decorateMethod(() => api.callAt(blockHash || optionsOrHash).pipe((0, rxjs_1.switchMap)((callAt) => {
84 const u8a = this.toU8a();
85 return callAt.transactionPaymentApi.queryInfo(u8a, u8a.length);
86 })));
87 }
88 const [allOptions] = makeSignAndSendOptions(optionsOrHash);
89 const address = (0, index_js_1.isKeyringPair)(account) ? account.address : account.toString();
90 // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call
91 return decorateMethod(() => api.derive.tx.signingInfo(address, allOptions.nonce, allOptions.era).pipe((0, rxjs_1.first)(), (0, rxjs_1.switchMap)((signingInfo) => {
92 // setup our options (same way as in signAndSend)
93 const eraOptions = makeEraOptions(api, this.registry, allOptions, signingInfo);
94 const signOptions = makeSignOptions(api, eraOptions, {});
95 // 1. Don't use the internal objects inside the new tx (hence toU8a)
96 // 2. Don't override the data from existing signed extrinsics
97 // 3. Ensure that this object stays intact, with no new sign after operation
98 const u8a = api.tx(this.toU8a()).signFake(address, signOptions).toU8a();
99 return api.call.transactionPaymentApi.queryInfo(u8a, u8a.length);
100 })))();
101 }
102 // send implementation for both immediate Hash and statusCb variants
103 send(statusCb) {
104 const isSubscription = api.hasSubscriptions && (this.__internal__ignoreStatusCb || !!statusCb);
105 // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call
106 return decorateMethod(isSubscription
107 ? this.__internal__observeSubscribe
108 : this.__internal__observeSend)(statusCb);
109 }
110 /**
111 * @description Signs a transaction, returning `this` to allow chaining. E.g.: `signAsync(...).send()`. Like `.signAndSend` this will retrieve the nonce and blockHash to send the tx with.
112 */
113 signAsync(account, partialOptions) {
114 // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call
115 return decorateMethod(() => this.__internal__observeSign(account, partialOptions).pipe((0, rxjs_1.map)(() => this)))();
116 }
117 // signAndSend implementation for all 3 cases above
118 signAndSend(account, partialOptions, optionalStatusCb) {
119 const [options, statusCb] = makeSignAndSendOptions(partialOptions, optionalStatusCb);
120 const isSubscription = api.hasSubscriptions && (this.__internal__ignoreStatusCb || !!statusCb);
121 // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call
122 return decorateMethod(() => this.__internal__observeSign(account, options).pipe((0, rxjs_1.switchMap)((info) => isSubscription
123 ? this.__internal__observeSubscribe(info)
124 : this.__internal__observeSend(info))) // FIXME This is wrong, SubmittableResult is _not_ a codec
125 )(statusCb);
126 }
127 // adds a transform to the result, applied before result is returned
128 withResultTransform(transform) {
129 this.__internal__transformResult = transform;
130 return this;
131 }
132 __internal__observeSign = (account, partialOptions) => {
133 const address = (0, index_js_1.isKeyringPair)(account) ? account.address : account.toString();
134 const options = optionsOrNonce(partialOptions);
135 return api.derive.tx.signingInfo(address, options.nonce, options.era).pipe((0, rxjs_1.first)(), (0, rxjs_1.mergeMap)(async (signingInfo) => {
136 const eraOptions = makeEraOptions(api, this.registry, options, signingInfo);
137 let updateId = -1;
138 if ((0, index_js_1.isKeyringPair)(account)) {
139 this.sign(account, eraOptions);
140 }
141 else {
142 updateId = await this.__internal__signViaSigner(address, eraOptions, signingInfo.header);
143 }
144 return { options: eraOptions, updateId };
145 }));
146 };
147 __internal__observeStatus = (txHash, status) => {
148 if (!status.isFinalized && !status.isInBlock) {
149 return (0, rxjs_1.of)(this.__internal__transformResult(new Result_js_1.SubmittableResult({
150 status,
151 txHash
152 })));
153 }
154 const blockHash = status.isInBlock
155 ? status.asInBlock
156 : status.asFinalized;
157 return api.derive.tx.events(blockHash).pipe((0, rxjs_1.map)(({ block, events }) => this.__internal__transformResult(new Result_js_1.SubmittableResult({
158 ...(0, index_js_1.filterEvents)(txHash, block, events, status),
159 status,
160 txHash
161 }))), (0, rxjs_1.catchError)((internalError) => (0, rxjs_1.of)(this.__internal__transformResult(new Result_js_1.SubmittableResult({
162 internalError,
163 status,
164 txHash
165 })))));
166 };
167 __internal__observeSend = (info) => {
168 return api.rpc.author.submitExtrinsic(this).pipe((0, rxjs_1.tap)((hash) => {
169 this.__internal__updateSigner(hash, info);
170 }));
171 };
172 __internal__observeSubscribe = (info) => {
173 const txHash = this.hash;
174 return api.rpc.author.submitAndWatchExtrinsic(this).pipe((0, rxjs_1.switchMap)((status) => this.__internal__observeStatus(txHash, status)), (0, rxjs_1.tap)((status) => {
175 this.__internal__updateSigner(status, info);
176 }));
177 };
178 __internal__signViaSigner = async (address, options, header) => {
179 const signer = options.signer || api.signer;
180 if (!signer) {
181 throw new Error('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.');
182 }
183 const payload = this.registry.createTypeUnsafe('SignerPayload', [(0, util_1.objectSpread)({}, options, {
184 address,
185 blockNumber: header ? header.number : 0,
186 method: this.method
187 })]);
188 let result;
189 if ((0, util_1.isFunction)(signer.signPayload)) {
190 result = await signer.signPayload(payload.toPayload());
191 }
192 else if ((0, util_1.isFunction)(signer.signRaw)) {
193 result = await signer.signRaw(payload.toRaw());
194 }
195 else {
196 throw new Error('Invalid signer interface, it should implement either signPayload or signRaw (or both)');
197 }
198 // Here we explicitly call `toPayload()` again instead of working with an object
199 // (reference) as passed to the signer. This means that we are sure that the
200 // payload data is not modified from our inputs, but the signer
201 super.addSignature(address, result.signature, payload.toPayload());
202 return result.id;
203 };
204 __internal__updateSigner = (status, info) => {
205 if (info && (info.updateId !== -1)) {
206 const { options, updateId } = info;
207 const signer = options.signer || api.signer;
208 if (signer && (0, util_1.isFunction)(signer.update)) {
209 signer.update(updateId, status);
210 }
211 }
212 };
213 }
214 return Submittable;
215}
216exports.createClass = createClass;