UNPKG

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