33 BaseCoin ,
44 BitGoBase ,
55 KeyPair ,
6- ParseTransactionOptions ,
76 ParsedTransaction ,
87 SignTransactionOptions ,
98 SignedTransaction ,
@@ -13,12 +12,23 @@ import {
1312 MPCAlgorithm ,
1413 TssVerifyAddressOptions ,
1514 MPCType ,
15+ PopulatedIntent ,
16+ PrebuildTransactionWithIntentOptions ,
1617 verifyEddsaTssWalletAddress ,
1718} from '@bitgo/sdk-core' ;
18- import { BaseCoin as StaticsBaseCoin , CoinFamily } from '@bitgo/statics' ;
19+ import { BaseCoin as StaticsBaseCoin , CoinFamily , coins } from '@bitgo/statics' ;
1920import utils from './lib/utils' ;
20- import { KeyPair as IotaKeyPair } from './lib' ;
21+ import { KeyPair as IotaKeyPair , Transaction , TransactionBuilderFactory } from './lib' ;
2122import { auditEddsaPrivateKey } from '@bitgo/sdk-lib-mpc' ;
23+ import BigNumber from 'bignumber.js' ;
24+ import * as _ from 'lodash' ;
25+ import {
26+ ExplainTransactionOptions ,
27+ IotaParseTransactionOptions ,
28+ TransactionExplanation ,
29+ TransferTxData ,
30+ } from './lib/iface' ;
31+ import { TransferTransaction } from './lib/transferTransaction' ;
2232
2333export class Iota extends BaseCoin {
2434 protected readonly _staticsCoin : Readonly < StaticsBaseCoin > ;
@@ -77,12 +87,44 @@ export class Iota extends BaseCoin {
7787 return utils . isValidAddress ( address ) ;
7888 }
7989
90+ /**
91+ * @inheritDoc
92+ */
93+ async explainTransaction ( params : ExplainTransactionOptions ) : Promise < TransactionExplanation > {
94+ const rawTx = params . txBase64 ;
95+ if ( ! rawTx ) {
96+ throw new Error ( 'missing required tx prebuild property txBase64' ) ;
97+ }
98+ const transaction = await this . rebuildTransaction ( rawTx ) ;
99+ if ( ! transaction ) {
100+ throw new Error ( 'failed to explain transaction' ) ;
101+ }
102+ return transaction . explainTransaction ( ) ;
103+ }
104+
80105 /**
81106 * Verifies that a transaction prebuild complies with the original intention
82107 * @param params
83108 */
84109 async verifyTransaction ( params : VerifyTransactionOptions ) : Promise < boolean > {
85- // TODO: Add IOTA-specific transaction verification logic
110+ const { txPrebuild : txPrebuild , txParams : txParams } = params ;
111+ const rawTx = txPrebuild . txBase64 ;
112+ if ( ! rawTx ) {
113+ throw new Error ( 'missing required tx prebuild property txBase64' ) ;
114+ }
115+ const transaction = await this . rebuildTransaction ( rawTx ) ;
116+ if ( ! transaction ) {
117+ throw new Error ( 'failed to verify transaction' ) ;
118+ }
119+ if ( txParams . recipients !== undefined ) {
120+ if ( ! ( transaction instanceof TransferTransaction ) ) {
121+ throw new Error ( 'Tx not a transfer transaction' ) ;
122+ }
123+ const txData = transaction . toJson ( ) as TransferTxData ;
124+ if ( ! txData . recipients || ! _ . isEqual ( txParams . recipients , txData . recipients ) ) {
125+ throw new Error ( 'Tx recipients does not match with expected txParams recipients' ) ;
126+ }
127+ }
86128 return true ;
87129 }
88130
@@ -102,9 +144,51 @@ export class Iota extends BaseCoin {
102144 * Parse a transaction
103145 * @param params
104146 */
105- async parseTransaction ( params : ParseTransactionOptions ) : Promise < ParsedTransaction > {
106- // TODO: Add IOTA-specific transaction parsing logic
107- return { } ;
147+ async parseTransaction ( params : IotaParseTransactionOptions ) : Promise < ParsedTransaction > {
148+ const transactionExplanation = await this . explainTransaction ( { txBase64 : params . txBase64 } ) ;
149+
150+ if ( ! transactionExplanation ) {
151+ throw new Error ( 'Invalid transaction' ) ;
152+ }
153+
154+ let fee = new BigNumber ( 0 ) ;
155+
156+ if ( transactionExplanation . outputs . length <= 0 ) {
157+ return {
158+ inputs : [ ] ,
159+ outputs : [ ] ,
160+ fee,
161+ } ;
162+ }
163+
164+ const senderAddress = transactionExplanation . outputs [ 0 ] . address ;
165+ if ( transactionExplanation . fee . fee !== '' ) {
166+ fee = new BigNumber ( transactionExplanation . fee . fee ) ;
167+ }
168+
169+ // assume 1 sender, who is also the fee payer
170+ const inputs = [
171+ {
172+ address : senderAddress ,
173+ amount : new BigNumber ( transactionExplanation . outputAmount ) . plus ( fee ) . toFixed ( ) ,
174+ } ,
175+ ] ;
176+
177+ const outputs : {
178+ address : string ;
179+ amount : string ;
180+ } [ ] = transactionExplanation . outputs . map ( ( output ) => {
181+ return {
182+ address : output . address ,
183+ amount : new BigNumber ( output . amount ) . toFixed ( ) ,
184+ } ;
185+ } ) ;
186+
187+ return {
188+ inputs,
189+ outputs,
190+ fee,
191+ } ;
108192 }
109193
110194 /**
@@ -149,4 +233,30 @@ export class Iota extends BaseCoin {
149233 }
150234 auditEddsaPrivateKey ( prv , publicKey ?? '' ) ;
151235 }
236+
237+ /** @inheritDoc */
238+ async getSignablePayload ( serializedTx : string ) : Promise < Buffer > {
239+ const rebuiltTransaction = await this . rebuildTransaction ( serializedTx ) ;
240+ return rebuiltTransaction . signablePayload ;
241+ }
242+
243+ /** inherited doc */
244+ setCoinSpecificFieldsInIntent ( intent : PopulatedIntent , params : PrebuildTransactionWithIntentOptions ) : void {
245+ intent . unspents = params . unspents ;
246+ }
247+
248+ private getTxBuilderFactory ( ) : TransactionBuilderFactory {
249+ return new TransactionBuilderFactory ( coins . get ( this . getChain ( ) ) ) ;
250+ }
251+
252+ private async rebuildTransaction ( txHex : string ) : Promise < Transaction > {
253+ const txBuilderFactory = this . getTxBuilderFactory ( ) ;
254+ try {
255+ const txBuilder = txBuilderFactory . from ( txHex ) ;
256+ txBuilder . transaction . isSimulateTx = false ;
257+ return ( await txBuilder . build ( ) ) as Transaction ;
258+ } catch {
259+ throw new Error ( 'Failed to rebuild transaction' ) ;
260+ }
261+ }
152262}
0 commit comments