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
11 changes: 7 additions & 4 deletions modules/bitgo/src/v2/coinFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ import {
Flrp,
FlrToken,
HashToken,
MonToken,
TethLikeCoin,
FiatAED,
FiatEur,
Expand Down Expand Up @@ -557,6 +558,10 @@ export function registerCoinConstructors(coinFactory: CoinFactory, coinMap: Coin
coinFactory.register(name, coinConstructor);
});

MonToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
coinFactory.register(name, coinConstructor);
});

XdcToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
coinFactory.register(name, coinConstructor);
});
Expand Down Expand Up @@ -1081,10 +1086,8 @@ export function getTokenConstructor(tokenConfig: TokenConfig): CoinConstructor |
case 'tton':
return JettonToken.createTokenConstructor(tokenConfig as JettonTokenConfig);
case 'mon':
case 'tmon': {
const coinNames = { Mainnet: 'mon', Testnet: 'tmon' };
return EthLikeErc20Token.createTokenConstructor(tokenConfig as EthLikeTokenConfig, coinNames);
}
case 'tmon':
return MonToken.createTokenConstructor(tokenConfig as EthLikeTokenConfig);
case 'xdc':
case 'txdc':
return XdcToken.createTokenConstructor(tokenConfig as EthLikeTokenConfig);
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 @@ -45,7 +45,7 @@ import { Iota } from '@bitgo/sdk-coin-iota';
import { Islm, Tislm } from '@bitgo/sdk-coin-islm';
import { Lnbtc, Tlnbtc } from '@bitgo/sdk-coin-lnbtc';
import { Ltc, Tltc } from '@bitgo/sdk-coin-ltc';
import { Mon, Tmon } from '@bitgo/sdk-coin-mon';
import { Mon, Tmon, MonToken } from '@bitgo/sdk-coin-mon';
import { Oas, Toas } from '@bitgo/sdk-coin-oas';
import { Opeth, Topeth, OpethToken } from '@bitgo/sdk-coin-opeth';
import { Osmo, Tosmo } from '@bitgo/sdk-coin-osmo';
Expand Down Expand Up @@ -119,7 +119,7 @@ export { Initia, Tinitia };
export { Iota };
export { Lnbtc, Tlnbtc };
export { Ltc, Tltc };
export { Mon, Tmon };
export { Mon, Tmon, MonToken };
export { Oas, Toas };
export { Opeth, Topeth, OpethToken };
export { Osmo, Tosmo };
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 @@ -58,6 +58,7 @@ describe('Coins', () => {
EthLikeErc721Token: 1,
HashToken: 1,
FlrToken: 1,
MonToken: 1,
XdcToken: 1,
JettonToken: 1,
};
Expand Down
1 change: 1 addition & 0 deletions modules/sdk-coin-mon/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './lib';
export * from './mon';
export * from './tmon';
export * from './register';
export * from './monToken';
58 changes: 58 additions & 0 deletions modules/sdk-coin-mon/src/monToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { coins, EthLikeTokenConfig } from '@bitgo/statics';
import { BitGoBase, CoinConstructor, common, MPCAlgorithm, NamedCoinConstructor } from '@bitgo/sdk-core';
import { CoinNames, EthLikeToken, recoveryBlockchainExplorerQuery } from '@bitgo/abstract-eth';

import { TransactionBuilder } from './lib';

export { EthLikeTokenConfig };

export class MonToken extends EthLikeToken {
public readonly tokenConfig: EthLikeTokenConfig;
static coinNames: CoinNames = {
Mainnet: 'mon',
Testnet: 'tmon',
};
constructor(bitgo: BitGoBase, tokenConfig: EthLikeTokenConfig) {
super(bitgo, tokenConfig, MonToken.coinNames);
}
static createTokenConstructor(config: EthLikeTokenConfig): CoinConstructor {
return super.createTokenConstructor(config, MonToken.coinNames);
}

static createTokenConstructors(): NamedCoinConstructor[] {
return super.createTokenConstructors(MonToken.coinNames);
}

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

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

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

/**
* Make a query to Mon explorer for information such as balance, token balance, solidity calls
* @param {Object} query key-value pairs of parameters to append after /api
* @param {string} apiKey optional API key to use instead of the one from the environment
* @returns {Promise<Object>} response from Mon explorer
*/
async recoveryBlockchainExplorerQuery(
query: Record<string, string>,
apiKey?: string
): Promise<Record<string, unknown>> {
const apiToken = apiKey || common.Environments[this.bitgo.getEnv()].monExplorerApiToken;
const explorerUrl = common.Environments[this.bitgo.getEnv()].monExplorerBaseUrl;
return await recoveryBlockchainExplorerQuery(query, explorerUrl as string, apiToken);
}

getFullName(): string {
return 'Mon Token';
}
}
4 changes: 4 additions & 0 deletions modules/sdk-coin-mon/src/register.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { BitGoBase } from '@bitgo/sdk-core';
import { Mon } from './mon';
import { Tmon } from './tmon';
import { MonToken } from './monToken';

export const register = (sdk: BitGoBase): void => {
sdk.register('mon', Mon.createInstance);
sdk.register('tmon', Tmon.createInstance);
MonToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
sdk.register(name, coinConstructor);
});
};
107 changes: 107 additions & 0 deletions modules/sdk-coin-mon/test/unit/monToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import 'should';
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
import { BitGoAPI } from '@bitgo/sdk-api';

