Skip to content

feat: add CoinFeature for new common evm sdk #6235

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Draft
wants to merge 2 commits into
base: WIN-5511
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion modules/account-lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
accountLibBaseCoin,
acountLibCrypto,
} from '@bitgo/sdk-core';
import { BaseCoin as CoinConfig, coins } from '@bitgo/statics';
import { BaseCoin as CoinConfig, coins, CoinFeature } from '@bitgo/statics';
export { Ed25519BIP32, Eddsa };

/**
Expand Down Expand Up @@ -191,6 +191,9 @@ export { Stt };
import * as Soneium from '@bitgo/sdk-coin-soneium';
export { Soneium };

import * as EvmCoin from '@bitgo/sdk-coin-evm';
export { EvmCoin };

const coinBuilderMap = {
trx: Trx.WrappedBuilder,
ttrx: Trx.WrappedBuilder,
Expand Down Expand Up @@ -303,6 +306,16 @@ const coinBuilderMap = {
tpolyx: Polyx.TransactionBuilderFactory,
};

/**
* coins.filter(coin => coin.coinFeature.has(EVM_SHARED_SDK)).forEach(coin => coinBuilderMap[coin.name] = EvmCoin.TransactionBuilder);
*/

coins
.filter((coin) => coin.features.includes(CoinFeature.SHARED_EVM_SDK))
.forEach((coin) => {
coinBuilderMap[coin.name] = EvmCoin.TransactionBuilder;
});

