Skip to content
Merged
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
18 changes: 16 additions & 2 deletions modules/bitgo/src/v2/coinFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import {
PolyxTokenConfig,
JettonTokenConfig,
CantonTokenConfig,
Erc7984TokenConfig,
Erc7984Coin,
} from '@bitgo/statics';
import {
Ada,
Expand Down Expand Up @@ -82,6 +84,7 @@ import {
EosToken,
Erc20Token,
Erc721Token,
Erc7984Token,
Etc,
Eth,
Ethw,
Expand Down Expand Up @@ -419,6 +422,13 @@ export function registerCoinConstructors(coinFactory: CoinFactory, coinMap: Coin
}
);

Erc7984Token.createTokenConstructors([
...tokens.bitcoin.eth.confidentialTokens,
...tokens.testnet.eth.confidentialTokens,
]).forEach(({ name, coinConstructor }) => {
coinFactory.register(name, coinConstructor);
});

StellarToken.createTokenConstructors([...tokens.bitcoin.xlm.tokens, ...tokens.testnet.xlm.tokens]).forEach(
({ name, coinConstructor }) => {
coinFactory.register(name, coinConstructor);
Expand Down Expand Up @@ -960,12 +970,16 @@ export function getTokenConstructor(tokenConfig: TokenConfig): CoinConstructor |

switch (tokenConfig.coin) {
case 'eth':
case 'hteth':
if (tokenConfig.type.includes('erc721')) {
case 'hteth': {
const staticCoin = coins.get(tokenConfig.type);
if (staticCoin instanceof Erc7984Coin) {
return Erc7984Token.createTokenConstructor(tokenConfig as Erc7984TokenConfig);
} else if (tokenConfig.type.includes('erc721')) {
return Erc721Token.createTokenConstructor(tokenConfig as EthLikeTokenConfig);
} else {
return Erc20Token.createTokenConstructor(tokenConfig as Erc20TokenConfig);
}
}
case 'xlm':
case 'txlm':
return StellarToken.createTokenConstructor(tokenConfig as StellarTokenConfig);
Expand Down
4 changes: 2 additions & 2 deletions modules/bitgo/src/v2/coins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { Doge, Tdoge } from '@bitgo/sdk-coin-doge';
import { Dot, Tdot } from '@bitgo/sdk-coin-dot';
import { Eos, EosToken, Teos } from '@bitgo/sdk-coin-eos';
import { Etc, Tetc } from '@bitgo/sdk-coin-etc';
import { Erc20Token, Erc721Token, Eth, Gteth, Hteth, Teth } from '@bitgo/sdk-coin-eth';
import { Erc20Token, Erc721Token, Erc7984Token, Eth, Gteth, Hteth, Teth } from '@bitgo/sdk-coin-eth';
import { EvmCoin, EthLikeErc20Token, EthLikeErc721Token } from '@bitgo/sdk-coin-evm';
import { Flr, Tflr, FlrToken } from '@bitgo/sdk-coin-flr';
import { Flrp } from '@bitgo/sdk-coin-flrp';
Expand Down Expand Up @@ -107,7 +107,7 @@ export { Doge, Tdoge };
export { Dot, Tdot };
export { Bcha, Tbcha };
export { Eos, EosToken, Teos };
export { Erc20Token, Erc721Token, Eth, Gteth, Hteth, Teth };
export { Erc20Token, Erc721Token, Erc7984Token, Eth, Gteth, Hteth, Teth };
export { Ethw };
export { EthLikeCoin, TethLikeCoin };
export { Etc, Tetc };
Expand Down
1 change: 1 addition & 0 deletions modules/bitgo/test/browser/browser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ describe('Coins', () => {
AdaToken: 1,
Erc20Token: 1,
Erc721Token: 1,
Erc7984Token: 1,
EthLikeCoin: 1,
TethLikeCoin: 1,
OfcToken: 1,
Expand Down
112 changes: 112 additions & 0 deletions modules/sdk-coin-eth/src/erc7984Token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* @prettier
*/
import { BitGoBase, CoinConstructor, MPCAlgorithm, NamedCoinConstructor } from '@bitgo/sdk-core';

import { coins, Erc7984TokenConfig, tokens } from '@bitgo/statics';
import { CoinNames } from '@bitgo/abstract-eth';

import { Eth } from './eth';
import { TransactionBuilder } from './lib';

export { Erc7984TokenConfig };

export class Erc7984Token extends Eth {
public readonly tokenConfig: Erc7984TokenConfig;
protected readonly sendMethodName: 'sendMultiSig' | 'sendMultiSigToken';
static coinNames: CoinNames = {
Mainnet: 'eth',
Testnet: 'hteth',
};

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

static createTokenConstructor(config: Erc7984TokenConfig): CoinConstructor {
return (bitgo: BitGoBase) => new Erc7984Token(bitgo, config);
}

static createTokenConstructors(
tokenConfigs: Erc7984TokenConfig[] = [
...tokens.bitcoin.eth.confidentialTokens,
...tokens.testnet.eth.confidentialTokens,
]
): NamedCoinConstructor[] {
const tokensCtors: NamedCoinConstructor[] = [];
for (const token of tokenConfigs) {
const tokenConstructor = Erc7984Token.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;
}

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

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

getFullName() {
return 'ERC7984 Confidential Token';
}

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

/**
* Flag for sending value of 0.
* ERC-7984 confidential transfers always carry an encrypted amount; zero-value sends are not meaningful.
*/
valuelessTransferAllowed() {
return false;
}

/**
* Flag for sending data along with transactions.
*/
transactionDataAllowed() {
return false;
}

/** @inheritDoc */
supportsTss(): boolean {
return true;
}

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

protected getTransactionBuilder(): TransactionBuilder {
return new TransactionBuilder(coins.get(this.getBaseChain()));
}
}
1 change: 1 addition & 0 deletions modules/sdk-coin-eth/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './erc20Token';
export * from './erc721Token';
export * from './erc7984Token';
export * from './eth';
export * from './gteth';
export * from './hteth';
Expand Down
16 changes: 14 additions & 2 deletions modules/sdk-coin-eth/src/register.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { BitGoBase, GlobalCoinFactory } from '@bitgo/sdk-core';
import { Erc20Token } from './erc20Token';
import { Erc721Token } from './erc721Token';
import { Erc7984Token } from './erc7984Token';
import { Eth } from './eth';
import { Gteth } from './gteth';
import { Hteth } from './hteth';
import { Teth } from './teth';
import { Erc721Token } from './erc721Token';
import { type CoinMap, getFormattedErc20Tokens } from '@bitgo/statics';
import { type CoinMap, getFormattedErc20Tokens, getFormattedErc7984Tokens } from '@bitgo/statics';

export const register = (sdk: BitGoBase): void => {
sdk.register('eth', Eth.createInstance);
Expand All @@ -18,6 +19,9 @@ export const register = (sdk: BitGoBase): void => {
Erc721Token.createTokenConstructors().forEach(({ name, coinConstructor }) => {
sdk.register(name, coinConstructor);
});
Erc7984Token.createTokenConstructors().forEach(({ name, coinConstructor }) => {
sdk.register(name, coinConstructor);
});
};

export const registerWithCoinMap = (sdk: BitGoBase, coinMap: CoinMap): void => {
Expand All @@ -36,4 +40,12 @@ export const registerWithCoinMap = (sdk: BitGoBase, coinMap: CoinMap): void => {
GlobalCoinFactory.registerToken(coinMap.get(name), coinConstructor);
}
});

// Registration for ERC-7984 confidential tokens from the coin map.
Erc7984Token.createTokenConstructors(getFormattedErc7984Tokens(coinMap)).forEach(({ name, coinConstructor }) => {
sdk.register(name, coinConstructor);
if (coinMap.has(name)) {
GlobalCoinFactory.registerToken(coinMap.get(name), coinConstructor);
}
});
};
14 changes: 8 additions & 6 deletions modules/sdk-coin-eth/test/unit/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import sinon from 'sinon';
import assert from 'assert';
import { BitGoAPI } from '@bitgo/sdk-api';
import { GlobalCoinFactory } from '@bitgo/sdk-core';
import { coins, Erc20Coin } from '@bitgo/statics';
import { coins, Erc20Coin, Erc7984Coin } from '@bitgo/statics';
import { register, registerWithCoinMap } from '../../src/register';
import { Erc20Token } from '../../src/erc20Token';
import { Erc721Token } from '../../src/erc721Token';
import { Erc7984Token } from '../../src/erc7984Token';

describe('ETH Register', function () {
let bitgo: BitGoAPI;
Expand Down Expand Up @@ -35,10 +36,11 @@ describe('ETH Register', function () {
assert.ok(registeredNames.includes('teth'));
assert.ok(registeredNames.includes('hteth'));

// ERC20 and ERC721 tokens should be registered
// ERC20, ERC721 and ERC7984 tokens should be registered
const erc20Count = Erc20Token.createTokenConstructors().length;
const erc721Count = Erc721Token.createTokenConstructors().length;
assert.strictEqual(registerSpy.callCount, 4 + erc20Count + erc721Count);
const erc7984Count = Erc7984Token.createTokenConstructors().length;
assert.strictEqual(registerSpy.callCount, 4 + erc20Count + erc721Count + erc7984Count);
});
});

Expand Down Expand Up @@ -69,12 +71,12 @@ describe('ETH Register', function () {
}
});

it('should not add tokens to the global coin map when coin map has no ERC20 tokens', function () {
const limitedCoinMap = coins.filter((coin) => !(coin instanceof Erc20Coin));
it('should not add tokens to the global coin map when coin map has no contract-address tokens', function () {
const limitedCoinMap = coins.filter((coin) => !(coin instanceof Erc20Coin) && !(coin instanceof Erc7984Coin));

registerWithCoinMap(bitgo, limitedCoinMap);

// registerToken should not be called since no ERC20 tokens are in the map
// registerToken should not be called since no ERC20 or ERC7984 tokens are in the map
assert.strictEqual(registerTokenSpy.callCount, 0);
});
});
Expand Down