import { register, MonToken } from '../../src';

describe('Mon Token:', function () {
let bitgo: TestBitGoAPI;
let monTokenCoin;
const tokenName = 'mon:usdc';

before(function () {
bitgo = TestBitGo.decorate(BitGoAPI, { env: 'prod' });
register(bitgo);
bitgo.initializeTestVars();
monTokenCoin = bitgo.coin(tokenName);
});

it('should return constants', function () {
monTokenCoin.getChain().should.equal('mon:usdc');
monTokenCoin.getBaseChain().should.equal('mon');
monTokenCoin.getFullName().should.equal('Mon Token');
monTokenCoin.getBaseFactor().should.equal(1e6);
monTokenCoin.type.should.equal(tokenName);
monTokenCoin.name.should.equal('Monad USDC');
monTokenCoin.coin.should.equal('mon');
monTokenCoin.network.should.equal('Mainnet');
monTokenCoin.decimalPlaces.should.equal(6);
});

describe('Token Registration and TransactionBuilder', function () {
const mainnetTokens = ['mon:usdc', 'mon:wmon'];

describe('Mainnet tokens', function () {
mainnetTokens.forEach((tokenName) => {
it(`${tokenName} should be registered as MonToken`, function () {
const token = bitgo.coin(tokenName);
token.should.be.instanceOf(MonToken);
});

it(`${tokenName} should create TransactionBuilder without error`, function () {
const token = bitgo.coin(tokenName) as MonToken;
// @ts-expect-error - accessing protected method for testing
(() => token.getTransactionBuilder()).should.not.throw();
});

it(`${tokenName} should use Mon-specific TransactionBuilder`, function () {
const token = bitgo.coin(tokenName) as MonToken;
// @ts-expect-error - accessing protected method for testing
const builder = token.getTransactionBuilder();
builder.should.have.property('_common');
builder.constructor.name.should.equal('TransactionBuilder');
});

it(`${tokenName} should not throw "Cannot use common sdk module" error`, function () {
const token = bitgo.coin(tokenName) as MonToken;
let errorThrown = false;
let errorMessage = '';

try {
// @ts-expect-error - accessing protected method for testing
const builder = token.getTransactionBuilder();
// Try to use the builder to ensure it's fully functional
// @ts-expect-error - type expects TransactionType enum
builder.type('Send');
} catch (e) {
errorThrown = true;
errorMessage = (e as Error).message;
}

errorThrown.should.equal(false);
errorMessage.should.not.match(/Cannot use common sdk module/);
});

it(`${tokenName} should build transaction successfully`, async function () {
const token = bitgo.coin(tokenName) as MonToken;
// @ts-expect-error - accessing protected method for testing
const builder = token.getTransactionBuilder();

// Set up a basic transfer transaction
// @ts-expect-error - type expects TransactionType enum
builder.type('Send');
builder.fee({
fee: '10000000000',
gasLimit: '100000',
});
builder.counter(1);
builder.contract(token.tokenContractAddress);

// Verify the builder is correctly configured
builder.should.have.property('_type', 'Send');
builder.should.have.property('_fee');
builder.should.have.property('_counter', 1);
});
});
});

it('should verify all Mon tokens use MonToken class, not EthLikeErc20Token', function () {
mainnetTokens.forEach((tokenName) => {
const token = bitgo.coin(tokenName);
token.should.be.instanceOf(MonToken);
token.constructor.name.should.equal('MonToken');
token.constructor.name.should.not.equal('EthLikeErc20Token');
});
});
});
});
100 changes: 100 additions & 0 deletions modules/statics/src/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,16 @@ export class XdcERC20Token extends ContractAddressDefinedToken {
}
}

