22// This software may be modified and distributed under the terms
33// of the Apache-2.0 license. See the LICENSE file for details.
44
5- import { AccountId , Address , Call , ExtrinsicEra , ExtrinsicStatus , EventRecord , Hash , Header , Index , SignedBlock } from '@polkadot/types/interfaces' ;
5+ import { AccountId , Address , Call , ExtrinsicEra , ExtrinsicStatus , EventRecord , Hash , Header , Index } from '@polkadot/types/interfaces' ;
66import { AnyNumber , AnyU8a , Callback , Codec , IExtrinsic , IExtrinsicEra , IKeyringPair , SignatureOptions } from '@polkadot/types/types' ;
77import { ApiInterfaceRx , ApiTypes } from './types' ;
88
@@ -110,8 +110,7 @@ export default function createSubmittableExtrinsic<ApiType> (
110110 type : ApiTypes ,
111111 api : ApiInterfaceRx ,
112112 decorateMethod : ApiBase < ApiType > [ 'decorateMethod' ] ,
113- extrinsic : Call | Uint8Array | string ,
114- trackingCb ?: Callback < ISubmittableResult >
113+ extrinsic : Call | Uint8Array | string
115114) : SubmittableExtrinsic < ApiType > {
116115 const _extrinsic = createType ( 'Extrinsic' , extrinsic , { version : api . extrinsicType } ) as unknown as SubmittableExtrinsic < ApiType > ;
117116 const _noStatusCb = type === 'rxjs' ;
@@ -124,35 +123,27 @@ export default function createSubmittableExtrinsic<ApiType> (
124123
125124 function statusObservable ( status : ExtrinsicStatus ) : Observable < ISubmittableResult > {
126125 if ( ! status . isFinalized ) {
127- const result = new SubmittableResult ( { status } ) ;
128-
129- trackingCb && trackingCb ( result ) ;
130-
131- return of ( result ) ;
126+ return of ( new SubmittableResult ( { status } ) ) ;
132127 }
133128
134129 const blockHash = status . asFinalized ;
135130
136131 return combineLatest ( [
137- api . rpc . chain . getBlock ( blockHash ) as Observable < SignedBlock > ,
132+ api . rpc . chain . getBlock ( blockHash ) ,
138133 api . query . system . events . at ( blockHash ) as Observable < Vec < EventRecord > >
139134 ] ) . pipe (
140- map ( ( [ signedBlock , allEvents ] ) : SubmittableResult => {
141- const result = new SubmittableResult ( {
135+ map ( ( [ signedBlock , allEvents ] ) : SubmittableResult =>
136+ new SubmittableResult ( {
142137 events : filterEvents ( _extrinsic . hash , signedBlock , allEvents ) ,
143138 status
144- } ) ;
145-
146- trackingCb && trackingCb ( result ) ;
147-
148- return result ;
149- } )
139+ } )
140+ )
150141 ) ;
151142 }
152143
153144 function sendObservable ( updateId : number = - 1 ) : Observable < Hash > {
154- return ( api . rpc . author
155- . submitExtrinsic ( _extrinsic ) as Observable < Hash > )
145+ return api . rpc . author
146+ . submitExtrinsic ( _extrinsic )
156147 . pipe (
157148 tap ( ( hash ) : void => {
158149 updateSigner ( updateId , hash ) ;
@@ -161,8 +152,8 @@ export default function createSubmittableExtrinsic<ApiType> (
161152 }
162153
163154 function subscribeObservable ( updateId : number = - 1 ) : Observable < ISubmittableResult > {
164- return ( api . rpc . author
165- . submitAndWatchExtrinsic ( _extrinsic ) as Observable < ExtrinsicStatus > )
155+ return api . rpc . author
156+ . submitAndWatchExtrinsic ( _extrinsic )
166157 . pipe (
167158 switchMap ( ( status ) : Observable < ISubmittableResult > =>
168159 statusObservable ( status )
@@ -206,6 +197,20 @@ export default function createSubmittableExtrinsic<ApiType> (
206197 } ) ;
207198 }
208199
200+ function getPrelimState ( address : string , options : Partial < SignerOptions > ) : Observable < [ Index , Header | null ] > {
201+ return combineLatest ( [
202+ // if we have a nonce already, don't retrieve the latest, use what is there
203+ isUndefined ( options . nonce )
204+ ? api . query . system . accountNonce < Index > ( address )
205+ : of ( createType ( 'Index' , options . nonce ) ) ,
206+ // if we have an era provided already or eraLength is <= 0 (immortal)
207+ // don't get the latest block, just pass null, handle in mergeMap
208+ ( isUndefined ( options . era ) || ( isNumber ( options . era ) && options . era > 0 ) )
209+ ? api . rpc . chain . getHeader ( )
210+ : of ( null )
211+ ] ) ;
212+ }
213+
209214 const signOrigin = _extrinsic . sign ;
210215
211216 Object . defineProperties (
@@ -246,71 +251,60 @@ export default function createSubmittableExtrinsic<ApiType> (
246251 let updateId : number | undefined ;
247252
248253 return decorateMethod (
249- ( ) : Observable < Codec > => ( (
250- combineLatest ( [
251- // if we have a nonce already, don't retrieve the latest, use what is there
252- isUndefined ( options . nonce )
253- ? api . query . system . accountNonce < Index > ( address )
254- : of ( createType ( 'Index' , options . nonce ) ) ,
255- // if we have an era provided already or eraLength is <= 0 (immortal)
256- // don't get the latest block, just pass null, handle in mergeMap
257- ( isUndefined ( options . era ) || ( isNumber ( options . era ) && options . era > 0 ) )
258- ? api . rpc . chain . getHeader ( ) as Observable < Header >
259- : of ( null )
260- ] )
261- ) . pipe (
262- first ( ) ,
263- mergeMap ( async ( [ nonce , header ] ) : Promise < void > => {
264- const eraOptions = expandEraOptions ( options , { header, nonce } ) ;
265-
266- // FIXME This is becoming real messy with all the options - way past
267- // "a method should fit on a single screen" stage. (Probably want to
268- // clean this when we remove `api.signer.sign` in the next beta cycle)
269- if ( isKeyringPair ( account ) ) {
270- this . sign ( account , eraOptions ) ;
271- } else if ( api . signer ) {
272- const payload = new SignerPayload ( {
273- ...eraOptions ,
274- address,
275- method : _extrinsic . method ,
276- blockNumber : header ? header . number : 0
277- } ) ;
278-
279- if ( api . signer . signPayload ) {
280- const { id, signature } = await api . signer . signPayload ( payload . toPayload ( ) ) ;
281-
282- // Here we explicitly call `toPayload()` again instead of working with an object
283- // (reference) as passed to the signer. This means that we are sure that the
284- // payload data is not modified from our inputs, but the signer
285- _extrinsic . addSignature ( address , signature , payload . toPayload ( ) ) ;
286- updateId = id ;
287- } else if ( api . signer . signRaw ) {
288- const { id, signature } = await api . signer . signRaw ( payload . toRaw ( ) ) ;
289-
290- // as above, always trust our payload as the signle sourec of truth
291- _extrinsic . addSignature ( address , signature , payload . toPayload ( ) ) ;
292- updateId = id ;
293- } else if ( api . signer . sign ) {
294- console . warn ( 'The Signer.sign interface is deprecated and will be removed in a future version, Swap to using the Signer.signPayload interface instead.' ) ;
295-
296- updateId = await api . signer . sign ( _extrinsic , address , {
254+ ( ) : Observable < Codec > => (
255+ getPrelimState ( address , options ) . pipe (
256+ first ( ) ,
257+ mergeMap ( async ( [ nonce , header ] ) : Promise < void > => {
258+ const eraOptions = expandEraOptions ( options , { header, nonce } ) ;
259+
260+ // FIXME This is becoming real messy with all the options - way past
261+ // "a method should fit on a single screen" stage. (Probably want to
262+ // clean this when we remove `api.signer.sign` in the next beta cycle)
263+ if ( isKeyringPair ( account ) ) {
264+ this . sign ( account , eraOptions ) ;
265+ } else if ( api . signer ) {
266+ const payload = new SignerPayload ( {
297267 ...eraOptions ,
298- blockNumber : header ? header . number . toBn ( ) : new BN ( 0 ) ,
299- genesisHash : api . genesisHash
268+ address,
269+ method : _extrinsic . method ,
270+ blockNumber : header ? header . number : 0
300271 } ) ;
272+
273+ if ( api . signer . signPayload ) {
274+ const { id, signature } = await api . signer . signPayload ( payload . toPayload ( ) ) ;
275+
276+ // Here we explicitly call `toPayload()` again instead of working with an object
277+ // (reference) as passed to the signer. This means that we are sure that the
278+ // payload data is not modified from our inputs, but the signer
279+ _extrinsic . addSignature ( address , signature , payload . toPayload ( ) ) ;
280+ updateId = id ;
281+ } else if ( api . signer . signRaw ) {
282+ const { id, signature } = await api . signer . signRaw ( payload . toRaw ( ) ) ;
283+
284+ // as above, always trust our payload as the signle sourec of truth
285+ _extrinsic . addSignature ( address , signature , payload . toPayload ( ) ) ;
286+ updateId = id ;
287+ } else if ( api . signer . sign ) {
288+ console . warn ( 'The Signer.sign interface is deprecated and will be removed in a future version, Swap to using the Signer.signPayload interface instead.' ) ;
289+
290+ updateId = await api . signer . sign ( _extrinsic , address , {
291+ ...eraOptions ,
292+ blockNumber : header ? header . number . toBn ( ) : new BN ( 0 ) ,
293+ genesisHash : api . genesisHash
294+ } ) ;
295+ } else {
296+ throw new Error ( 'Invalid signer interface' ) ;
297+ }
301298 } else {
302- throw new Error ( 'Invalid signer interface ' ) ;
299+ throw new Error ( 'no signer exists ' ) ;
303300 }
304- } else {
305- throw new Error ( 'no signer exists' ) ;
306- }
307- } ) ,
308- switchMap ( ( ) : Observable < ISubmittableResult > | Observable < Hash > => {
309- return isSubscription
310- ? subscribeObservable ( updateId )
311- : sendObservable ( updateId ) ;
312- } )
313- ) as Observable < Codec > )
301+ } ) ,
302+ switchMap ( ( ) : Observable < ISubmittableResult > | Observable < Hash > => {
303+ return isSubscription
304+ ? subscribeObservable ( updateId )
305+ : sendObservable ( updateId ) ;
306+ } )
307+ ) as Observable < Codec > ) // FIXME This is wrong, SubmittableResult is _not_ a codec
314308 ) ( statusCb ) ;
315309 }
316310 }
0 commit comments