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,33 @@ 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+ getTrustlineOperationLineOrThrow ( operation : stellar . Operation ) : stellar . Asset | stellar . LiquidityPoolAsset {
1013+ if ( operation . type === 'changeTrust' && operation . line ) return operation . line ;
1014+ throw new Error ( 'Invalid operation - expected changeTrust operation with line property' ) ;
1015+ }
1016+
1017+ getTrustlineOperationLimitOrThrow ( operation : stellar . Operation ) : string {
1018+ if ( operation . type === 'changeTrust' && operation . limit ) return operation . limit ;
1019+ throw new Error ( 'Invalid operation - expected changeTrust operation with limit property' ) ;
1020+ }
1021+
1022+ isOperationLineOfAssetType ( line : stellar . Asset | stellar . LiquidityPoolAsset ) : line is stellar . Asset {
1023+ // line should be stellar.Asset, we removed the explicit cast and check the type instead
1024+ if ( ! line . getAssetType ) return false ;
1025+ return line . getAssetType ( ) !== 'liquidity_pool_shares' ;
10281026 }
10291027
10301028 /**
@@ -1033,10 +1031,7 @@ export class Xlm extends BaseCoin {
10331031 * @param {TransactionParams } txParams - params used to build the tx
10341032 */
10351033 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- }
1034+ const trustlineOperations = this . getTrustlineOperationsOrThrow ( operations , txParams , 'trustlines' ) ;
10401035 _ . forEach ( trustlineOperations , ( op : stellar . Operation ) => {
10411036 if ( op . type !== 'changeTrust' ) {
10421037 throw new Error ( 'Invalid asset type' ) ;
@@ -1067,6 +1062,95 @@ export class Xlm extends BaseCoin {
10671062 } ) ;
10681063 }
10691064
1065+ getRecipientOrThrow ( txParams : TransactionParams ) : ITransactionRecipient {
1066+ if ( ! txParams . recipients || txParams . recipients . length === 0 )
1067+ throw new Error ( 'Missing recipients on token enablement' ) ;
1068+ if ( txParams . recipients . length > 1 ) throw new Error ( 'Multiple recipients not supported on token enablement' ) ;
1069+ return txParams . recipients [ 0 ] ;
1070+ }
1071+
1072+ getTokenDataOrThrow ( txParams : TransactionParams ) : string {
1073+ const recipient = this . getRecipientOrThrow ( txParams ) ;
1074+ const fullTokenData = recipient . tokenName ;
1075+ if ( ! fullTokenData || fullTokenData === '' ) throw new Error ( 'Missing tokenName on token enablement recipient' ) ;
1076+ return fullTokenData ;
1077+ }
1078+
1079+ private getTokenCodeFromTokenName ( tokenName : string ) : string {
1080+ const tokenCode = tokenName . split ( ':' ) [ 1 ] ?. split ( '-' ) [ 0 ] ?? '' ;
1081+ if ( tokenCode === '' ) throw new Error ( `Invalid tokenName format on token enablement for token ${ tokenName } ` ) ;
1082+ return tokenCode ;
1083+ }
1084+
1085+ private getIssuerFromTokenName ( tokenName : string ) : string {
1086+ const issuer = tokenName . split ( ':' ) [ 1 ] ?. split ( '-' ) [ 1 ] ?? '' ;
1087+ if ( issuer === '' ) throw new Error ( `Invalid issuer format on token enablement for token ${ tokenName } ` ) ;
1088+ return issuer ;
1089+ }
1090+
1091+ // first to verify
1092+ verifyTxType ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1093+ operations . forEach ( ( operation ) => {
1094+ if ( ! operation . type ) throw new Error ( 'Missing operation type on token enablement' ) ;
1095+ if ( operation . type !== 'changeTrust' )
1096+ throw new Error ( `Invalid operation on token enablement: expected changeTrust, got ${ operation . type } ` ) ;
1097+ } ) ;
1098+ }
1099+
1100+ verifyAssetType ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1101+ operations . forEach ( ( operation ) => {
1102+ const line = this . getTrustlineOperationLineOrThrow ( operation ) ;
1103+ const assetType = line . getAssetType ( ) ;
1104+ if ( assetType === 'liquidity_pool_shares' )
1105+ throw new Error ( `Invalid asset type on token enablement: got ${ assetType } ` ) ;
1106+ } ) ;
1107+ }
1108+
1109+ verifyTokenIssuer ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1110+ const fullTokenData = this . getTokenDataOrThrow ( txParams ) ;
1111+ const expectedIssuer = this . getIssuerFromTokenName ( fullTokenData ) ;
1112+
1113+ operations . forEach ( ( operation ) => {
1114+ const line = this . getTrustlineOperationLineOrThrow ( operation ) ;
1115+ if ( ! ( 'issuer' in line ) ) throw new Error ( 'Missing issuer on token enablement operation' ) ;
1116+ if ( line . issuer !== expectedIssuer )
1117+ throw new Error ( `Invalid issuer on token enablement operation: expected ${ expectedIssuer } , got ${ line . issuer } ` ) ;
1118+ } ) ;
1119+ }
1120+
1121+ verifyTokenName ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1122+ const fullTokenData = this . getTokenDataOrThrow ( txParams ) ;
1123+ const expectedTokenCode = this . getTokenCodeFromTokenName ( fullTokenData ) ;
1124+
1125+ operations . forEach ( ( operation ) => {
1126+ const line = this . getTrustlineOperationLineOrThrow ( operation ) ;
1127+ if ( ! ( 'code' in line ) ) throw new Error ( 'Missing token code on token enablement operation' ) ;
1128+ if ( line . code === '' ) throw new Error ( 'Empty token code on token enablement operation' ) ;
1129+ if ( line . code !== expectedTokenCode )
1130+ throw new Error (
1131+ `Invalid token code on token enablement operation: expected ${ expectedTokenCode } , got ${ line . code } `
1132+ ) ;
1133+ } ) ;
1134+ }
1135+
1136+ verifyTokenLimits ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1137+ const recipient = this . getRecipientOrThrow ( txParams ) ;
1138+
1139+ operations . forEach ( ( operation ) => {
1140+ // trustline params use limits in base units
1141+ const line = this . getTrustlineOperationLineOrThrow ( operation ) ;
1142+ const limit = this . getTrustlineOperationLimitOrThrow ( operation ) ; // line should be stellar.Asset
1143+ if ( ! this . isOperationLineOfAssetType ( line ) ) throw new Error ( 'Invalid asset type' ) ;
1144+ const operationLimitBaseUnits = this . bigUnitsToBaseUnits ( limit ) ;
1145+ const operationToken = this . getTokenNameFromStellarAsset ( line ) ;
1146+
1147+ // Enable token limit is set to Xlm.maxTrustlineLimit by default
1148+ if ( recipient . tokenName !== operationToken || operationLimitBaseUnits !== Xlm . maxTrustlineLimit ) {
1149+ throw new Error ( 'Token limit must be set to max limit on enable token operations' ) ;
1150+ }
1151+ } ) ;
1152+ }
1153+
10701154 /**
10711155 * Verify that a transaction prebuild complies with the original intention
10721156 *
@@ -1099,8 +1183,14 @@ export class Xlm extends BaseCoin {
10991183 ( operation ) => operation . type === 'createAccount' || operation . type === 'payment'
11001184 ) ;
11011185
1102- if ( txParams . type === 'enabletoken' ) {
1103- this . verifyEnableTokenTxOperations ( tx . operations , txParams ) ;
1186+ if ( txParams . type === 'enabletoken' && verification . verifyTokenEnablement ) {
1187+ const trustlineOperations = this . getTrustlineOperationsOrThrow ( tx . operations , txParams , 'recipients' ) ;
1188+ this . verifyTxType ( txParams , trustlineOperations ) ;
1189+
1190+ this . verifyAssetType ( txParams , trustlineOperations ) ;
1191+ this . verifyTokenIssuer ( txParams , trustlineOperations ) ;
1192+ this . verifyTokenName ( txParams , trustlineOperations ) ;
1193+ this . verifyTokenLimits ( txParams , trustlineOperations ) ;
11041194 } else if ( txParams . type === 'trustline' ) {
11051195 this . verifyTrustlineTxOperations ( tx . operations , txParams ) ;
11061196 } else {
0 commit comments