/**
* The Mon network supports tokens
* Mon Tokens are ERC20 tokens
*/
export class MonERC20Token extends ContractAddressDefinedToken {
constructor(options: Erc20ConstructorOptions) {
super(options);
}
}

/**
* The Xrp network supports tokens
* Xrp tokens are identified by their issuer address
Expand Down Expand Up @@ -2956,6 +2966,96 @@ export function txdcErc20(
);
}

/**
* Factory function for MonErc20 token instances.
*
* @param id uuid v4
* @param name unique identifier of the token
* @param fullName Complete human-readable name of the token
* @param decimalPlaces Number of decimal places this token supports (divisibility exponent)
* @param contractAddress Contract address of this token
* @param asset Asset which this coin represents. This is the same for both mainnet and testnet variants of a coin.
* @param prefix? Optional token prefix. Defaults to empty string
* @param suffix? Optional token suffix. Defaults to token name.
* @param network? Optional token network. Defaults to Mon mainnet network.
* @param features? Features of this coin. Defaults to the DEFAULT_FEATURES defined in `AccountCoin`
* @param primaryKeyCurve The elliptic curve for this chain/token
*/
export function monErc20(
id: string,
name: string,
fullName: string,
decimalPlaces: number,
contractAddress: string,
asset: UnderlyingAsset,
features: CoinFeature[] = [...AccountCoin.DEFAULT_FEATURES, CoinFeature.EIP1559],
prefix = '',
suffix: string = name.toUpperCase(),
network: AccountNetwork = Networks.main.mon,
primaryKeyCurve: KeyCurve = KeyCurve.Secp256k1
) {
return Object.freeze(
new MonERC20Token({
id,
name,
fullName,
network,
contractAddress,
prefix,
suffix,
features,
decimalPlaces,
asset,
isToken: true,
primaryKeyCurve,
baseUnit: BaseUnit.ETH,
})
);
}

/**
* Factory function for Mon testnet MonErc20 token instances.
*
* @param id uuid v4
* @param name unique identifier of the token
* @param fullName Complete human-readable name of the token
* @param decimalPlaces Number of decimal places this token supports (divisibility exponent)
* @param contractAddress Contract address of this token
* @param asset Asset which this coin represents. This is the same for both mainnet and testnet variants of a coin.
* @param prefix? Optional token prefix. Defaults to empty string
* @param suffix? Optional token suffix. Defaults to token name.
* @param network? Optional token network. Defaults to the Mon test network.
* @param features? Features of this coin. Defaults to the DEFAULT_FEATURES defined in `AccountCoin`
* @param primaryKeyCurve The elliptic curve for this chain/token
*/
export function tmonErc20(
id: string,
name: string,
fullName: string,
decimalPlaces: number,
contractAddress: string,
asset: UnderlyingAsset,
features: CoinFeature[] = AccountCoin.DEFAULT_FEATURES,
prefix = '',
suffix: string = name.toUpperCase(),
network: AccountNetwork = Networks.test.mon,
primaryKeyCurve: KeyCurve = KeyCurve.Secp256k1
) {
return monErc20(
id,
name,
fullName,
decimalPlaces,
contractAddress,
asset,
features,
prefix,
suffix,
network,
primaryKeyCurve
);
}

/**
* Factory function for xrp token instances.
*
Expand Down
Loading