Skip to content

Commit

Permalink
Merge pull request #5372 from BitGo/WIN-4290
Browse files Browse the repository at this point in the history
feat: adding overrides for coredao for non-bitgo recovery support through wrw
  • Loading branch information
DinshawKothari authored Feb 3, 2025
2 parents 5bc43e7 + 0905dc6 commit 0bde625
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 5 deletions.
4 changes: 3 additions & 1 deletion modules/sdk-coin-coredao/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@
"@bitgo/abstract-eth": "^22.4.13",
"@bitgo/sdk-core": "^28.22.0",
"@bitgo/statics": "^50.22.0",
"@ethereumjs/common": "^2.6.5"
"@ethereumjs/common": "^2.6.5",
"@ethereumjs/tx": "^3.3.0",
"bn.js": "^5.2.1"
},
"devDependencies": {
"@bitgo/sdk-api": "^1.58.5",
Expand Down
41 changes: 40 additions & 1 deletion modules/sdk-coin-coredao/src/coredao.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { BaseCoin, BitGoBase, common, MPCAlgorithm } from '@bitgo/sdk-core';
import { BaseCoin as StaticsBaseCoin, coins } from '@bitgo/statics';
import { AbstractEthLikeNewCoins, recoveryBlockchainExplorerQuery } from '@bitgo/abstract-eth';
import { AbstractEthLikeNewCoins, optionalDeps, recoveryBlockchainExplorerQuery } from '@bitgo/abstract-eth';
import { TransactionBuilder } from './lib';
import BN from 'bn.js';

export class Coredao extends AbstractEthLikeNewCoins {
protected constructor(bitgo: BitGoBase, staticsCoin?: Readonly<StaticsBaseCoin>) {
Expand Down Expand Up @@ -31,4 +32,42 @@ export class Coredao extends AbstractEthLikeNewCoins {
const explorerUrl = common.Environments[this.bitgo.getEnv()].coredaoExplorerBaseUrl;
return await recoveryBlockchainExplorerQuery(query, explorerUrl as string, apiToken);
}

/** @inheritDoc */
async queryAddressBalance(address: string): Promise<BN> {
const result = await this.recoveryBlockchainExplorerQuery({
module: 'account',
action: 'balance',
address: address,
});
// throw if the result does not exist or the result is not a valid number
if (!result || !result.result || isNaN(Number(result.result))) {
throw new Error(`Could not obtain address balance for ${address} from the explorer, got: ${result.result}`);
}
result.result = result.result.toString();

return new optionalDeps.ethUtil.BN(result.result, 10);
}

/** @inheritDoc */
async getAddressNonce(address: string): Promise<number> {
// Get nonce for backup key
let nonce = 0;

const result = await this.recoveryBlockchainExplorerQuery({
module: 'account',
action: 'txlist',
sort: 'desc',
address: address,
});
if (!result || !Array.isArray(result.result)) {
throw new Error('Unable to find next nonce from the explorer, got: ' + JSON.stringify(result));
}
const backupKeyTxList = result.result;
if (backupKeyTxList.length > 0) {
// Calculate last nonce used
nonce = Math.max(...backupKeyTxList.filter((tx) => tx.from === address).map((tx) => tx.nonce as number)) + 1;
}
return nonce;
}
}
88 changes: 87 additions & 1 deletion modules/sdk-coin-coredao/test/unit/coredao.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import 'should';
import * as should from 'should';

import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
import { BitGoAPI } from '@bitgo/sdk-api';

import { Coredao, Tcoredao } from '../../src/index';
import nock from 'nock';

import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx';
import { stripHexPrefix } from '@ethereumjs/util';

const bitgo: TestBitGoAPI = TestBitGo.decorate(BitGoAPI, { env: 'test' });

Expand Down Expand Up @@ -39,4 +43,86 @@ describe('Coredao', function () {
tcoredao.allowsAccountConsolidations().should.equal(false);
});
});