/**
* Get the list of coin tickers supported by this library.
*/
Expand Down
9 changes: 8 additions & 1 deletion modules/bitgo/src/v2/coinFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Near, TNear } from '@bitgo/sdk-coin-near';
import { SolToken } from '@bitgo/sdk-coin-sol';
import { TrxToken } from '@bitgo/sdk-coin-trx';
import { CoinFactory } from '@bitgo/sdk-core';
import { CoinMap, coins, getFormattedTokens } from '@bitgo/statics';
import { CoinFeature, CoinMap, coins, getFormattedTokens } from '@bitgo/statics';
import {
Ada,
Algo,
Expand Down Expand Up @@ -49,6 +49,7 @@ import {
Eth,
Ethw,
EthLikeCoin,
EvmCoin,
FetchAi,
Flr,
TethLikeCoin,
Expand Down Expand Up @@ -345,6 +346,12 @@ export function registerCoinConstructors(coinFactory: CoinFactory, coinMap: Coin
coinFactory.register('zeta', Zeta.createInstance);
coinFactory.register('zketh', Zketh.createInstance);

coins
.filter((coin) => coin.features.includes(CoinFeature.SHARED_EVM_SDK))
.forEach((coin) => {
coinFactory.register(coin.name, EvmCoin.createInstance);
});

const tokens = getFormattedTokens(coinMap);

Erc20Token.createTokenConstructors([...tokens.bitcoin.eth.tokens, ...tokens.testnet.eth.tokens]).forEach(
Expand Down
2 changes: 2 additions & 0 deletions modules/bitgo/src/v2/coins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { Erc20Token, Eth, Gteth, Hteth, Teth } from '@bitgo/sdk-coin-eth';
import { FetchAi, TfetchAi } from '@bitgo/sdk-coin-fetchai';
import { Flr, Tflr } from '@bitgo/sdk-coin-flr';
import { Ethw } from '@bitgo/sdk-coin-ethw';
import { EvmCoin } from '@bitgo/sdk-coin-evm';
import { EthLikeCoin, TethLikeCoin } from '@bitgo/sdk-coin-ethlike';
import { Hash, Thash } from '@bitgo/sdk-coin-hash';
import { Hbar, Thbar } from '@bitgo/sdk-coin-hbar';
Expand Down Expand Up @@ -102,6 +103,7 @@ export { Erc20Token, Eth, Gteth, Hteth, Teth };
export { Ethw };
export { EthLikeCoin, TethLikeCoin };
export { Etc, Tetc };
export { EvmCoin };
export { FetchAi, TfetchAi };
export { Flr, Tflr };
export { Hash, Thash };
Expand Down
1 change: 1 addition & 0 deletions modules/sdk-coin-evm/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist/
3 changes: 3 additions & 0 deletions modules/sdk-coin-evm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
.idea/
dist/
8 changes: 8 additions & 0 deletions modules/sdk-coin-evm/.mocharc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require: 'ts-node/register'
timeout: '60000'
reporter: 'min'
reporter-option:
- 'cdn=true'
- 'json=false'
exit: true
spec: ['test/**/*.ts']
12 changes: 12 additions & 0 deletions modules/sdk-coin-evm/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
!dist/
.idea/
.prettierrc.yml
tsconfig.json
src/
test/
scripts/
.nyc_output
CODEOWNERS
node_modules/
.prettierignore
.mocharc.js
2 changes: 2 additions & 0 deletions modules/sdk-coin-evm/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.nyc_output/
dist/
3 changes: 3 additions & 0 deletions modules/sdk-coin-evm/.prettierrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
printWidth: 120
singleQuote: true
trailingComma: 'es5'
4 changes: 4 additions & 0 deletions modules/sdk-coin-evm/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Change Log

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
3 changes: 3 additions & 0 deletions modules/sdk-coin-evm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @bitgo/sdk-coin-evm

Configurable common module for EVM assets, using @bitgo/abstract-eth for reduced coin integration boilerplate
44 changes: 44 additions & 0 deletions modules/sdk-coin-evm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "@bitgo/sdk-coin-evm",
"version": "1.1.0",
"description": "Configurable common module for EVM assets, using @bitgo/abstract-eth for reduced coin integration boilerplate.",
"main": "./dist/src/index.js",
"types": "./dist/src/index.d.ts",
"scripts": {
"build": "yarn tsc --build --incremental --verbose .",
"fmt": "prettier --write .",
"check-fmt": "prettier --check .",
"clean": "rm -r ./dist",
"lint": "eslint --quiet .",
"test": "npm run coverage",
"coverage": "nyc -- npm run unit-test",
"unit-test": "mocha",
"prepare": "npm run build"
},
"dependencies": {},
"devDependencies": {},
"author": "BitGo SDK Team <sdkteam@bitgo.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/BitGo/BitGoJS.git",
"directory": "modules/sdk-coin-evm"
},
"files": [
"dist/src"
],
"lint-staged": {
"*.{js,ts}": [
"yarn prettier --write",
"yarn eslint --fix"
]
},
"publishConfig": {
"access": "public"
},
"nyc": {
"extension": [
".ts"
]
}
}
68 changes: 68 additions & 0 deletions modules/sdk-coin-evm/src/evm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @prettier
*/
import { BaseCoin, BitGoBase, common, MPCAlgorithm, MultisigType, multisigTypes } from '@bitgo/sdk-core';
import { BaseCoin as StaticsBaseCoin, CoinFeature, coins } from '@bitgo/statics';
import {
AbstractEthLikeNewCoins,
OfflineVaultTxInfo,
RecoverOptions,
recoveryBlockchainExplorerQuery,
TransactionBuilder as EthLikeTransactionBuilder,
UnsignedSweepTxMPCv2,
} from '@bitgo/abstract-eth';
import { TransactionBuilder } from './lib';
import assert from 'assert';

export class EvmCoin extends AbstractEthLikeNewCoins {
protected constructor(bitgo: BitGoBase, staticsCoin?: Readonly<StaticsBaseCoin>) {
super(bitgo, staticsCoin);
}

static createInstance(bitgo: BitGoBase, staticsCoin?: Readonly<StaticsBaseCoin>): BaseCoin {
return new EvmCoin(bitgo, staticsCoin);
}

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

/** @inheritDoc */
supportsTss(): boolean {
return this.staticsCoin?.features.includes(CoinFeature.TSS) ?? false;
}

/** inherited doc */
getDefaultMultisigType(): MultisigType {
return this.staticsCoin?.features.includes(CoinFeature.TSS) ? multisigTypes.tss : multisigTypes.onchain;
}

/** @inheritDoc */
getMPCAlgorithm(): MPCAlgorithm {
return 'ecdsa';
}

protected async buildUnsignedSweepTxnTSS(params: RecoverOptions): Promise<OfflineVaultTxInfo | UnsignedSweepTxMPCv2> {
if (this.staticsCoin?.features.includes(CoinFeature.MPCV2)) {
return this.buildUnsignedSweepTxnMPCv2(params);
}
return super.buildUnsignedSweepTxnTSS(params);
}

/**
* Make a query to chain explorer for information such as balance, token balance, solidity calls
* @param {Object} query key-value pairs of parameters to append after /api
* @returns {Promise<Object>} response from chain explorer
*/
async recoveryBlockchainExplorerQuery(query: Record<string, string>): Promise<Record<string, unknown>> {
const evmConfig = common.Environments[this.bitgo.getEnv()].evm;
assert(
evmConfig && this.getFamily() in evmConfig,
`env config is missing for ${this.getFamily()} in ${this.bitgo.getEnv()}`
);

const apiToken = evmConfig[this.getFamily()].apiToken;
const explorerUrl = evmConfig[this.getFamily()].baseUrl;
return await recoveryBlockchainExplorerQuery(query, explorerUrl as string, apiToken as string);
}
}
3 changes: 3 additions & 0 deletions modules/sdk-coin-evm/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './evm';
export * from './lib';
export * from './register';
6 changes: 6 additions & 0 deletions modules/sdk-coin-evm/src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as Utils from './utils';

export { TransactionBuilder } from './transactionBuilder';
export { TransferBuilder } from './transferBuilder';
export { Transaction, KeyPair } from '@bitgo/abstract-eth';
export { Utils };
36 changes: 36 additions & 0 deletions modules/sdk-coin-evm/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { BuildTransactionError, TransactionType } from '@bitgo/sdk-core';
import { TransactionBuilder as AbstractTransactionBuilder, Transaction } from '@bitgo/abstract-eth';
import { getCommon } from './utils';
import { TransferBuilder } from './transferBuilder';

export class TransactionBuilder extends AbstractTransactionBuilder {
protected _transfer: TransferBuilder;
private _signatures: any;

constructor(_coinConfig: Readonly<CoinConfig>) {
super(_coinConfig);
this._common = getCommon(this._coinConfig);
this.transaction = new Transaction(this._coinConfig, this._common);
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
}
return this._transfer;
}

addSignature(publicKey, signature) {
this._signatures = [];
this._signatures.push({ publicKey, signature });
}

protected getContractData(addresses: string[]): string {
throw new Error('Method not implemented.');
}
}
1 change: 1 addition & 0 deletions modules/sdk-coin-evm/src/lib/transferBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { TransferBuilder } from '@bitgo/abstract-eth';
29 changes: 29 additions & 0 deletions modules/sdk-coin-evm/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { CoinFeature, NetworkType, BaseCoin, EthereumNetwork } from '@bitgo/statics';
import EthereumCommon from '@ethereumjs/common';
import { InvalidTransactionError } from '@bitgo/sdk-core';

/**
* @param {NetworkType} network either mainnet or testnet
* @returns {EthereumCommon} Ethereum common configuration object
*/
export function getCommon(coin: Readonly<BaseCoin>): EthereumCommon {
if (!coin.features.includes(CoinFeature.SHARED_EVM_SDK)) {
throw new InvalidTransactionError(`Cannot use common sdk module for the coin ${coin.name}`);
}

if (!coin.features.includes(CoinFeature.SHARED_EVM_SDK)) {
throw new InvalidTransactionError(`Cannot use common sdk module for the coin ${coin.name}`);
}
return EthereumCommon.custom(
{
name: coin.network.name,
networkId: (coin.network as EthereumNetwork).chainId,
chainId: (coin.network as EthereumNetwork).chainId,
},
{
baseChain: coin.network.type === NetworkType.MAINNET ? 'mainnet' : 'sepolia',
hardfork: coin.features.includes(CoinFeature.EIP1559) ? 'london' : undefined,
eips: coin.features.includes(CoinFeature.EIP1559) ? [1559] : undefined,
}
);
}
11 changes: 11 additions & 0 deletions modules/sdk-coin-evm/src/register.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { BitGoBase } from '@bitgo/sdk-core';
import { CoinFeature, coins } from '@bitgo/statics';
import { EvmCoin } from './evm';

export const register = (sdk: BitGoBase): void => {
coins
.filter((coin) => coin.features.includes(CoinFeature.SHARED_EVM_SDK))
.forEach((coin) => {
sdk.register(coin.name, EvmCoin.createInstance);
});
};
12 changes: 12 additions & 0 deletions modules/sdk-coin-evm/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./",
"strictPropertyInitialization": false,
"esModuleInterop": true,
"typeRoots": ["../../types", "./node_modules/@types", "../../node_modules/@types"]
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
6 changes: 6 additions & 0 deletions modules/sdk-core/src/bitgo/environments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ interface EnvironmentTemplate {
soneiumExplorerApiToken?: string;
stxNodeUrl: string;
vetNodeUrl: string;
evm?: {
[key: string]: {
baseUrl: string;
apiToken?: string;
};
};
}

export interface Environment extends EnvironmentTemplate {
Expand Down
5 changes: 5 additions & 0 deletions modules/statics/src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,11 @@ export enum CoinFeature {
*/
SHARED_EVM_SIGNING = 'shared-evm-signing',

/**
* This coin is an EVM compatible coin and should use common EVM SDK module
*/
SHARED_EVM_SDK = 'shared-evm-sdk',

/**
* This coin supports multisig wallets
*/
Expand Down