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/**
@@ -1002,14 +1002,17 @@ export class Xlm extends BaseCoin {
10021002 * @param {TransactionParams } txParams - params used to build the tx
10031003 */
10041004 verifyEnableTokenTxOperations ( operations : stellar . Operation [ ] , txParams : TransactionParams ) : void {
1005+ // CHECKED on getTrustlineOperationsOrThrow
10051006 const trustlineOperations = _ . filter ( operations , [ 'type' , 'changeTrust' ] ) as stellar . Operation . ChangeTrust [ ] ;
10061007 if ( trustlineOperations . length !== _ . get ( txParams , 'recipients' , [ ] ) . length ) {
10071008 throw new Error ( 'transaction prebuild does not match expected trustline operations' ) ;
10081009 }
10091010 _ . forEach ( trustlineOperations , ( op : stellar . Operation ) => {
1011+ // CHECKED on verifyTxType
10101012 if ( op . type !== 'changeTrust' ) {
10111013 throw new Error ( 'Invalid asset type' ) ;
10121014 }
1015+ // CHECKED on verifyAssetType
10131016 if ( op . line . getAssetType ( ) === 'liquidity_pool_shares' ) {
10141017 throw new Error ( 'Invalid asset type' ) ;
10151018 }
@@ -1027,16 +1030,42 @@ export class Xlm extends BaseCoin {
10271030 } ) ;
10281031 }
10291032
1033+ getTrustlineOperationsOrThrow (
1034+ operations : stellar . Operation [ ] ,
1035+ txParams : TransactionParams ,
1036+ operationTypePropName : 'trustlines' | 'recipients'
1037+ ) : stellar . Operation . ChangeTrust [ ] {
1038+ const trustlineOperations = operations . filter ( ( op ) => op ?. type === 'changeTrust' ) ;
1039+ if ( trustlineOperations . length !== _ . get ( txParams , operationTypePropName , [ ] ) . length ) {
1040+ throw new Error ( 'transaction prebuild does not match expected trustline operations' ) ;
1041+ }
1042+
1043+ return trustlineOperations ;
1044+ }
1045+
1046+ getTrustlineOperationLineOrThrow ( operation : stellar . Operation ) {
1047+ if ( operation . type === 'changeTrust' && operation . line ) return operation . line ;
1048+ throw new Error ( 'Invalid operation - expected changeTrust operation with line property' ) ;
1049+ }
1050+
1051+ getTrustlineOperationLimitOrThrow ( operation : stellar . Operation ) {
1052+ if ( operation . type === 'changeTrust' && operation . limit ) return operation . limit ;
1053+ throw new Error ( 'Invalid operation - expected changeTrust operation with limit property' ) ;
1054+ }
1055+
1056+ isOperationLineOfAssetType ( line : stellar . Asset | stellar . LiquidityPoolAsset ) : line is stellar . Asset {
1057+ // line should be stellar.Asset, we removed the explicit cast and check the type instead
1058+ if ( ! line . getAssetType ) return false ;
1059+ return line . getAssetType ( ) !== 'liquidity_pool_shares' ;
1060+ }
1061+
10301062 /**
10311063 * Verify that a tx prebuild's operations comply with the original intention
10321064 * @param {stellar.Operation } operations - tx operations
10331065 * @param {TransactionParams } txParams - params used to build the tx
10341066 */
10351067 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- }
1068+ const trustlineOperations = this . getTrustlineOperationsOrThrow ( operations , txParams , 'trustlines' ) ;
10401069 _ . forEach ( trustlineOperations , ( op : stellar . Operation ) => {
10411070 if ( op . type !== 'changeTrust' ) {
10421071 throw new Error ( 'Invalid asset type' ) ;
@@ -1067,6 +1096,95 @@ export class Xlm extends BaseCoin {
10671096 } ) ;
10681097 }
10691098
1099+ getRecipientOrThrow ( txParams : TransactionParams ) : ITransactionRecipient {
1100+ if ( ! txParams . recipients || txParams . recipients . length === 0 )
1101+ throw new Error ( 'Missing recipients on token enablement' ) ;
1102+ if ( txParams . recipients . length > 1 ) throw new Error ( 'Multiple recipients not supported on token enablement' ) ;
1103+ return txParams . recipients [ 0 ] ;
1104+ }
1105+
1106+ getTokenDataOrThrow ( txParams : TransactionParams ) : string {
1107+ const recipient = this . getRecipientOrThrow ( txParams ) ;
1108+ const fullTokenData = recipient . tokenName ;
1109+ if ( ! fullTokenData || fullTokenData === '' ) throw new Error ( 'Missing tokenName on token enablement recipient' ) ;
1110+ return fullTokenData ;
1111+ }
1112+
1113+ private getTokenCodeFromTokenName ( tokenName : string ) : string {
1114+ const tokenCode = tokenName . split ( ':' ) [ 1 ] ?. split ( '-' ) [ 0 ] ?? '' ;
1115+ if ( tokenCode === '' ) throw new Error ( `Invalid tokenName format on token enablement for token ${ tokenName } ` ) ;
1116+ return tokenCode ;
1117+ }
1118+
1119+ private getIssuerFromTokenName ( tokenName : string ) : string {
1120+ const issuer = tokenName . split ( ':' ) [ 1 ] ?. split ( '-' ) [ 1 ] ?? '' ;
1121+ if ( issuer === '' ) throw new Error ( `Invalid issuer format on token enablement for token ${ tokenName } ` ) ;
1122+ return issuer ;
1123+ }
1124+
1125+ // first to verify
1126+ verifyTxType ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1127+ operations . forEach ( ( operation ) => {
1128+ if ( ! operation . type ) throw new Error ( 'Missing operation type on token enablement' ) ;
1129+ if ( operation . type !== 'changeTrust' )
1130+ throw new Error ( `Invalid operation on token enablement: expected changeTrust, got ${ operation . type } ` ) ;
1131+ } ) ;
1132+ }
1133+
1134+ verifyAssetType ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1135+ operations . forEach ( ( operation ) => {
1136+ const line = this . getTrustlineOperationLineOrThrow ( operation ) ;
1137+ const assetType = line . getAssetType ( ) ;
1138+ if ( assetType === 'liquidity_pool_shares' )
1139+ throw new Error ( `Invalid asset type on token enablement: got ${ assetType } ` ) ;
1140+ } ) ;
1141+ }
1142+
1143+ verifyTokenIssuer ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1144+ const fullTokenData = this . getTokenDataOrThrow ( txParams ) ;
1145+ const expectedIssuer = this . getIssuerFromTokenName ( fullTokenData ) ;
1146+
1147+ operations . forEach ( ( operation ) => {
1148+ const line = this . getTrustlineOperationLineOrThrow ( operation ) ;
1149+ if ( ! ( 'issuer' in line ) ) throw new Error ( 'Missing issuer on token enablement operation' ) ;
1150+ if ( line . issuer !== expectedIssuer )
1151+ throw new Error ( `Invalid issuer on token enablement operation: expected ${ expectedIssuer } , got ${ line . issuer } ` ) ;
1152+ } ) ;
1153+ }
1154+
1155+ verifyTokenName ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1156+ const fullTokenData = this . getTokenDataOrThrow ( txParams ) ;
1157+ const expectedTokenCode = this . getTokenCodeFromTokenName ( fullTokenData ) ;
1158+
1159+ operations . forEach ( ( operation ) => {
1160+ const line = this . getTrustlineOperationLineOrThrow ( operation ) ;
1161+ if ( ! ( 'code' in line ) ) throw new Error ( 'Missing token code on token enablement operation' ) ;
1162+ if ( line . code === '' ) throw new Error ( 'Empty token code on token enablement operation' ) ;
1163+ if ( line . code !== expectedTokenCode )
1164+ throw new Error (
1165+ `Invalid token code on token enablement operation: expected ${ expectedTokenCode } , got ${ line . code } `
1166+ ) ;
1167+ } ) ;
1168+ }
1169+
1170+ verifyTokenLimits ( txParams : TransactionParams , operations : stellar . Operation [ ] ) : void {
1171+ const recipient = this . getRecipientOrThrow ( txParams ) ;
1172+
1173+ operations . forEach ( ( operation ) => {
1174+ // trustline params use limits in base units
1175+ const line = this . getTrustlineOperationLineOrThrow ( operation ) ;
1176+ const limit = this . getTrustlineOperationLimitOrThrow ( operation ) ; // line should be stellar.Asset
1177+ if ( ! this . isOperationLineOfAssetType ( line ) ) throw new Error ( 'Invalid asset type' ) ;
1178+ const operationLimitBaseUnits = this . bigUnitsToBaseUnits ( limit ) ;
1179+ const operationToken = this . getTokenNameFromStellarAsset ( line ) ;
1180+
1181+ // Enable token limit is set to Xlm.maxTrustlineLimit by default
1182+ if ( recipient . tokenName !== operationToken || operationLimitBaseUnits !== Xlm . maxTrustlineLimit ) {
1183+ throw new Error ( 'Token limit must be set to max limit on enable token operations' ) ;
1184+ }
1185+ } ) ;
1186+ }
1187+
10701188 /**
10711189 * Verify that a transaction prebuild complies with the original intention
10721190 *
@@ -1099,8 +1217,14 @@ export class Xlm extends BaseCoin {
10991217 ( operation ) => operation . type === 'createAccount' || operation . type === 'payment'
11001218 ) ;
11011219
1102- if ( txParams . type === 'enabletoken' ) {
1103- this . verifyEnableTokenTxOperations ( tx . operations , txParams ) ;
1220+ if ( txParams . type === 'enabletoken' && verification . verifyTokenEnablement ) {
1221+ const trustlineOperations = this . getTrustlineOperationsOrThrow ( tx . operations , txParams , 'recipients' ) ;
1222+ this . verifyTxType ( txParams , trustlineOperations ) ;
1223+
1224+ this . verifyAssetType ( txParams , trustlineOperations ) ;
1225+ this . verifyTokenIssuer ( txParams , trustlineOperations ) ;
1226+ this . verifyTokenName ( txParams , trustlineOperations ) ;
1227+ this . verifyTokenLimits ( txParams , trustlineOperations ) ;
11041228 } else if ( txParams . type === 'trustline' ) {
11051229 this . verifyTrustlineTxOperations ( tx . operations , txParams ) ;
11061230 } else {
0 commit comments