Skip to content

Commit

Permalink
Merge pull request #4021 from BitGo/WP-847-add-avax-token-support-in-…
Browse files Browse the repository at this point in the history
…recovery-method

feat(sdk-coin-avaxc): add avax token support in recovery method
  • Loading branch information
alia-bitgo authored Oct 30, 2023
2 parents 8f9d08c + 2ace2a1 commit 68e4f44
Show file tree
Hide file tree
Showing 3 changed files with 387 additions and 9 deletions.
91 changes: 83 additions & 8 deletions modules/sdk-coin-avaxc/src/avaxc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,31 @@ import {
} from './iface';
import { AvaxpLib } from '@bitgo/sdk-coin-avaxp';

const AVAXC_TOKENS = {
// mainnet tokens
'avaxc:png': '0x60781c2586d68229fde47564546784ab3faca982',
'avaxc:xava': '0xd1c3f94de7e5b45fa4edbba472491a9f4b166fc4',
'avaxc:klo': '0xb27c8941a7df8958a1778c0259f76d1f8b711c35',
'avaxc:joe': '0x6e84a6216ea6dacc71ee8e6b0a5b7322eebc0fdd',
'avaxc:qi': '0x8729438eb15e2c8b576fcc6aecda6a148776c0f5',
'avaxc:usdt': '0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7',
'avaxc:usdc': '0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e',
'avaxc:link': '0x5947bb275c521040051d82396192181b413227a3',
'avaxc:cai': '0x48f88a3fe843ccb0b5003e70b4192c1d7448bef0',
'avaxc:usdc-e': '0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664',
'avaxc:dai-e': '0xd586e7f844cea2f87f50152665bcbc2c279d8d70',
'avaxc:usdt-e': '0xc7198437980c041c805a1edcba50c1ce5db95118',
'avaxc:wbtc-e': '0x50b7545627a5162f82a992c33b87adc75187b218',
'avaxc:weth-e': '0x49d5c2bdffac6ce2bfdb6640f4f80f226bc10bab',
'avaxc:aave-e': '0x63a72806098bd3d9520cc43356dd78afe5d386d9',
'avaxc:usdc-wormhole': '0x543672e9cbec728cbba9c3ccd99ed80ac3607fa8',
'avaxc:btc-b': '0x152b9d0fdc40c096757f570a51e494bd4b943e50',

// testnet tokens
'tavaxc:link': '0x0b9d5d9136855f6fec3c0993fee6e9ce8a297846',
'tavaxc:opm': '0x9a25414c8a41599cb7048f2e4dd42db02c1de487',
};

