11import assert from 'assert' ;
2+ import { BigNumber } from 'bignumber.js' ;
23import * as _ from 'lodash' ;
34import * as querystring from 'querystring' ;
4- import * as url from 'url' ;
5- import * as request from 'superagent' ;
65import * as stellar from 'stellar-sdk' ;
7- import { BigNumber } from 'bignumber.js ' ;
8- import * as Utils from './lib/utils ' ;
6+ import * as request from 'superagent ' ;
7+ import * as url from 'url ' ;
98import { KeyPair as StellarKeyPair } from './lib/keyPair' ;
9+ import * as Utils from './lib/utils' ;
1010
11+ import { toBitgoRequest } from '@bitgo/sdk-api' ;
1112import {
13+ AuditDecryptedKeyParams ,
1214 BaseCoin ,
15+ SignTransactionOptions as BaseSignTransactionOptions ,
16+ TransactionExplanation as BaseTransactionExplanation ,
17+ TransactionRecipient as BaseTransactionOutput ,
18+ TransactionParams as BaseTransactionParams ,
19+ TransactionPrebuild as BaseTransactionPrebuild ,
20+ VerifyAddressOptions as BaseVerifyAddressOptions ,
21+ VerifyTransactionOptions as BaseVerifyTransactionOptions ,
1322 BitGoBase ,
1423 checkKrsProvider ,
1524 common ,
@@ -19,26 +28,17 @@ import {
1928 ITransactionRecipient ,
2029 KeyIndices ,
2130 KeyPair ,
31+ MultisigType ,
32+ multisigTypes ,
33+ NotSupported ,
2234 ParsedTransaction ,
2335 ParseTransactionOptions ,
2436 promiseProps ,
25- SignTransactionOptions as BaseSignTransactionOptions ,
2637 StellarFederationUserNotFoundError ,
2738 TokenEnablementConfig ,
28- TransactionExplanation as BaseTransactionExplanation ,
29- TransactionParams as BaseTransactionParams ,
30- TransactionPrebuild as BaseTransactionPrebuild ,
31- TransactionRecipient as BaseTransactionOutput ,
3239 UnexpectedAddressError ,
33- VerifyAddressOptions as BaseVerifyAddressOptions ,
34- VerifyTransactionOptions as BaseVerifyTransactionOptions ,
3540 Wallet ,
36- NotSupported ,
37- MultisigType ,
38- multisigTypes ,
39- AuditDecryptedKeyParams ,
4041} from '@bitgo/sdk-core' ;
41- import { toBitgoRequest } from '@bitgo/sdk-api' ;
4242import { getStellarKeys } from './getStellarKeys' ;
4343
4444/**
@@ -996,35 +996,37 @@ export class Xlm extends BaseCoin {
996996 } as any ;
997997 }
998998
999- /**
1000- * Verify that a tx prebuild's operations comply with the original intention
1001- * @param {stellar.Operation } operations - tx operations
1002- * @param {TransactionParams } txParams - params used to build the tx
1003- */
1004- verifyEnableTokenTxOperations ( operations : stellar . Operation [ ] , txParams : TransactionParams ) : void {
1005- const trustlineOperations = _ . filter ( operations , [ 'type' , 'changeTrust' ] ) as stellar . Operation . ChangeTrust [ ] ;
1006- if ( trustlineOperations . length !== _ . get ( txParams , 'recipients' , [ ] ) . length ) {
999+ getTrustlineOperationsOrThrow (
1000+ operations : stellar . Operation [ ] ,
1001+ txParams : TransactionParams ,
1002+ operationTypePropName : 'trustlines' | 'recipients'
1003+ ) : stellar . Operation . ChangeTrust [ ] {
1004+ const trustlineOperations = operations . filter ( ( op ) => op ?. type === 'changeTrust' ) ;
1005+ if ( trustlineOperations . length !== _ . get ( txParams , operationTypePropName , [ ] ) . length ) {
10071006 throw new Error ( 'transaction prebuild does not match expected trustline operations' ) ;
10081007 }
1009- _ . forEach ( trustlineOperations , ( op : stellar . Operation ) => {
1010- if ( op . type !== 'changeTrust' ) {
1011- throw new Error ( 'Invalid asset type' ) ;
1012- }
1013- if ( op . line . getAssetType ( ) === 'liquidity_pool_shares' ) {
1014- throw new Error ( 'Invalid asset type' ) ;
1015- }
1016- const asset = op . line as stellar . Asset ;
1017- const opToken = this . getTokenNameFromStellarAsset ( asset ) ;
1018- const tokenTrustline = _ . find ( txParams . recipients , ( recipient ) => {
1019- // trustline params use limits in base units
1020- const opLimitBaseUnits = this . bigUnitsToBaseUnits ( op . limit ) ;
1021- // Enable token limit is set to Xlm.maxTrustlineLimit by default
1022- return recipient . tokenName === opToken && opLimitBaseUnits === Xlm . maxTrustlineLimit ;
1023- } ) ;
1024- if ( ! tokenTrustline ) {
1025- throw new Error ( 'transaction prebuild does not match expected trustline tokens' ) ;
1026- }
1027- } ) ;
1008+
1009+ return trustlineOperations ;
1010+ }
1011+
1012+ isChangeTrustOperation ( operation : stellar . Operation ) : operation is stellar . Operation . ChangeTrust {
1013+ return operation . type && operation . type === 'changeTrust' ;
1014+ }
1015+
1016+ getTrustlineOperationLineOrThrow ( operation : stellar . Operation ) : stellar . Asset | stellar . LiquidityPoolAsset {
1017+ if ( this . isChangeTrustOperation ( operation ) && operation . line ) return operation . line ;
1018+ throw new Error ( 'Invalid operation - expected changeTrust operation with line property' ) ;
1019+ }
1020+
1021+ getTrustlineOperationLimitOrThrow ( operation : stellar . Operation ) : string {
1022+ if ( this . isChangeTrustOperation ( operation ) && operation . limit ) return operation . limit ;
1023+ throw new Error ( 'Invalid operation - expected changeTrust operation with limit property' ) ;
1024+ }
1025+
1026+ isOperationLineOfAssetType ( line : stellar . Asset | stellar . LiquidityPoolAsset ) : line is stellar . Asset {
1027+ // line should be stellar.Asset, we removed the explicit cast and check the type instead
1028+ if ( ! line . getAssetType ) return false ;
1029+ return line . getAssetType ( ) !== 'liquidity_pool_shares' ;
10281030 }
10291031
10301032 /**
@@ -1033,10 +1035,7 @@ export class Xlm extends BaseCoin {
10331035 * @param {TransactionParams } txParams - params used to build the tx
10341036 */
10351037 verifyTrustlineTxOperations ( operations : stellar . Operation [ ] , txParams : TransactionParams ) : void {
1036- const trustlineOperations = _ . filter ( operations , [ 'type' , 'changeTrust' ] ) as stellar . Operation . ChangeTrust [ ] ;
1037- if ( trustlineOperations . length !== _ . get ( txParams , 'trustlines' , [ ] ) . length ) {
1038- throw new Error ( 'transaction prebuild does not match expected trustline operations' ) ;
1039- }
1038+ const trustlineOperations = this . getTrustlineOperationsOrThrow ( operations , txParams , 'trustlines' ) ;
10401039 _ . forEach ( trustlineOperations , ( op : stellar . Operation ) => {
10411040 if ( op . type !== 'changeTrust' ) {
10421041 throw new Error ( 'Invalid asset type' ) ;
@@ -1067,6 +1066,98 @@ export class Xlm extends BaseCoin {
10671066 } ) ;
10681067 }
10691068
1069+ getRecipientOrThrow ( txParams : TransactionParams ) : ITransactionRecipient {
1070+ if ( ! txParams . recipients || txParams . recipients . length === 0 )
1071+ throw new Error ( 'Missing recipients on token enablement' ) ;
1072+ if ( txParams . recipients . length > 1 ) throw new Error ( 'Multiple recipients not supported on token enablement' ) ;
1073+ return txParams . recipients [ 0 ] ;
1074+ }
1075+
1076+ getTokenDataOrThrow ( txParams : TransactionParams ) : string {
1077+ const recipient = this . getRecipientOrThrow ( txParams ) ;
1078+ const fullTokenData = recipient . tokenName ;
1079+ if ( ! fullTokenData || fullTokenData === '' ) throw new Error ( 'Missing tokenName on token enablement recipient' ) ;
1080+ return fullTokenData ;
1081+ }
1082+
1083+ private getTokenCodeFromTokenName ( tokenName : string ) : string {
1084+ const tokenCode = tokenName . split ( ':' ) [ 1 ] ?. split ( '-' ) [ 0 ] ?? '' ;
1085+ if ( tokenCode === '' ) throw new Error ( `Invalid tokenName format on token enablement for token ${ tokenName } ` ) ;
1086+ return tokenCode ;
1087+ }
1088+
1089+ private getIssuerFromTokenName ( tokenName : string ) : string {
1090+ const issuer = tokenName . split ( ':' ) [ 1 ] ?. split ( '-' ) [ 1 ] ?? '' ;
1091+ if ( issuer === '' ) throw new Error ( `Invalid issuer format on token enablement for token ${ tokenName } ` ) ;
1092+ return issuer ;
1093+ }
1094+
1095+ verifyTxType ( operations : stellar . Operation [ ] ) : void {
1096+ operations . forEach ( ( operation ) => {
1097+ if ( ! this . isChangeTrustOperation ( operation ) )
1098+ throw new Error (
1099+ ! operation . type
1100+ ? 'Missing operation type on token enablements'
1101+ : `Invalid operation on token enablement: expected changeTrust, got ${ operation . type } `
1102+ ) ;
1103+ } ) ;
1104+ }
1105+
1106+ verifyAssetType ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1107+ operations . forEach ( ( operation ) => {
1108+ const line = this . getTrustlineOperationLineOrThrow ( operation ) ;
1109+ if ( ! this . isOperationLineOfAssetType ( line ) ) {
1110+ const assetType = line . getAssetType ( ) ;
1111+ throw new Error ( `Invalid asset type on token enablement: got ${ assetType } ` ) ;
1112+ }
1113+ } ) ;
1114+ }
1115+
1116+ verifyTokenIssuer ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1117+ const fullTokenData = this . getTokenDataOrThrow ( txParams ) ;
1118+ const expectedIssuer = this . getIssuerFromTokenName ( fullTokenData ) ;
1119+
1120+ operations . forEach ( ( operation ) => {
1121+ const line = this . getTrustlineOperationLineOrThrow ( operation ) ;
1122+ if ( ! ( 'issuer' in line ) ) throw new Error ( 'Missing issuer on token enablement operation' ) ;
1123+ if ( line . issuer !== expectedIssuer )
1124+ throw new Error ( `Invalid issuer on token enablement operation: expected ${ expectedIssuer } , got ${ line . issuer } ` ) ;
1125+ } ) ;
1126+ }
1127+
1128+ verifyTokenName ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1129+ const fullTokenData = this . getTokenDataOrThrow ( txParams ) ;
1130+ const expectedTokenCode = this . getTokenCodeFromTokenName ( fullTokenData ) ;
1131+
1132+ operations . forEach ( ( operation ) => {
1133+ const line = this . getTrustlineOperationLineOrThrow ( operation ) ;
1134+ if ( ! ( 'code' in line ) ) throw new Error ( 'Missing token code on token enablement operation' ) ;
1135+ if ( line . code === '' ) throw new Error ( 'Empty token code on token enablement operation' ) ;
1136+ if ( line . code !== expectedTokenCode )
1137+ throw new Error (
1138+ `Invalid token code on token enablement operation: expected ${ expectedTokenCode } , got ${ line . code } `
1139+ ) ;
1140+ } ) ;
1141+ }
1142+
1143+ verifyTokenLimits ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1144+ const recipient = this . getRecipientOrThrow ( txParams ) ;
1145+
1146+ operations . forEach ( ( operation ) => {
1147+ // trustline params use limits in base units
1148+ const line = this . getTrustlineOperationLineOrThrow ( operation ) ;
1149+ const limit = this . getTrustlineOperationLimitOrThrow ( operation ) ; // line should be stellar.Asset
1150+ if ( ! this . isOperationLineOfAssetType ( line ) ) throw new Error ( 'Invalid asset type' ) ;
1151+ const operationLimitBaseUnits = this . bigUnitsToBaseUnits ( limit ) ;
1152+ const operationToken = this . getTokenNameFromStellarAsset ( line ) ;
1153+
1154+ // Enable token limit is set to Xlm.maxTrustlineLimit by default
1155+ if ( recipient . tokenName !== operationToken || operationLimitBaseUnits !== Xlm . maxTrustlineLimit ) {
1156+ throw new Error ( 'Token limit must be set to max limit on enable token operations' ) ;
1157+ }
1158+ } ) ;
1159+ }
1160+
10701161 /**
10711162 * Verify that a transaction prebuild complies with the original intention
10721163 *
@@ -1099,8 +1190,13 @@ export class Xlm extends BaseCoin {
10991190 ( operation ) => operation . type === 'createAccount' || operation . type === 'payment'
11001191 ) ;
11011192
1102- if ( txParams . type === 'enabletoken' ) {
1103- this . verifyEnableTokenTxOperations ( tx . operations , txParams ) ;
1193+ if ( txParams . type === 'enabletoken' && verification . verifyTokenEnablement ) {
1194+ const trustlineOperations = this . getTrustlineOperationsOrThrow ( tx . operations , txParams , 'recipients' ) ;
1195+ this . verifyTxType ( trustlineOperations ) ;
1196+ this . verifyAssetType ( txParams , trustlineOperations ) ;
1197+ this . verifyTokenIssuer ( txParams , trustlineOperations ) ;
1198+ this . verifyTokenName ( txParams , trustlineOperations ) ;
1199+ this . verifyTokenLimits ( txParams , trustlineOperations ) ;
11041200 } else if ( txParams . type === 'trustline' ) {
11051201 this . verifyTrustlineTxOperations ( tx . operations , txParams ) ;
11061202 } else {
0 commit comments