@@ -42,6 +42,7 @@ import { getDerivationPath } from '@bitgo/sdk-lib-mpc';
42
42
import { bip32 } from '@bitgo/secp256k1' ;
43
43
import {
44
44
BaseCoin as StaticsBaseCoin ,
45
+ CoinFeature ,
45
46
CoinMap ,
46
47
coins ,
47
48
EthereumNetwork as EthLikeNetwork ,
@@ -50,12 +51,13 @@ import {
50
51
import type * as EthLikeCommon from '@ethereumjs/common' ;
51
52
import type * as EthLikeTxLib from '@ethereumjs/tx' ;
52
53
import { FeeMarketEIP1559Transaction , Transaction as LegacyTransaction } from '@ethereumjs/tx' ;
54
+ import { RLP } from '@ethereumjs/rlp' ;
53
55
import { SignTypedDataVersion , TypedDataUtils , TypedMessage } from '@metamask/eth-sig-util' ;
54
56
import { BigNumber } from 'bignumber.js' ;
55
57
import BN from 'bn.js' ;
56
58
import { randomBytes } from 'crypto' ;
57
59
import debugLib from 'debug' ;
58
- import { addHexPrefix , stripHexPrefix } from 'ethereumjs-util' ;
60
+ import { addHexPrefix , bufArrToArr , stripHexPrefix } from 'ethereumjs-util' ;
59
61
import Keccak from 'keccak' ;
60
62
import _ from 'lodash' ;
61
63
import secp256k1 from 'secp256k1' ;
@@ -482,8 +484,8 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
482
484
replayProtectionOptions ?: ReplayProtectionOptions
483
485
) : EthLikeCommon . default {
484
486
// if eip1559 params are specified, default to london hardfork, otherwise,
485
- // default to tangerine whistle to avoid replay protection issues
486
- const defaultHardfork = ! ! eip1559 ? 'london' : optionalDeps . EthCommon . Hardfork . TangerineWhistle ;
487
+ // default to petersburg to avoid replay protection issues
488
+ const defaultHardfork = ! ! eip1559 ? 'london' : optionalDeps . EthCommon . Hardfork . Petersburg ;
487
489
const ethLikeCommon = AbstractEthLikeNewCoins . getCustomChainCommon ( replayProtectionOptions ?. chain as number ) ;
488
490
ethLikeCommon . setHardfork ( replayProtectionOptions ?. hardfork ?? defaultHardfork ) ;
489
491
return ethLikeCommon ;
@@ -1109,6 +1111,15 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
1109
1111
if ( params . recoveryDestination === undefined || ! this . isValidAddress ( params . recoveryDestination ) ) {
1110
1112
throw new Error ( 'invalid recoveryDestination' ) ;
1111
1113
}
1114
+
1115
+ if ( ! this . staticsCoin ?. features . includes ( CoinFeature . EIP1559 ) ) {
1116
+ if ( params . eip1559 ) {
1117
+ throw new Error ( 'Invalid fee params. EIP1559 not supported' ) ;
1118
+ }
1119
+ if ( params . replayProtectionOptions ?. hardfork === 'london' ) {
1120
+ throw new Error ( 'Invalid replayProtection options. Cannot use the hardfork "london" for this chain' ) ;
1121
+ }
1122
+ }
1112
1123
}
1113
1124
1114
1125
/**
@@ -2034,7 +2045,6 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
2034
2045
let lastScanIndex = 0 ;
2035
2046
2036
2047
for ( let i = 0 ; i < req . length ; i ++ ) {
2037
- const MPC = new Ecdsa ( ) ;
2038
2048
const transaction = req [ i ] ?. txRequest ?. transactions ?. [ 0 ] ?. unsignedTx as unknown as UnsignedTransactionTss ;
2039
2049
if ( ! req [ i ] . ovc || ! req [ i ] . ovc [ 0 ] . ecdsaSignature ) {
2040
2050
throw new Error ( 'Missing signature(s)' ) ;
@@ -2056,9 +2066,6 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
2056
2066
s : shares [ 2 ] ,
2057
2067
y : shares [ 3 ] ,
2058
2068
} as unknown as ECDSAMethodTypes . Signature ;
2059
- const signatureHex = Buffer . from ( signature . toString ( ) , 'hex' ) ;
2060
- const txBuilder = this . getTransactionBuilder ( getCommon ( this . getNetwork ( ) as EthLikeNetwork ) ) ;
2061
- txBuilder . from ( transaction . serializedTxHex as string ) ;
2062
2069
2063
2070
if ( ! transaction . coinSpecific ?. commonKeyChain ) {
2064
2071
throw new Error ( `Missing common keychain for transaction at index ${ i } ` ) ;
@@ -2071,29 +2078,23 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
2071
2078
throw new Error ( `Missing common key chain for transaction at index ${ i } ` ) ;
2072
2079
}
2073
2080
2074
- const derivationPath = transaction . derivationPath ?? 'm/0' ;
2075
- const derivedCommonKeyChain = MPC . deriveUnhardened ( String ( commonKeyChain ) , String ( derivationPath ) ) ;
2076
- const derivedPublicKey = new KeyPairLib ( { pub : derivedCommonKeyChain . slice ( 0 , 66 ) } ) ;
2077
- txBuilder . addSignature ( { pub : derivedPublicKey . getKeys ( ) . pub } , signatureHex ) ;
2078
2081
const ethCommmon = AbstractEthLikeNewCoins . getEthLikeCommon (
2079
2082
transaction . eip1559 ,
2080
2083
transaction . replayProtectionOptions
2081
2084
) ;
2082
- let unsignedTx ;
2085
+ let unsignedTx : EthLikeTxLib . FeeMarketEIP1559Transaction | EthLikeTxLib . Transaction ;
2083
2086
if ( transaction . eip1559 ) {
2084
- unsignedTx = await FeeMarketEIP1559Transaction . fromSerializedTx (
2085
- Buffer . from ( transaction . serializedTxHex , 'hex' )
2086
- ) ;
2087
+ unsignedTx = FeeMarketEIP1559Transaction . fromSerializedTx ( Buffer . from ( transaction . serializedTxHex , 'hex' ) ) ;
2087
2088
} else {
2088
- unsignedTx = await LegacyTransaction . fromSerializedTx ( Buffer . from ( transaction . serializedTxHex , 'hex' ) ) ;
2089
+ unsignedTx = LegacyTransaction . fromSerializedTx ( Buffer . from ( transaction . serializedTxHex , 'hex' ) ) ;
2089
2090
}
2090
2091
const signedTx = this . getSignedTxFromSignature ( ethCommmon , unsignedTx , finalSignature ) ;
2091
2092
broadcastableTransactions . push ( {
2092
2093
serializedTx : addHexPrefix ( signedTx . serialize ( ) . toString ( 'hex' ) ) ,
2093
2094
} ) ;
2094
2095
2095
- if ( i === req . length - 1 && transaction . coinSpecific ! . lastScanIndex ) {
2096
- lastScanIndex = transaction . coinSpecific ! . lastScanIndex as number ;
2096
+ if ( i === req . length - 1 && transaction . coinSpecific ? .lastScanIndex ) {
2097
+ lastScanIndex = transaction . coinSpecific ? .lastScanIndex as number ;
2097
2098
}
2098
2099
}
2099
2100
@@ -2112,11 +2113,7 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
2112
2113
if ( ! _ . isUndefined ( params . derivationSeed ) && typeof params . derivationSeed !== 'string' ) {
2113
2114
throw new Error ( 'invalid derivationSeed' ) ;
2114
2115
}
2115
- if (
2116
- _ . isUndefined ( params . bitgoDestinationAddress ) ||
2117
- typeof params . bitgoDestinationAddress !== 'string' ||
2118
- ! this . isValidAddress ( params . bitgoDestinationAddress )
2119
- ) {
2116
+ if ( _ . isUndefined ( params . recoveryDestination ) || ! this . isValidAddress ( params . recoveryDestination ) ) {
2120
2117
throw new Error ( 'missing or invalid destinationAddress' ) ;
2121
2118
}
2122
2119
}
@@ -2125,17 +2122,19 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
2125
2122
* Helper function for recover()
2126
2123
* This transforms the unsigned transaction information into a format the BitGo offline vault expects
2127
2124
* @param {UnformattedTxInfo } txInfo - tx info
2128
- * @param {EthLikeTxLib.Transaction | EthLikeTxLib. FeeMarketEIP1559Transaction } ethTx - the ethereumjs tx object
2125
+ * @param {LegacyTransaction | FeeMarketEIP1559Transaction } ethTx - the ethereumjs tx object
2129
2126
* @param {string } derivationPath - the derivationPath
2130
2127
* @param {number } nonce - the nonce of the backup key address
2131
2128
* @param {Buffer } gasPrice - gas price for the tx
2132
2129
* @param {number } gasLimit - gas limit for the tx
2133
2130
* @param {EIP1559 } eip1559 - eip1559 params
2131
+ * @param replayProtectionOptions
2132
+ * @param commonKeyChain
2134
2133
* @returns {Promise<OfflineVaultTxInfo> }
2135
2134
*/
2136
2135
private buildTxRequestForOfflineVaultMPCv2 (
2137
2136
txInfo : UnformattedTxInfo ,
2138
- ethTx : EthLikeTxLib . Transaction | EthLikeTxLib . FeeMarketEIP1559Transaction ,
2137
+ ethTx : LegacyTransaction | FeeMarketEIP1559Transaction ,
2139
2138
derivationPath : string ,
2140
2139
nonce : number ,
2141
2140
gasPrice : Buffer ,
@@ -2154,7 +2153,10 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
2154
2153
2155
2154
const unsignedTx : UnsignedTransactionTss = {
2156
2155
serializedTxHex : ethTx . serialize ( ) . toString ( 'hex' ) ,
2157
- signableHex : ethTx . getMessageToSign ( false ) . toString ( 'hex' ) ,
2156
+ signableHex :
2157
+ ethTx instanceof FeeMarketEIP1559Transaction
2158
+ ? ethTx . getMessageToSign ( false ) . toString ( 'hex' )
2159
+ : Buffer . from ( RLP . encode ( bufArrToArr ( ethTx . getMessageToSign ( false ) ) ) ) . toString ( 'hex' ) ,
2158
2160
derivationPath : derivationPath ,
2159
2161
feeInfo : {
2160
2162
fee : fee ,
@@ -2194,8 +2196,8 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
2194
2196
}
2195
2197
2196
2198
private async buildTssRecoveryTxn ( baseAddress : string , gasPrice : any , gasLimit : any , params : RecoverOptions ) {
2197
- const nonce = await this . getAddressNonce ( baseAddress , params . apiKey ) ;
2198
2199
const txAmount = await this . validateBalanceAndGetTxAmount ( baseAddress , gasPrice , gasLimit , params . apiKey ) ;
2200
+ const nonce = await this . getAddressNonce ( baseAddress , params . apiKey ) ;
2199
2201
const recipients = [
2200
2202
{
2201
2203
address : params . recoveryDestination ,
0 commit comments