export class AvaxC extends BaseCoin {
static hopTransactionSalt = 'bitgoHopAddressRequestSalt';

Expand Down Expand Up @@ -340,6 +365,25 @@ export class AvaxC extends BaseCoin {
return new optionalDeps.ethUtil.BN(result.result, 10);
}

/**
* Queries Snowtrace for the token balance of an address
* @param {string} address - the AVAXC address
* @returns {Promise<BigNumber>} address balance
*/
async queryAddressTokenBalance(address: string, contractAddress: string): Promise<any> {
const result = await this.recoveryBlockchainExplorerQuery({
module: 'account',
action: 'tokenbalance',
address: address,
contractaddress: contractAddress,
});
// throw if the result does not exist or the result is not a valid number
if (!result || !result.result || isNaN(result.result)) {
throw new Error(`Could not obtain address token balance for ${address} from Snowtrace, got: ${result.result}`);
}
return new optionalDeps.ethUtil.BN(result.result, 10);
}

/**
* Queries the contract (via Snowtrace) for the next sequence ID
* @param {string} address - address of the contract
Expand Down Expand Up @@ -523,6 +567,20 @@ export class AvaxC extends BaseCoin {
throw new Error('invalid walletContractAddress');
}

let tokenName;
if (params.tokenContractAddress) {
if (!this.isValidAddress(params.tokenContractAddress)) {
throw new Error('invalid tokenContractAddress');
}

tokenName = (Object.keys(AVAXC_TOKENS) as (keyof typeof AVAXC_TOKENS)[]).find((key) => {
return AVAXC_TOKENS[key] === params.tokenContractAddress;
});
if (_.isUndefined(tokenName)) {
throw new Error('token not supported');
}
}

if (_.isUndefined(params.recoveryDestination) || !this.isValidAddress(params.recoveryDestination)) {
throw new Error('invalid recoveryDestination');
}
Expand Down Expand Up @@ -590,8 +648,14 @@ export class AvaxC extends BaseCoin {
);
}

// get balance of wallet and deduct fees to get transaction amount
const txAmount = await this.queryAddressBalance(params.walletContractAddress);
let txAmount;
if (params.tokenContractAddress) {
// get token balance of wallet
txAmount = await this.queryAddressTokenBalance(params.walletContractAddress, params.tokenContractAddress);
} else {
// get balance of wallet and deduct fees to get transaction amount
txAmount = await this.queryAddressBalance(params.walletContractAddress);
}

// build recipients object
const recipients = [
Expand Down Expand Up @@ -626,6 +690,7 @@ export class AvaxC extends BaseCoin {
operationHash,
signature,
gasLimit: gasLimit.toString(10),
tokenContractAddress: params.tokenContractAddress,
};

const txBuilder = this.getTransactionBuilder() as TransactionBuilder;
Expand All @@ -646,12 +711,22 @@ export class AvaxC extends BaseCoin {
...txFee,
gasLimit: gasLimit.toString(),
});
txBuilder
.transfer()
.amount(recipients[0].amount)
.contractSequenceId(sequenceId)
.expirationTime(this.getDefaultExpireTime())
.to(params.recoveryDestination);
if (params.tokenContractAddress) {
txBuilder
.transfer()
.coin(tokenName)
.amount(recipients[0].amount)
.contractSequenceId(sequenceId)
.expirationTime(this.getDefaultExpireTime())
.to(params.recoveryDestination);
} else {
txBuilder
.transfer()
.amount(recipients[0].amount)
.contractSequenceId(sequenceId)
.expirationTime(this.getDefaultExpireTime())
.to(params.recoveryDestination);
}

if (isUnsignedSweep) {
const tx = await txBuilder.build();
Expand Down
123 changes: 123 additions & 0 deletions modules/sdk-coin-avaxc/test/resources/avaxc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,126 @@ export const EXPORT_C = {
threshold: 2,
fee: '1000025',
};

const hotWalletRecoveryUser = {
userKey:
'{"iv":"bqPszODXX/dZCdQh3Rtnkw==","v":1,"iter":10000,"ks":256,"ts":64,"mode"' +
':"ccm","adata":"","cipher":"aes","salt":"g9Z7kqquE78=","ct":"o6JABeNwv0kEcc' +
'QGuHdrfFiBQ4QE491cP22R8QhREuosAAMezqr7hbT3Q77i/agHK9QXPkUaR/U52TOeX/H6Q7jTY' +
'EETNxTe+ma3wVrOnPEX6svKERvUCo/IzIDhwriWeT35lZNVxxJGwtpCj2wtelIk67Gzgns="}',
backupKey:
'{"iv":"oE4sYous0T7vfzWl6sOIWg==","v":1,"iter":10000,"ks":256,"ts":64,"mode"' +
':"ccm","adata":"","cipher":"aes","salt":"I+cvCbXdrfc=","ct":"0QyUMV78WoBILM' +
'32agbvbmsycaN71PIkvMs2qJaPweQ+Gt7lTpbjz88iQFW16tR4tdGRLuhadQZSrlzHEl7CzhYLn' +
'yBAqacJN3To4teK3VTXYa8xjlpMfLTR47i4zn51qfNx9mR/b5D0DFA2U5SrOG8SsSn+fm8="}',
walletContractAddress: '0xe93f3fb23419b3b2e9b3edcfa79c94ad1d84b381',
walletPassphrase: 'Ghghjkg!455544llll',
backupKeyAddress: '0xd43c4549c80e313293897c545e99d652e98d86da',
};

const coldWalletRecoveryUser = {
userKey:
'xpub661MyMwAqRbcFpb8nBxgG2rkwyHoKfWdgGxGdz84zagAwUwedkiEePHCe6casjvWkFUv4jLYToKRu3S3QANYjytpES8HNZFWZGcCa5hcyHs',
backupKey:
'xpub661MyMwAqRbcG5ZCcZSAst64sWbLBvd3APKFDJcr9u4AosNbyUGJR6PcSUdxte44AoENFQyy7rMbd3tdZ1kJxw3TQwnnCsfipUx2BNNppog',
walletContractAddress: '0xfe0340c352dd6807f61fa57ba2b4c3fa21b01ec0',
walletPassphrase: 'Ghghjkg!455544llll',
backupKeyAddress: '0xee433c84a778e74b6af4c627c8c739b71b11b8d9',
};

export const recoveryUsers = {
hotWalletRecoveryUser,
coldWalletRecoveryUser,
};

const addressNonceResponse1 = {
status: '0',
message: 'No transactions found',
result: [],
};

const addressNonceResponse2 = {
status: '1',
message: 'OK',
result: [
{
blockNumber: '27139350',
timeStamp: '1698339817',
hash: '0x7dff6ba726df2d88022f7e26b6bb88253b19b50c9096e479742dcf01ea30a1a4',
nonce: '0',
blockHash: '0x97cf3e28e86e2909996c0e5bccc57aa46bb15dbe06d2dd880dcb8c6a650005b7',
transactionIndex: '0',
from: '0xee433c84a778e74b6af4c627c8c739b71b11b8d9',
to: '0xfe0340c352dd6807f61fa57ba2b4c3fa21b01ec0',
value: '0',
gas: '500000',
gasPrice: '30000000000',
isError: '0',
txreceipt_status: '1',
input:
'0x0dcd7a6c000000000000000000000000e93f3fb23419b3b2e9b3edcfa79c94ad1d84b3810000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000b9d5d9136855f6fec3c0993fee6e9ce8a297846000000000000000000000000000000000000000000000000000000006543d600000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000004132a881d1ad3ba1be799c75d45a1ce9503f607f0eba738e221810af7acc86b810116eebe2dcd0ed83ec25722d239b2104435f27901e6681c396d30e65f5bc36de1b00000000000000000000000000000000000000000000000000000000000000',
contractAddress: '',
cumulativeGasUsed: '100924',
gasUsed: '100924',
confirmations: '500',
methodId: '0x0dcd7a6c',
functionName: '',
},
{
blockNumber: '27139407',
timeStamp: '1698339962',
hash: '0xaf843a012e814e13cefa451d876851019ad91bca199a9eee2208c6d4fd0f4496',
nonce: '1',
blockHash: '0xc4ef44536185511c13e5d59bd083f347f5c06811dc9a0bbee94e1d015d219b8e',
transactionIndex: '0',
from: '0xee433c84a778e74b6af4c627c8c739b71b11b8d9',
to: '0xfe0340c352dd6807f61fa57ba2b4c3fa21b01ec0',
value: '0',
gas: '500000',
gasPrice: '30000000000',
isError: '0',
txreceipt_status: '1',
input:
'0x39125215000000000000000000000000e93f3fb23419b3b2e9b3edcfa79c94ad1d84b3810000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000006543d6ae000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041ee83c22815fd8e015321b3fbd463799aa092c942ca2cbf6f8fefb936dde0a6f670381934f9d3f7506aead0438fcd53ef686905cc963d42de49e0261bd15a105e1b00000000000000000000000000000000000000000000000000000000000000',
contractAddress: '',
cumulativeGasUsed: '99542',
gasUsed: '99542',
confirmations: '443',
methodId: '0x39125215',
functionName: '',
},
],
};

const backupAddressBalanceResponse = {
status: '1',
message: 'OK',
result: '43986020000000000',
};

const addressTokenBalanceResponse = {
status: '1',
message: 'OK',
result: '1500000000000000000',
};

const sequenceIdResponse1 = {
jsonrpc: '2.0',
id: 1,
result: '0x0000000000000000000000000000000000000000000000000000000000000003',
};

const sequenceIdResponse2 = {
jsonrpc: '2.0',
id: 1,
result: '0x000000000000000000000000000000000000000000000000000000000000000d',
};

export const endpointResponses = {
addressNonceResponse1,
addressNonceResponse2,
backupAddressBalanceResponse,
addressTokenBalanceResponse,
sequenceIdResponse1,
sequenceIdResponse2,
};
Loading

0 comments on commit 68e4f44

Please sign in to comment.