Skip to content

Commit 2a310c6

Browse files
Merge remote-tracking branch 'origin/master' into rel/latest
2 parents 4d66f92 + 7da169f commit 2a310c6

File tree

60 files changed

+2106
-1158
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+2106
-1158
lines changed

examples/ts/eth/recover-eth.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Recover ETH from a multi-sig wallet
3+
*
4+
* This script demonstrates how to generate an unsigned recovery transaction for a
5+
* Multi-Sig ETH wallet.
6+
*/
7+
8+
import { BitGo } from 'bitgo';
9+
import { Hteth } from '@bitgo/sdk-coin-eth';
10+
11+
// Set up BitGo object
12+
const bitgo = new BitGo({ env: 'test' }); // change to 'prod' for mainnet
13+
bitgo.register('hteth', Hteth.createInstance);
14+
15+
async function recoverEth() {
16+
try {
17+
// Use the recovery with API key for output
18+
const recovery = (await (bitgo.coin('hteth') as Hteth).recover({
19+
userKey: 'user-public-key',
20+
backupKey: 'backup-public-key',
21+
walletContractAddress: 'Address-of-your-multisig-wallet',
22+
recoveryDestination: 'Address-To-Recover-Funds-To',
23+
isUnsignedSweep: true,
24+
apiKey: 'Add Your Etherscan ApiKey here',
25+
})) as any;
26+
27+
// Print the recovery transaction hex
28+
console.log('Recovery transaction hex:');
29+
console.log(recovery.tx);
30+
31+
// Print additional information
32+
console.log('\nFull recovery object:', JSON.stringify(recovery, null, 2));
33+
34+
return recovery;
35+
} catch (e) {
36+
console.error('Error performing recovery:', e);
37+
throw e;
38+
}
39+
}
40+
41+
// Execute the recovery function
42+
recoverEth()
43+
.then(() => process.exit(0))
44+
.catch((e) => {
45+
console.error(e);
46+
process.exit(1);
47+
});
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* Copyright 2025, BitGo, Inc. All Rights Reserved.
3+
*/
4+
import { BitGoAPI } from '@bitgo/sdk-api';
5+
import { Trx } from '@bitgo/sdk-coin-trx';
6+
import * as fs from 'fs';
7+
import BigNumber from 'bignumber.js';
8+
require('dotenv').config({ path: '../../.env' });
9+
10+
const bitgo = new BitGoAPI({
11+
accessToken: '',
12+
env: 'prod',
13+
});
14+
15+
const coin = 'trx';
16+
bitgo.register(coin, Trx.createInstance);
17+
18+
const walletId = '';
19+
20+
// This script is used for fetching the amount of TRX that can be consolidated
21+
// specifically for wallets which use gas tank to fund addresses in case of token deposit (we block 36 trx for each unique token that is present in the address)
22+
async function main() {
23+
const wallet = await bitgo.coin(coin).wallets().get({ id: walletId });
24+
let prevId = undefined;
25+
let index = 1;
26+
27+
do {
28+
const addresses = await wallet.addresses({ includeBalances: true, prevId });
29+
prevId = addresses.nextBatchPrevId;
30+
31+
for (const { address, balance, needsConsolidation, tokenConsolidationState } of addresses.addresses) {
32+
const { tokens, spendableBalanceString } = balance;
33+
let tokenCounter = 0;
34+
console.log(`Address ${index}: ${address}`);
35+
const trxSpendableBalance = new BigNumber(spendableBalanceString || '0');
36+
console.log('TRX balance: ', trxSpendableBalance);
37+
38+
// Process token balances if any
39+
for (const key in tokens) {
40+
const tokenSpendableBalance = new BigNumber(tokens[key]?.spendableBalanceString || '0');
41+
console.log(`Token: ${key}, Token spendable balance: ${tokenSpendableBalance}`);
42+
if (tokenSpendableBalance.isGreaterThan(0)) {
43+
tokenCounter += 1;
44+
}
45+
}
46+
47+
if (tokenCounter > 0 && trxSpendableBalance.isGreaterThanOrEqualTo(0)) {
48+
// This is for enteprises which use gas tank for funding receive addresses with TRX on token deposit
49+
// we block 36 TRX for each unique token that needs to be consolidated and arrive at the amout of TRX that can be consolidated
50+
const trxBalanceThatCanBeConsolidated = BigNumber.max(trxSpendableBalance.minus(tokenCounter * 36000000), 0);
51+
const data = `${address}, No of tokens: ${tokenCounter}, TRX balance: ${trxSpendableBalance}, TRX balance that can be consolidated: ${trxBalanceThatCanBeConsolidated}, V1 flag: ${needsConsolidation}, V2 flag: ${tokenConsolidationState['trx']}\n`;
52+
fs.appendFileSync('addresses-with-token-balance-and-trx-to-be-consolidated.txt', data);
53+
} else if (tokenCounter == 0 && trxSpendableBalance.isGreaterThanOrEqualTo(0)) {
54+
// if address does not have any tokens and has more than 1 TRX, then we need to subtract 1 TRX from it (min TRX thats left in the address)
55+
const trxBalanceThatCanBeConsolidated = BigNumber.max(trxSpendableBalance.minus(1000000), 0);
56+
const data = `${address}, No of tokens: ${tokenCounter}, TRX balance: ${trxSpendableBalance}, TRX balance that can be consolidated: ${trxBalanceThatCanBeConsolidated}, V1 flag: ${needsConsolidation}, V2 flag: ${tokenConsolidationState['trx']}\n`;
57+
fs.appendFileSync('addresses-without-token-balance-and-trx-to-be-consolidated.txt', data);
58+
}
59+
60+
index += 1;
61+
}
62+
} while (prevId !== undefined);
63+
}
64+
65+
main().catch((e) => console.error(e));

