Skip to content

Commit 709de2e

Browse files
Merge pull request #7906 from BitGo/rel/latest-merge-conflict-4
Rel/latest merge conflict 4
2 parents 51d0238 + 73bc58e commit 709de2e

File tree

9 files changed

+228
-15
lines changed

9 files changed

+228
-15
lines changed

modules/abstract-utxo/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
"@bitgo/utxo-core": "^1.30.0",
6969
"@bitgo/utxo-lib": "^11.19.1",
7070
"@bitgo/utxo-ord": "^1.22.22",
71-
"@bitgo/wasm-utxo": "^1.22.0",
71+
"@bitgo/wasm-utxo": "^1.24.0",
7272
"@types/lodash": "^4.14.121",
7373
"@types/superagent": "4.1.15",
7474
"bignumber.js": "^9.0.2",

modules/statics/src/tokenConfig.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,7 +1192,7 @@ const mergeEthLikeTokenMap = (...maps: EthLikeTokenMap[]): EthLikeTokenMap => {
11921192
return mergedMap;
11931193
};
11941194

1195-
const getFormattedTokensByNetwork = (network: 'Mainnet' | 'Testnet', coinMap: typeof coins) => {
1195+
export const getFormattedTokensByNetwork = (network: 'Mainnet' | 'Testnet', coinMap: typeof coins) => {
11961196
const networkType = network === 'Mainnet' ? NetworkType.MAINNET : NetworkType.TESTNET;
11971197

11981198
const ethLikeTokenMap = getEthLikeTokens(network, TokenTypeEnum.ERC20);
@@ -1358,7 +1358,7 @@ export const getFormattedTokens = (coinMap = coins): Tokens => {
13581358
* Verify mainnet or testnet tokens
13591359
* @param tokens
13601360
*/
1361-
const verifyTokens = function (tokens: BaseTokenConfig[]) {
1361+
export const verifyTokens = function (tokens: BaseTokenConfig[]) {
13621362
const verifiedTokens: Record<string, boolean> = {};
13631363
tokens.forEach((token) => {
13641364
if (verifiedTokens[token.type]) {

modules/statics/test/unit/tokenConfigTests.ts

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@ import {
1414
getFormattedEthLikeTokenConfig,
1515
getEthLikeTokens,
1616
getFormattedTokens,
17+
getFormattedTokensByNetwork,
18+
verifyTokens,
1719
EthLikeTokenConfig,
1820
TokenTypeEnum,
21+
BaseTokenConfig,
22+
BaseContractAddressConfig,
1923
} from '../../src/tokenConfig';
2024
import { EthLikeERC20Token } from '../../src/account';
2125

@@ -552,4 +556,209 @@ describe('EthLike Token Config Functions', function () {
552556
});
553557
});
554558
});
559+
560+
describe('getFormattedTokensByNetwork', function () {
561+
it('should return tokens for Mainnet network', function () {
562+
const result = getFormattedTokensByNetwork('Mainnet', coins);
563+
564+
result.should.be.an.Object();
565+
result.should.have.property('eth');
566+
result.eth.should.have.property('tokens');
567+
result.eth.should.have.property('nfts');
568+
569+
// All eth tokens should be Mainnet
570+
result.eth.tokens.forEach((token) => {
571+
token.network.should.equal('Mainnet');
572+
});
573+
});
574+
575+
it('should return tokens for Testnet network', function () {
576+
const result = getFormattedTokensByNetwork('Testnet', coins);
577+
578+
result.should.be.an.Object();
579+
result.should.have.property('eth');
580+
result.eth.should.have.property('tokens');
581+
result.eth.should.have.property('nfts');
582+
583+
// All eth tokens should be Testnet
584+
result.eth.tokens.forEach((token) => {
585+
token.network.should.equal('Testnet');
586+
});
587+
});
588+
589+
it('should return the same chain keys for both networks', function () {
590+
const mainnetResult = getFormattedTokensByNetwork('Mainnet', coins);
591+
const testnetResult = getFormattedTokensByNetwork('Testnet', coins);
592+
593+
const mainnetKeys = Object.keys(mainnetResult).sort();
594+
const testnetKeys = Object.keys(testnetResult).sort();
595+
596+
mainnetKeys.should.deepEqual(testnetKeys);
597+
});
598+
599+
it('should have no duplicate token types within any chain', function () {
600+
const mainnetResult = getFormattedTokensByNetwork('Mainnet', coins);
601+
const testnetResult = getFormattedTokensByNetwork('Testnet', coins);
602+
603+
// Check for duplicates in Mainnet
604+
Object.entries(mainnetResult).forEach(([chain, chainData]) => {
605+
if (chainData.tokens && chainData.tokens.length > 0) {
606+
const tokenTypes = chainData.tokens.map((t) => t.type);
607+
const uniqueTokenTypes = new Set(tokenTypes);
608+
const duplicates = tokenTypes.filter((t, i) => tokenTypes.indexOf(t) !== i);
609+
tokenTypes.length.should.equal(
610+
uniqueTokenTypes.size,
611+
`Mainnet ${chain} has duplicate token types: ${duplicates}`
612+
);
613+
}
614+
});
615+
616+
// Check for duplicates in Testnet
617+
Object.entries(testnetResult).forEach(([chain, chainData]) => {
618+
if (chainData.tokens && chainData.tokens.length > 0) {
619+
const tokenTypes = chainData.tokens.map((t) => t.type);
620+
const uniqueTokenTypes = new Set(tokenTypes);
621+
const duplicates = tokenTypes.filter((t, i) => tokenTypes.indexOf(t) !== i);
622+
tokenTypes.length.should.equal(
623+
uniqueTokenTypes.size,
624+
`Testnet ${chain} has duplicate token types: ${duplicates}`
625+
);
626+
}
627+
});
628+
});
629+
630+
it('should filter tokens correctly by network type', function () {
631+
const mainnetResult = getFormattedTokensByNetwork('Mainnet', coins);
632+
const testnetResult = getFormattedTokensByNetwork('Testnet', coins);
633+
634+
// Verify no testnet tokens in mainnet result
635+
Object.values(mainnetResult).forEach((chainData) => {
636+
if (chainData.tokens && chainData.tokens.length > 0) {
637+
chainData.tokens.forEach((token) => {
638+
if (token && token.network) {
639+
token.network.should.equal('Mainnet');
640+
}
641+
});
642+
}
643+
if ('nfts' in chainData && chainData.nfts && chainData.nfts.length > 0) {
644+
chainData.nfts.forEach((nft) => {
645+
if (nft && nft.network) {
646+
nft.network.should.equal('Mainnet');
647+
}
648+
});
649+
}
650+
});
651+
652+
// Verify no mainnet tokens in testnet result
653+
Object.values(testnetResult).forEach((chainData) => {
654+
if (chainData.tokens && chainData.tokens.length > 0) {
655+
chainData.tokens.forEach((token) => {
656+
if (token && token.network) {
657+
token.network.should.equal('Testnet');
658+
}
659+
});
660+
}
661+
if ('nfts' in chainData && chainData.nfts && chainData.nfts.length > 0) {
662+
chainData.nfts.forEach((nft) => {
663+
if (nft && nft.network) {
664+
nft.network.should.equal('Testnet');
665+
}
666+
});
667+
}
668+
});
669+
});
670+
});
671+
672+
describe('verifyTokens', function () {
673+
it('should return verified tokens record when no duplicates exist', function () {
674+
const mockTokens: BaseTokenConfig[] = [
675+
{ type: 'token1', coin: 'eth', name: 'Token 1', decimalPlaces: 18 },
676+
{ type: 'token2', coin: 'eth', name: 'Token 2', decimalPlaces: 18 },
677+
{ type: 'token3', coin: 'eth', name: 'Token 3', decimalPlaces: 6 },
678+
];
679+
680+
const result = verifyTokens(mockTokens);
681+
682+
result.should.be.an.Object();
683+
result.should.have.property('token1', true);
684+
result.should.have.property('token2', true);
685+
result.should.have.property('token3', true);
686+
});
687+
688+
it('should throw an error when duplicate token types exist', function () {
689+
const mockTokensWithDuplicates: BaseTokenConfig[] = [
690+
{ type: 'token1', coin: 'eth', name: 'Token 1', decimalPlaces: 18 },
691+
{ type: 'token2', coin: 'eth', name: 'Token 2', decimalPlaces: 18 },
692+
{ type: 'token1', coin: 'eth', name: 'Token 1 Duplicate', decimalPlaces: 18 }, // Duplicate
693+
];
694+
695+
(() => {
696+
verifyTokens(mockTokensWithDuplicates);
697+
}).should.throw('token : token1 duplicated.');
698+
});
699+
700+
it('should throw an error when token contract address is not lowercase', function () {
701+
const mockTokensWithUppercaseAddress: BaseContractAddressConfig[] = [
702+
{
703+
type: 'token1',
704+
coin: 'eth',
705+
name: 'Token 1',
706+
decimalPlaces: 18,
707+
network: 'Mainnet',
708+
tokenContractAddress: '0xAbCdEf1234567890AbCdEf1234567890AbCdEf12', // Mixed case
709+
},
710+
];
711+
712+
(() => {
713+
verifyTokens(mockTokensWithUppercaseAddress);
714+
}).should.throw(/token contract: token1 is not all lower case/);
715+
});
716+
717+
it('should pass when token contract address is lowercase', function () {
718+
const mockTokensWithLowercaseAddress: BaseContractAddressConfig[] = [
719+
{
720+
type: 'token1',
721+
coin: 'eth',
722+
name: 'Token 1',
723+
decimalPlaces: 18,
724+
network: 'Mainnet',
725+
tokenContractAddress: '0xabcdef1234567890abcdef1234567890abcdef12', // All lowercase
726+
},
727+
];
728+
729+
const result = verifyTokens(mockTokensWithLowercaseAddress);
730+
result.should.have.property('token1', true);
731+
});
732+
733+
it('should handle empty token array', function () {
734+
const result = verifyTokens([]);
735+
736+
result.should.be.an.Object();
737+
Object.keys(result).length.should.equal(0);
738+
});
739+
740+
it('should verify real tokens from getFormattedTokens have no duplicates', function () {
741+
const formattedTokens = getFormattedTokens();
742+
743+
// Test mainnet eth tokens
744+
(() => {
745+
verifyTokens(formattedTokens.bitcoin.eth.tokens);
746+
}).should.not.throw();
747+
748+
// Test testnet eth tokens
749+
(() => {
750+
verifyTokens(formattedTokens.testnet.eth.tokens);
751+
}).should.not.throw();
752+
753+
// Test mainnet xlm tokens
754+
(() => {
755+
verifyTokens(formattedTokens.bitcoin.xlm.tokens);
756+
}).should.not.throw();
757+
758+
// Test testnet xlm tokens
759+
(() => {
760+
verifyTokens(formattedTokens.testnet.xlm.tokens);
761+
}).should.not.throw();
762+
});
763+
});
555764
});

modules/utxo-bin/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"@bitgo/unspents": "^0.50.14",
3232
"@bitgo/utxo-core": "^1.30.0",
3333
"@bitgo/utxo-lib": "^11.19.1",
34-
"@bitgo/wasm-utxo": "^1.22.0",
34+
"@bitgo/wasm-utxo": "^1.24.0",
3535
"@noble/curves": "1.8.1",
3636
"archy": "^1.0.0",
3737
"bech32": "^2.0.0",

modules/utxo-core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
"@bitgo/secp256k1": "^1.9.0",
8282
"@bitgo/unspents": "^0.50.14",
8383
"@bitgo/utxo-lib": "^11.19.1",
84-
"@bitgo/wasm-utxo": "^1.22.0",
84+
"@bitgo/wasm-utxo": "^1.24.0",
8585
"bip174": "npm:@bitgo-forks/bip174@3.1.0-master.4",
8686
"fast-sha256": "^1.3.0"
8787
},

modules/utxo-ord/src/inscriptions.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ import { PreparedInscriptionRevealData } from '@bitgo/sdk-core';
2121

2222
const OPS = bscript.OPS;
2323
const MAX_LENGTH_TAP_DATA_PUSH = 520;
24+
// default "postage" amount
25+
// https://github.com/ordinals/ord/blob/0.24.2/src/lib.rs#L149
26+
const DEFAULT_POSTAGE_AMOUNT = BigInt(10_000);
2427

2528
/**
2629
* The max size of an individual OP_PUSH in a Taproot script is 520 bytes. This
@@ -100,7 +103,7 @@ function getInscriptionRevealSize(
100103
},
101104
],
102105
});
103-
psbt.addOutput({ script: commitOutput, value: BigInt(10_000) });
106+
psbt.addOutput({ script: commitOutput, value: DEFAULT_POSTAGE_AMOUNT });
104107

105108
psbt.signTaprootInput(
106109
0,

modules/utxo-ord/test/inscription.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ function createCommitTransactionPsbt(commitAddress: string, walletKeys: utxolib.
99

1010
commitTransactionPsbt.addOutput({
1111
script: commitTransactionOutputScript,
12-
value: BigInt(42),
12+
value: BigInt(10_000),
1313
});
1414

1515
const walletUnspent = testutil.mockWalletUnspent(networks.testnet, BigInt(20_000), { keys: walletKeys });
@@ -64,7 +64,7 @@ describe('inscriptions', () => {
6464
});
6565
});
6666

67-
xdescribe('Inscription Reveal Data', () => {
67+
describe('Inscription Reveal Data', () => {
6868
it('should sign reveal transaction and validate reveal size', () => {
6969
const walletKeys = testutil.getDefaultWalletKeys();
7070
const inscriptionData = Buffer.from('And Desert You', 'ascii');
@@ -76,19 +76,20 @@ describe('inscriptions', () => {
7676
);
7777

7878
const commitTransactionPsbt = createCommitTransactionPsbt(address, walletKeys);
79+
// Use the commit address (P2TR) as recipient to match the output script size
80+
// used in getInscriptionRevealSize estimation
7981
const fullySignedRevealTransaction = inscriptions.signRevealTransaction(
8082
walletKeys.user.privateKey as Buffer,
8183
tapLeafScript,
8284
address,
83-
'2N9R3mMCv6UfVbWEUW3eXJgxDeg4SCUVsu9',
85+
address,
8486
commitTransactionPsbt.getUnsignedTx().toBuffer(),
8587
networks.testnet
8688
);
8789

8890
fullySignedRevealTransaction.finalizeTapInputWithSingleLeafScriptAndSignature(0);
8991
const actualVirtualSize = fullySignedRevealTransaction.extractTransaction(true).virtualSize();
9092

91-
// TODO(BG-70861): figure out why size is slightly different and re-enable test
9293
assert.strictEqual(revealTransactionVSize, actualVirtualSize);
9394
});
9495
});

modules/utxo-staking/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"@bitgo/babylonlabs-io-btc-staking-ts": "^3.3.0",
6464
"@bitgo/utxo-core": "^1.30.0",
6565
"@bitgo/utxo-lib": "^11.19.1",
66-
"@bitgo/wasm-utxo": "^1.22.0",
66+
"@bitgo/wasm-utxo": "^1.24.0",
6767
"bip174": "npm:@bitgo-forks/bip174@3.1.0-master.4",
6868
"bip322-js": "^2.0.0",
6969
"bitcoinjs-lib": "^6.1.7",

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -996,10 +996,10 @@
996996
monocle-ts "^2.3.13"
997997
newtype-ts "^0.3.5"
998998

999-
"@bitgo/wasm-utxo@^1.22.0":
1000-
version "1.22.0"
1001-
resolved "https://registry.npmjs.org/@bitgo/wasm-utxo/-/wasm-utxo-1.22.0.tgz#106cb3ddcdaf39753a513aca5c8e0508faba5dc7"
1002-
integrity sha512-/2jPyJvb3OwoFJ4fYI8V28zQVwj5ma6y17mByDFtMz7td0SraycPqYP6Y0B+YcVlqTMlZ0SYoEGKXBqeBqPy6w==
999+
"@bitgo/wasm-utxo@^1.24.0":
1000+
version "1.24.0"
1001+
resolved "https://registry.npmjs.org/@bitgo/wasm-utxo/-/wasm-utxo-1.24.0.tgz#27c28b496daad594fa0b20d7ced654dbeeb3473f"
1002+
integrity sha512-7AEBQJ03V8JWiH1SEkrf6j4IAjo6Tl/G7QHtmBXwoMs5Bpy0haZMERl0eodmiCIczHYGTmpk6fgGNyvaVflg7A==
10031003

10041004
"@brandonblack/musig@^0.0.1-alpha.0":
10051005
version "0.0.1-alpha.1"

0 commit comments

Comments
 (0)