UNPKG

9.51 kBPlain TextView Raw
1import * as _ from 'lodash';
2import { Environments, EnvironmentName } from './v2/environments';
3import { OfcTokenConfig } from './v2/coins/ofcToken';
4import { Erc20TokenConfig } from './v2/coins/erc20Token';
5import { StellarTokenConfig } from './v2/coins/stellarToken';
6import { CeloTokenConfig } from './v2/coins/celoToken';
7import { EosTokenConfig } from './v2/coins/eosToken';
8import { AlgoTokenConfig } from './v2/coins/algoToken';
9import { coins, Erc20Coin, StellarCoin, OfcCoin, CeloCoin, CoinKind, NetworkType, EosCoin, Networks, AlgoCoin } from '@bitgo/statics';
10
11export interface Tokens {
12 bitcoin: {
13 eth: {
14 tokens: Erc20TokenConfig[];
15 };
16 xlm: {
17 tokens: StellarTokenConfig[];
18 };
19 algo: {
20 tokens: AlgoTokenConfig[];
21 };
22 ofc: {
23 tokens: OfcTokenConfig[];
24 };
25 celo: {
26 tokens: CeloTokenConfig[];
27 };
28 eos: {
29 tokens: EosTokenConfig[];
30 };
31 };
32 testnet: {
33 eth: {
34 tokens: Erc20TokenConfig[];
35 };
36 xlm: {
37 tokens: StellarTokenConfig[];
38 };
39 algo: {
40 tokens: AlgoTokenConfig[];
41 };
42 ofc: {
43 tokens: OfcTokenConfig[];
44 };
45 celo: {
46 tokens: CeloTokenConfig[];
47 };
48 eos: {
49 tokens: EosTokenConfig[];
50 }
51 };
52}
53
54// Get the list of ERC-20 tokens from statics and format it properly
55const formattedErc20Tokens = coins.reduce((acc: Erc20TokenConfig[], coin) => {
56 if (coin instanceof Erc20Coin) {
57 let baseCoin: string;
58 switch (coin.network) {
59 case Networks.main.ethereum:
60 baseCoin = 'eth';
61 break;
62 case Networks.test.kovan:
63 baseCoin = 'teth';
64 break;
65 case Networks.test.goerli:
66 baseCoin = 'gteth';
67 break;
68 default:
69 throw new Error(`Erc20 token ${coin.name} has an unsupported network`);
70 }
71
72 acc.push({
73 type: coin.name,
74 coin: baseCoin,
75 network: coin.network.type === NetworkType.MAINNET ? 'Mainnet' : 'Testnet',
76 name: coin.fullName,
77 tokenContractAddress: coin.contractAddress.toString().toLowerCase(),
78 decimalPlaces: coin.decimalPlaces,
79 });
80 }
81 return acc;
82}, []);
83
84export const ethGasConfigs = {
85 minimumGasPrice: 1000000000, // minimum gas price a user can provide (1 Gwei)
86 defaultGasPrice: 20000000000, // default gas price if estimation fails (20 Gwei)
87 maximumGasPrice: 2500000000000, // minimum gas price a user can provide (2500 Gwei)
88 defaultGasLimit: 500000, // Default gas limit we set for contract send
89 defaultGasLimitTokenSend: 1000000, // Default gas limit we set for token send
90 minimumGasLimit: 30000, // minimum gas limit a user can set for a send
91 maximumGasLimit: 20000000, // Customers cannot set gas limits beyond this amount
92};
93// Get the list of Stellar tokens from statics and format it properly
94const formattedStellarTokens = coins.reduce((acc: StellarTokenConfig[], coin) => {
95 if (coin instanceof StellarCoin) {
96 acc.push({
97 type: coin.name,
98 coin: coin.network.type === NetworkType.MAINNET ? 'xlm' : 'txlm',
99 network: coin.network.type === NetworkType.MAINNET ? 'Mainnet' : 'Testnet',
100 name: coin.fullName,
101 decimalPlaces: coin.decimalPlaces,
102 });
103 }
104 return acc;
105}, []);
106
107// Get the list of Stellar tokens from statics and format it properly
108const formattedAlgoTokens = coins.reduce((acc: AlgoTokenConfig[], coin) => {
109 if (coin instanceof AlgoCoin) {
110 acc.push({
111 type: coin.name,
112 coin: coin.network.type === NetworkType.MAINNET ? 'algo' : 'talgo',
113 network: coin.network.type === NetworkType.MAINNET ? 'Mainnet' : 'Testnet',
114 name: coin.fullName,
115 decimalPlaces: coin.decimalPlaces,
116 });
117 }
118 return acc;
119}, []);
120
121// Get the list of OFC tokens from statics and format it properly
122const formattedOfcCoins = coins.reduce((acc: OfcTokenConfig[], coin) => {
123 if (coin instanceof OfcCoin) {
124 acc.push({
125 type: coin.name,
126 coin: 'ofc',
127 backingCoin: coin.asset,
128 name: coin.fullName,
129 decimalPlaces: coin.decimalPlaces,
130 isFiat: coin.kind === CoinKind.FIAT,
131 });
132 }
133 return acc;
134}, []);
135
136const formattedCeloTokens = coins.reduce((acc: CeloTokenConfig[], coin) => {
137 if (coin instanceof CeloCoin) {
138 acc.push({
139 type: coin.name,
140 coin: coin.network.type === NetworkType.MAINNET ? 'celo' : 'tcelo',
141 network: coin.network.type === NetworkType.MAINNET ? 'Mainnet' : 'Testnet',
142 name: coin.fullName,
143 tokenContractAddress: coin.contractAddress.toString().toLowerCase(),
144 decimalPlaces: coin.decimalPlaces,
145 });
146 }
147 return acc;
148}, []);
149
150const formattedEosTokens = coins.reduce((acc: EosTokenConfig[], coin) => {
151 if (coin instanceof EosCoin) {
152 acc.push({
153 type: coin.name,
154 coin: coin.network.type === NetworkType.MAINNET ? 'eos' : 'teos',
155 network: coin.network.type === NetworkType.MAINNET ? 'Mainnet' : 'Testnet',
156 name: coin.fullName,
157 tokenContractAddress: coin.contractName.toString().toLowerCase(),
158 decimalPlaces: coin.decimalPlaces,
159 });
160 }
161 return acc;
162}, []);
163
164export const tokens: Tokens = {
165 // network name for production environments
166 bitcoin: {
167 eth: {
168 tokens: formattedErc20Tokens.filter(token => token.network === 'Mainnet'),
169 },
170 xlm: {
171 tokens: formattedStellarTokens.filter(token => token.network === 'Mainnet'),
172 },
173 algo: {
174 tokens: formattedAlgoTokens.filter(token => token.network === 'Mainnet'),
175 },
176 ofc: {
177 tokens: formattedOfcCoins.filter(token => coins.get(token.type).network.type === NetworkType.MAINNET),
178 },
179 celo: {
180 tokens: formattedCeloTokens.filter(token => token.network === 'Mainnet'),
181 },
182 eos: {
183 tokens: formattedEosTokens.filter(token => token.network === 'Mainnet'),
184 },
185 },
186 // network name for test environments
187 testnet: {
188 eth: {
189 tokens: formattedErc20Tokens.filter(token => token.network === 'Testnet'),
190 },
191 xlm: {
192 tokens: formattedStellarTokens.filter(token => token.network === 'Testnet'),
193 },
194 algo: {
195 tokens: formattedAlgoTokens.filter(token => token.network === 'Testnet'),
196 },
197 ofc: {
198 tokens: formattedOfcCoins.filter(token => coins.get(token.type).network.type === NetworkType.TESTNET),
199 },
200 celo: {
201 tokens: formattedCeloTokens.filter(token => token.network === 'Testnet'),
202 },
203 eos: {
204 tokens: formattedEosTokens.filter(token => token.network === 'Testnet'),
205 },
206 },
207};
208
209/**
210 * Verify mainnet or testnet tokens
211 * @param tokens
212 */
213const verifyTokens = function (tokens) {
214 const verifiedTokens = {};
215 _.forEach(tokens, function (token) {
216 if (verifiedTokens[token.type]) {
217 throw new Error('token : ' + token.type + ' duplicated.');
218 }
219 verifiedTokens[token.type] = true;
220
221 if (token.tokenContractAddress && token.tokenContractAddress !== _.toLower(token.tokenContractAddress)) {
222 throw new Error('token contract: ' + token.type + ' is not all lower case: ' + token.tokenContractAddress);
223 }
224 });
225 return verifiedTokens;
226};
227
228const mainnetErc20Tokens = verifyTokens(tokens.bitcoin.eth.tokens);
229const mainnetStellarTokens = verifyTokens(tokens.bitcoin.xlm.tokens);
230export const mainnetTokens = _.assign({}, mainnetErc20Tokens, mainnetStellarTokens);
231
232const testnetErc20Tokens = verifyTokens(tokens.testnet.eth.tokens);
233const testnetStellarTokens = verifyTokens(tokens.testnet.xlm.tokens);
234export const testnetTokens = _.assign({}, testnetErc20Tokens, testnetStellarTokens);
235
236
237export const defaults = {
238 maxFee: 0.1e8,
239 maxFeeRate: 1000000,
240 minFeeRate: 5000,
241 fallbackFeeRate: 50000,
242 minOutputSize: 2730,
243 minInstantFeeRate: 10000,
244 bitgoEthAddress: '0x0f47ea803926926f299b7f1afc8460888d850f47',
245};
246
247// Supported cross-chain recovery routes. The coin to be recovered is the index, the valid coins for recipient wallets
248// are listed in the array.
249export const supportedCrossChainRecoveries = {
250 btc: ['bch', 'ltc', 'bsv'],
251 bch: ['btc', 'ltc', 'bsv'],
252 ltc: ['btc', 'bch', 'bsv'],
253 bsv: ['btc', 'ltc', 'bch'],
254};
255
256export type KrsProvider = {
257 feeType: 'flatUsd';
258 feeAmount: number;
259 supportedCoins: string[];
260 feeAddresses?: Record<string, string>
261}
262
263// KRS providers and their fee structures
264export const krsProviders: Record<string, KrsProvider> = {
265 keyternal: {
266 feeType: 'flatUsd',
267 feeAmount: 99,
268 supportedCoins: ['btc', 'eth'],
269 feeAddresses: {
270 btc: '', // TODO [BG-6965] Get address from Keyternal - recovery will fail for now until Keyternal is ready
271 },
272 },
273 bitgoKRSv2: {
274 feeType: 'flatUsd',
275 feeAmount: 0, // we will receive payments off-chain
276 supportedCoins: ['btc', 'eth'],
277 },
278 dai: {
279 feeType: 'flatUsd',
280 feeAmount: 0, // dai will receive payments off-chain
281 supportedCoins: ['btc', 'eth', 'xlm', 'xrp', 'dash', 'zec', 'ltc', 'bch', 'bsv', 'bcha'],
282 },
283};
284
285export const coinGeckoBaseUrl = 'https://api.coingecko.com/api/v3/';
286
287// TODO: once server starts returning eth address keychains, remove bitgoEthAddress
288/**
289 * Get the default (hardcoded) constants for a particular network.
290 *
291 * Note that this may not be the complete set of constants, and additional constants may get fetched
292 * from BitGo during the lifespan of a BitGo object.
293 * @param env
294 */
295export const defaultConstants = (env: EnvironmentName) => {
296 if (Environments[env] === undefined) {
297 throw Error(`invalid environment ${env}`);
298 }
299
300 const network = Environments[env].network;
301 return _.merge({}, defaults, tokens[network]);
302};