Skip to content

Commit

Permalink
refactor(abstract-eth): move methods to abstract-eth
Browse files Browse the repository at this point in the history
  • Loading branch information
gianchandania committed Nov 8, 2023
1 parent 522c486 commit 7f579c0
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 57 deletions.
8 changes: 4 additions & 4 deletions modules/abstract-eth/src/abstractEthLikeNewCoins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ interface HopTransactionBuildOptions {
walletPassphrase: string;
}

interface BuildOptions {
export interface BuildOptions {
hop?: boolean;
wallet?: Wallet;
recipients?: Recipient[];
Expand Down Expand Up @@ -361,7 +361,7 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
static hopTransactionSalt = 'bitgoHopAddressRequestSalt';
protected readonly sendMethodName: 'sendMultiSig' | 'sendMultiSigToken';

protected readonly _staticsCoin: Readonly<StaticsBaseCoin>;
readonly staticsCoin: Readonly<StaticsBaseCoin>;

protected constructor(bitgo: BitGoBase, staticsCoin?: Readonly<StaticsBaseCoin>) {
super(bitgo, staticsCoin);
Expand All @@ -370,12 +370,12 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
throw new Error('missing required constructor parameter staticsCoin');
}

this._staticsCoin = staticsCoin;
this.staticsCoin = staticsCoin;
this.sendMethodName = 'sendMultiSig';
}

getNetwork(): EthLikeNetwork | undefined {
return this._staticsCoin?.network as EthLikeNetwork;
return this.staticsCoin?.network as EthLikeNetwork;
}

valuelessTransferAllowed(): boolean {
Expand Down
137 changes: 128 additions & 9 deletions modules/sdk-coin-eth/src/erc20Token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,96 @@ import {
MPCAlgorithm,
NamedCoinConstructor,
} from '@bitgo/sdk-core';
import { EthLikeTokenConfig, Erc20TokenConfig } from '@bitgo/statics';
import { coins, EthLikeTokenConfig, Erc20TokenConfig, tokens } from '@bitgo/statics';
import { CoinNames } from '@bitgo/abstract-eth';
import { bip32 } from '@bitgo/utxo-lib';
import * as _ from 'lodash';

import { CoinNames, EthLikeToken, RecoverOptions, RecoveryInfo, optionalDeps } from '@bitgo/abstract-eth';
import { Eth } from './eth';
import { Eth, RecoverOptions, RecoveryInfo, optionalDeps, TransactionPrebuild } from './eth';
import { TransactionBuilder } from './lib';

export { Erc20TokenConfig };
export class Erc20Token extends EthLikeToken {
export class Erc20Token extends Eth {
public readonly tokenConfig: EthLikeTokenConfig;
protected readonly sendMethodName: 'sendMultiSig' | 'sendMultiSigToken';
static coinNames: CoinNames = {
Mainnet: 'eth',
Testnet: 'gteth',
};

constructor(bitgo: BitGoBase, tokenConfig: Erc20TokenConfig) {
super(bitgo, tokenConfig, Erc20Token.coinNames);
const staticsCoin = coins.get(Erc20Token.coinNames[tokenConfig.network]);
super(bitgo, staticsCoin);
this.tokenConfig = tokenConfig;
this.sendMethodName = 'sendMultiSigToken';
}

static createTokenConstructor(config: EthLikeTokenConfig): CoinConstructor {
return super.createTokenConstructor(config, Erc20Token.coinNames);
static createTokenConstructor(config: Erc20TokenConfig): CoinConstructor {
return (bitgo: BitGoBase) => new Erc20Token(bitgo, config);
}

static createTokenConstructors(): NamedCoinConstructor[] {
return super.createTokenConstructors(Erc20Token.coinNames);
const tokensCtors: NamedCoinConstructor[] = [];
for (const token of [...tokens.bitcoin.eth.tokens, ...tokens.testnet.eth.tokens]) {
const tokenConstructor = Erc20Token.createTokenConstructor(token);
tokensCtors.push({ name: token.type, coinConstructor: tokenConstructor });
tokensCtors.push({ name: token.tokenContractAddress, coinConstructor: tokenConstructor });
}
return tokensCtors;
}

get type() {
return this.tokenConfig.type;
}

get name() {
return this.tokenConfig.name;
}

get coin() {
return this.tokenConfig.coin;
}

get network() {
return this.tokenConfig.network;
}

get tokenContractAddress() {
return this.tokenConfig.tokenContractAddress;
}

getFullName(): string {
get decimalPlaces() {
return this.tokenConfig.decimalPlaces;
}

getChain() {
return this.tokenConfig.type;
}

getFullName() {
return 'ERC20 Token';
}

getBaseFactor() {
return Math.pow(10, this.tokenConfig.decimalPlaces);
}

/**
* Flag for sending value of 0
* @returns {boolean} True if okay to send 0 value, false otherwise
*/
valuelessTransferAllowed() {
return false;
}

/**
* Flag for sending data along with transactions
* @returns {boolean} True if okay to send tx data (ETH), false otherwise
*/
transactionDataAllowed() {
return false;
}

/** @inheritDoc */
supportsTss(): boolean {
return true;
Expand All @@ -51,6 +111,10 @@ export class Erc20Token extends EthLikeToken {
return 'ecdsa';
}

protected getTransactionBuilder(): TransactionBuilder {
return new TransactionBuilder(coins.get(this.getBaseChain()));
}

/**
* Builds a token recovery transaction without BitGo
* @param params
Expand Down Expand Up @@ -233,4 +297,59 @@ export class Erc20Token extends EthLikeToken {

return signedTx;
}

getOperation(recipient, expireTime, contractSequenceId) {
return [
['string', 'address', 'uint', 'address', 'uint', 'uint'],
[
'ERC20',
new optionalDeps.ethUtil.BN(optionalDeps.ethUtil.stripHexPrefix(recipient.address), 16),
recipient.amount,
new optionalDeps.ethUtil.BN(optionalDeps.ethUtil.stripHexPrefix(this.tokenContractAddress), 16),
expireTime,
contractSequenceId,
],
];
}

getSendMethodArgs(txInfo) {
// Method signature is
// sendMultiSigToken(address toAddress, uint value, address tokenContractAddress, uint expireTime, uint sequenceId, bytes signature)
return [
{
name: 'toAddress',
type: 'address',
value: txInfo.recipient.address,
},
{
name: 'value',
type: 'uint',
value: txInfo.recipient.amount,
},
{
name: 'tokenContractAddress',
type: 'address',
value: this.tokenContractAddress,
},
{
name: 'expireTime',
type: 'uint',
value: txInfo.expireTime,
},
{
name: 'sequenceId',
type: 'uint',
value: txInfo.contractSequenceId,
},
{
name: 'signature',
type: 'bytes',
value: optionalDeps.ethUtil.toBuffer(optionalDeps.ethUtil.addHexPrefix(txInfo.signature)),
},
];
}

verifyCoin(txPrebuild: TransactionPrebuild): boolean {
return txPrebuild.coin === this.tokenConfig.coin && txPrebuild.token === this.tokenConfig.type;
}
}
42 changes: 41 additions & 1 deletion modules/sdk-coin-eth/src/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '@bitgo/sdk-core';
import {
AbstractEthLikeNewCoins,
BuildOptions,
BuildTransactionParams,
EIP1559,
RecoveryInfo,
Expand All @@ -37,8 +38,17 @@ import type * as EthTxLib from '@ethereumjs/tx';
import { SignTypedDataVersion, TypedDataUtils, TypedMessage } from '@metamask/eth-sig-util';

import { TransactionBuilder } from './lib/transactionBuilder';
import { Erc20Token } from './erc20Token';

export { Recipient, HalfSignedTransaction, FullySignedTransaction, TransactionPrebuild, optionalDeps };
export {
Recipient,
HalfSignedTransaction,
FullySignedTransaction,
TransactionPrebuild,
optionalDeps,
RecoverOptions,
RecoveryInfo,
};

export class Eth extends AbstractEthLikeNewCoins {
protected constructor(bitgo: BitGoBase, staticsCoin?: Readonly<StaticsBaseCoin>) {
Expand Down Expand Up @@ -469,6 +479,36 @@ export class Eth extends AbstractEthLikeNewCoins {
return { halfSigned: txParams };
}

/**
* Modify prebuild before sending it to the server. Add things like hop transaction params
* @param buildParams The whitelisted parameters for this prebuild
* @param buildParams.hop True if this should prebuild a hop tx, else false
* @param buildParams.recipients The recipients array of this transaction
* @param buildParams.wallet The wallet sending this tx
* @param buildParams.walletPassphrase the passphrase for this wallet
*/
async getExtraPrebuildParams(buildParams: BuildOptions): Promise<BuildOptions> {
if (
!_.isUndefined(buildParams.hop) &&
buildParams.hop &&
!_.isUndefined(buildParams.wallet) &&
!_.isUndefined(buildParams.recipients) &&
!_.isUndefined(buildParams.walletPassphrase)
) {
if (this instanceof Erc20Token) {
throw new Error(
`Hop transactions are not enabled for ERC-20 tokens, nor are they necessary. Please remove the 'hop' parameter and try again.`
);
}
return (await this.createHopTransactionParams({
wallet: buildParams.wallet,
recipients: buildParams.recipients,
walletPassphrase: buildParams.walletPassphrase,
})) as any;
}
return {};
}

/**
* Create a new transaction builder for the current chain
* @return a new transaction builder
Expand Down
Loading

0 comments on commit 7f579c0

Please sign in to comment.