describe('Build Recover Transaction', function () {
const bitgo = TestBitGo.decorate(BitGoAPI, { env: 'test' });
const getTxListRequest: Record<string, string> = {
module: 'account',
action: 'txlist',
sort: 'desc',
address: '0x594886d686261172b95fae7401841843504f156b',
};

const getTxListResponse: Record<string, unknown> = {
status: '1',
result: [
{
hash: '0xede855d43d70ea1bb75db63d4f75113dae0845f0d4bdb0b2d8bda55249c70812',
nonce: '23',
from: '0x594886d686261172b95fae7401841843504f156b',
},
],
message: 'OK',
};

const getBalanceRequest: Record<string, string> = {
module: 'account',
action: 'balance',
address: '0x594886d686261172b95fae7401841843504f156b',
};

const getBalanceResponse: Record<string, unknown> = {
status: '1',
result: 100000000000000000,
message: 'OK',
};

const mockData = {
recoveryDestination: '0x224056675da79dac836c330704e9b091a7fc2e1c',
userKeyData:
'{"iv":"1j/pXswlTT5JEkqmopflwA==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"yA1ls3UYGwk=","ct":"DWf+iaYWSig20VhHWiNYurPyONJO4rAMC0Erxp06SUlLVdLAiD4bHsjNhh3t8OeuVQLlAkfqDBOufVV6DGFcRaBK5KE/mjON2XSIVk+GW8K/ZbdxYpvyFX2zKQtONUP4OXhSrKghgJb7nHCTWtHFPuP734IIKTA1s+iPp4Ojafd7nwXh9uKV5lfD8hSKKt6oWcAqGIeyTk2EUl948wbKdxECgXA3noGHGeDEd4i17VnR44gu9A7A2YB2hQamrn5u1T6uAbNJomYcvD2szzo3bqEi9vFA8WMrX6MYvmTlH21NM5KsxZPPSIHaBlBL/w1bnwgrC6qLEQChnWOIinVepUmWj+NQWkRADbyz9gJLQZtYIcOO1YOoNMKr96a/0zKoktjOrL4d6e617RSWyeOC+OMCtIwBw/LHaQyFzCtuwdtSaG6xoDJIkT0mW7AFX6veu6xK6DM2S2/dNrFlF2cT8a0KO+L4uiA5QcsznPiYv7uUmC2SbX2nskBd2xe6+TpF9VcMMYNk6P01YXj+w9zVUtZRNRv9ke8rMq0Tg8LdorfDqCS15TB86P5iSEOgjP4xXzw5s+5QVhO1Y+4RQuDFW7tDRto0XWZlY7jFg/ NJ7PXOipNPasFjBKHROhM9gq5V4sT5opjbetUHqFGaJC4sRY4HvO9Y+oxFk7khJkK40EFlQzvDCkmDOTjVv/fb5N2kJjq7IGeZTsEeFA90fogUM2DGvwnBB0icggHLS2px40/RHlK2jz3V1XQp5ecddhLzc38/lik9MUsmtTlK3VB5WjGfDRSLBZqy1hjsvKlvKTbpVkg937X/Wun62c4kquYzSJIHd0UanAKd5D7O+oIoUZR7FgpbDQoutRhVflDTyYpOq7tSJOrYbjA1qEJVWJqeFIKdfQ9ZboT9aFq3aGQxuk7mz9CyrKVRQfPBrjzEbW8mKCqpJLUshKXkcETnlNzSBbOuPokIRz+nM3rlYFevk4oiA418QdH3rK+gPQv4NkKHGcZlb3Vzro85seOZVqzgITXadXUznBG0/mYmY+Iqe1zHI1nDDGrxCqzpK4v31M2e7xyPu/kMHZYKNvM0k2jy"}',
backupKeyData:
'{"iv":"LYD8I6UEfqMEmFgwC/YR+w==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"IxVKifwPPWU=","ct":"qw+MmwAoinYPgyAbVKGbKDKFaNv+tyj9W1ird+MdVrLBurYoccf1wESdBpLVIIEaljXHZD/uxXSxOHtIjM4tUpnwajI5fSG0pYa2p048uN4K54tBtCJqj1W1jPyDVdaGEi6TheI5jGjKPEnI3J4ZPbQ7BfeT0WceIX3S9dT+VETug6vAf4axU8phIejCRuHgnvtErx/xiKH3zSDnc3oOSjHfoz0ujR/ZLtb4FRPaXujgRDISqoR4CtN7io3myq2NArzNsYi6PluD/jp80My+1jImIrmDqY+DVgGTDBAOz97eL9lXS2uiw3VuoPUSsQCdCrIjnhsq0BtL/W53eZbrXH565Bykm6L6DqJLJeVOrIMUAG/An1X9iUSqmHDFr7SW2z6NCy624vC3ulBRUcji44XHcmm4eVU1SgZDlzB00wBTNxHObIVJwWX8ZP33bYjeav3WBClXrC3b0CqEVzY7F3nLT8I6q1bb0WrRzB3blX7Avf0FylnUN0QbN1mQLUA01LZYof3+gPFCfDEzT/qX8EpgcxLpGgd8T4sj7RKjv0rHBZ462l1yh4lN2PqmZW/10UkyTYYqr0Jw3neYFHQTLoS2FqBNkftyBYkO8eiVXvbeaHuCugiDBB963cNR3nE1a6UqHH1Ime1K6chAoUZlq5arfT3OPR4lPpWXXwigznNcUMMJVQhGVJI1siEOXR1b78fe12VTD1PKhRQC5J9UvE3nBRoXoEWkj1b3/UQfUF6ITQQYDg8lZYKdRYyOlrQDS56oVI1HF8uR0+IQSbtshWSw4OrN6FPHf6ZuhcwX3PSvZwXpNVndxKII8pgFAn0SyQAFsWVY/4OKCO/O+PJkg7ODcJo/S9BAqcxmInBWgnaLMEhCGl77xVELW/B8wVOEVC3ApGtQsjg1CQg/PWsJY5EDkVykejiRTZyim9C84hZSL6bSpDCGOWnEEkI1U9gQlE0kkj1ZBQpe7xxOJ7jhWqNC800orDNsSIdOlDvD/SXX1zHf88aby9VNkUUwb6D9cUItuVkL4gllk/7aTnt0UIoiy8X6Z3w3z3tQi4i9pEZNeM3kG0nlQX12Q7788YNR"}',
walletRootAddress: '0x594886d686261172b95fae7401841843504f156b',
walletPassphrase: 'test_1234_test',
explorerUrl: 'https://app.test.btcs.network',
getTxListRequest: getTxListRequest,
getTxListResponse: getTxListResponse,
getBalanceRequest: getBalanceRequest,
getBalanceResponse: getBalanceResponse,
};

it('should generate a signed non-bitgo recovery tx', async () => {
nock(mockData.explorerUrl)
.get('/api')
.twice()
.query(mockData.getTxListRequest)
.reply(200, mockData.getTxListResponse);
nock(mockData.explorerUrl).get('/api').query(mockData.getBalanceRequest).reply(200, mockData.getBalanceResponse);

const baseCoin: any = bitgo.coin('tcoredao');
const transaction = await baseCoin.recover({
userKey: mockData.userKeyData,
backupKey: mockData.backupKeyData,
walletContractAddress: mockData.walletRootAddress,
walletPassphrase: mockData.walletPassphrase,
recoveryDestination: mockData.recoveryDestination,
isTss: true,
eip1559: { maxFeePerGas: 20000000000, maxPriorityFeePerGas: 10000000000 },
gasLimit: 500000,
replayProtectionOptions: {
chain: 1115,
hardfork: 'london',
},
});
should.exist(transaction);
transaction.should.have.property('id');
transaction.should.have.property('tx');
const tx = FeeMarketEIP1559Transaction.fromSerializedTx(Buffer.from(stripHexPrefix(transaction.tx), 'hex'));
tx.getSenderAddress().toString().should.equal(mockData.walletRootAddress);
const jsonTx = tx.toJSON();
jsonTx.chainId?.should.equal('0x45b');
jsonTx.to?.should.equal(mockData.recoveryDestination);
});
});
});
4 changes: 2 additions & 2 deletions modules/sdk-core/src/bitgo/environments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ const mainnetBase: EnvironmentTemplate = {
hmacVerificationEnforced: true,
suiNodeUrl: 'https://fullnode.mainnet.sui.io',
etcNodeUrl: 'https://etc.blockscout.com',
coredaoExplorerBaseUrl: 'https://scan.coredao.org/',
coredaoExplorerBaseUrl: 'https://app.coredao.org/',
oasExplorerBaseUrl: 'https://explorer.oasys.games',
rosettaNodeURL: 'http://localhost:8081', //TODO(WIN-4242): update when rosetta node is available
};
Expand Down Expand Up @@ -220,7 +220,7 @@ const testnetBase: EnvironmentTemplate = {
hmacVerificationEnforced: false,
suiNodeUrl: 'https://fullnode.testnet.sui.io',
etcNodeUrl: 'https://etc-mordor.blockscout.com',
coredaoExplorerBaseUrl: 'https://scan.test.btcs.network',
coredaoExplorerBaseUrl: 'https://app.test.btcs.network',
oasExplorerBaseUrl: 'https://explorer.testnet.oasys.games',
rosettaNodeURL: 'http://localhost:8081', //TODO(WIN-4242): update when rosetta node is available
};
Expand Down

0 comments on commit 0bde625

Please sign in to comment.