modules/abstract-cosmos/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
},
4040
"dependencies": {
4141
"@bitgo/sdk-core": "^35.9.0",
42+
"@bitgo/sdk-api": "^1.65.1",
4243
"@bitgo/sdk-lib-mpc": "^10.6.0",
4344
"@bitgo/secp256k1": "^1.4.0",
4445
"@bitgo/statics": "^55.3.0",
@@ -54,6 +55,7 @@
5455
"superagent": "^9.0.1"
5556
},
5657
"devDependencies": {
58+
"@bitgo/sdk-test": "^8.0.97",
5759
"@types/lodash": "^4.14.183"
5860
},
5961
"gitHead": "18e460ddf02de2dbf13c2aa243478188fb539f0c"

modules/abstract-cosmos/src/cosmosToken.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ export class CosmosToken extends CosmosCoin {
1515
return (bitgo: BitGoBase) => new CosmosToken(bitgo, config);
1616
}
1717

18-
static createTokenConstructors(): NamedCoinConstructor[] {
18+
static createTokenConstructors(
19+
tokenConfigs: CosmosTokenConfig[] = [...tokens.bitcoin.cosmos.tokens, ...tokens.testnet.cosmos.tokens]
20+
): NamedCoinConstructor[] {
1921
const tokensCtors: NamedCoinConstructor[] = [];
20-
for (const token of [...tokens.bitcoin.cosmos.tokens, ...tokens.testnet.cosmos.tokens]) {
22+
for (const token of tokenConfigs) {
2123
const tokenConstructor = CosmosToken.createTokenConstructor(token);
2224
tokensCtors.push({ name: token.type, coinConstructor: tokenConstructor });
2325
}
@@ -53,8 +55,8 @@ export class CosmosToken extends CosmosCoin {
5355
}
5456

5557
getFullName(): string {
56-
// Eg - returns "Atom Token", "Osmo Token"
57-
return `${this.tokenConfig.coin.charAt(0).toUpperCase()}${this.tokenConfig.coin.slice(1)} Token`;
58+
const displayCoin = this.getFamily();
59+
return `${displayCoin.charAt(0).toUpperCase() + displayCoin.slice(1)} Token`;
5860
}
5961

6062
getBaseFactor(): number {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import 'should';
2+
3+
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
4+
import { BitGoAPI } from '@bitgo/sdk-api';
5+
import { CosmosToken } from '../../src';
6+
7+
describe('Cosmos Tokens', function () {
8+
let bitgo: TestBitGoAPI;
9+
let mainnetCosmosToken;
10+
let testnetCosmosToken;
11+
const testnetTokenName = 'thash:ylds';
12+
const mainnetTokenName = 'hash:ylds';
13+
14+
before(function () {
15+
bitgo = TestBitGo.decorate(BitGoAPI, { env: 'test' });
16+
CosmosToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
17+
bitgo.safeRegister(name, coinConstructor);
18+
});
19+
bitgo.initializeTestVars();
20+
mainnetCosmosToken = bitgo.coin(mainnetTokenName);
21+
testnetCosmosToken = bitgo.coin(testnetTokenName);
22+
});
23+
24+
it('should return constants for Hash YLDS testnet token', function () {
25+
testnetCosmosToken.getChain().should.equal(testnetTokenName);
26+
testnetCosmosToken.getBaseChain().should.equal('thash');
27+
testnetCosmosToken.getFullName().should.equal('Hash Token');
28+
testnetCosmosToken.getBaseFactor().should.equal(1e6);
29+
testnetCosmosToken.type.should.equal(testnetTokenName);
30+
testnetCosmosToken.name.should.equal('Testnet YLDS Token');
31+
testnetCosmosToken.coin.should.equal('thash');
32+
testnetCosmosToken.network.should.equal('Testnet');
33+
testnetCosmosToken.denom.should.equal('uylds.fcc');
34+
testnetCosmosToken.decimalPlaces.should.equal(6);
35+
});
36+
37+
it('should return constants for Hash YLDS mainnet token', function () {
38+
mainnetCosmosToken.getChain().should.equal(mainnetTokenName);
39+
mainnetCosmosToken.getBaseChain().should.equal('hash');
40+
mainnetCosmosToken.getFullName().should.equal('Hash Token');
41+
mainnetCosmosToken.getBaseFactor().should.equal(1e6);
42+
mainnetCosmosToken.type.should.equal(mainnetTokenName);
43+
mainnetCosmosToken.name.should.equal('YLDS Token');
44+
mainnetCosmosToken.coin.should.equal('hash');
45+
mainnetCosmosToken.network.should.equal('Mainnet');
46+
mainnetCosmosToken.denom.should.equal('uylds.fcc');
47+
mainnetCosmosToken.decimalPlaces.should.equal(6);
48+
});
49+
});

0 commit comments

Comments
 (0)