1
1
import assert from 'assert' ;
2
+ import { BigNumber } from 'bignumber.js' ;
2
3
import * as _ from 'lodash' ;
3
4
import * as querystring from 'querystring' ;
4
- import * as url from 'url' ;
5
- import * as request from 'superagent' ;
6
5
import * 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 ' ;
9
8
import { KeyPair as StellarKeyPair } from './lib/keyPair' ;
9
+ import * as Utils from './lib/utils' ;
10
10
11
+ import { toBitgoRequest } from '@bitgo/sdk-api' ;
11
12
import {
13
+ AuditDecryptedKeyParams ,
12
14
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 ,
13
22
BitGoBase ,
14
23
checkKrsProvider ,
15
24
common ,
@@ -19,26 +28,17 @@ import {
19
28
ITransactionRecipient ,
20
29
KeyIndices ,
21
30
KeyPair ,
31
+ MultisigType ,
32
+ multisigTypes ,
33
+ NotSupported ,
22
34
ParsedTransaction ,
23
35
ParseTransactionOptions ,
24
36
promiseProps ,
25
- SignTransactionOptions as BaseSignTransactionOptions ,
26
37
StellarFederationUserNotFoundError ,
27
38
TokenEnablementConfig ,
28
- TransactionExplanation as BaseTransactionExplanation ,
29
- TransactionParams as BaseTransactionParams ,
30
- TransactionPrebuild as BaseTransactionPrebuild ,
31
- TransactionRecipient as BaseTransactionOutput ,
32
39
UnexpectedAddressError ,
33
- VerifyAddressOptions as BaseVerifyAddressOptions ,
34
- VerifyTransactionOptions as BaseVerifyTransactionOptions ,
35
40
Wallet ,
36
- NotSupported ,
37
- MultisigType ,
38
- multisigTypes ,
39
- AuditDecryptedKeyParams ,
40
41
} from '@bitgo/sdk-core' ;
41
- import { toBitgoRequest } from '@bitgo/sdk-api' ;
42
42
import { getStellarKeys } from './getStellarKeys' ;
43
43
44
44
/**
@@ -996,35 +996,37 @@ export class Xlm extends BaseCoin {
996
996
} as any ;
997
997
}
998
998
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 ) {
1007
1006
throw new Error ( 'transaction prebuild does not match expected trustline operations' ) ;
1008
1007
}
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' ;
1028
1030
}
1029
1031
1030
1032
/**
@@ -1033,10 +1035,7 @@ export class Xlm extends BaseCoin {
1033
1035
* @param {TransactionParams } txParams - params used to build the tx
1034
1036
*/
1035
1037
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' ) ;
1040
1039
_ . forEach ( trustlineOperations , ( op : stellar . Operation ) => {
1041
1040
if ( op . type !== 'changeTrust' ) {
1042
1041
throw new Error ( 'Invalid asset type' ) ;
@@ -1067,6 +1066,98 @@ export class Xlm extends BaseCoin {
1067
1066
} ) ;
1068
1067
}
1069
1068
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
+
1070
1161
/**
1071
1162
* Verify that a transaction prebuild complies with the original intention
1072
1163
*
@@ -1099,8 +1190,13 @@ export class Xlm extends BaseCoin {
1099
1190
( operation ) => operation . type === 'createAccount' || operation . type === 'payment'
1100
1191
) ;
1101
1192
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 ) ;
1104
1200
} else if ( txParams . type === 'trustline' ) {
1105
1201
this . verifyTrustlineTxOperations ( tx . operations , txParams ) ;
1106
1202
} else {
0 commit comments