diff --git a/modules/account-lib/src/index.ts b/modules/account-lib/src/index.ts index ba9607bc9cd..8f79e94b02b 100644 --- a/modules/account-lib/src/index.ts +++ b/modules/account-lib/src/index.ts @@ -249,6 +249,7 @@ const coinBuilderMap = { tcspr: Cspr.TransactionBuilderFactory, stx: Stx.TransactionBuilderFactory, tstx: Stx.TransactionBuilderFactory, + tstxsignet: Stx.TransactionBuilderFactory, algo: Algo.TransactionBuilderFactory, talgo: Algo.TransactionBuilderFactory, sol: Sol.TransactionBuilderFactory, diff --git a/modules/bitgo/src/v2/coinFactory.ts b/modules/bitgo/src/v2/coinFactory.ts index 4a93008ff67..d2475788043 100644 --- a/modules/bitgo/src/v2/coinFactory.ts +++ b/modules/bitgo/src/v2/coinFactory.ts @@ -207,6 +207,7 @@ import { Tsol, Tsoneium, Tstx, + Tstxsignet, Tstt, Tsui, Tsusd, @@ -390,6 +391,7 @@ export function registerCoinConstructors(coinFactory: CoinFactory, coinMap: Coin coinFactory.register('tsoneium', Tsoneium.createInstance); coinFactory.register('tstarknet', Starknet.createInstance); coinFactory.register('tstx', Tstx.createInstance); + coinFactory.register('tstxsignet', Tstxsignet.createInstance); coinFactory.register('tstt', Tstt.createInstance); coinFactory.register('tsui', Tsui.createInstance); coinFactory.register('tsusd', Tsusd.createInstance); @@ -972,6 +974,8 @@ export function getCoinConstructor(coinName: string): CoinConstructor | undefine return Starknet.createInstance; case 'tstx': return Tstx.createInstance; + case 'tstxsignet': + return Tstxsignet.createInstance; case 'tstt': return Tstt.createInstance; case 'tsui': @@ -1184,6 +1188,7 @@ export function getTokenConstructor( } case 'stx': case 'tstx': + case 'tstxsignet': return Sip10Token.createTokenConstructor(tokenConfig as Sip10TokenConfig); case 'near': case 'tnear': diff --git a/modules/bitgo/src/v2/coins/index.ts b/modules/bitgo/src/v2/coins/index.ts index d96941637af..faa4deda276 100644 --- a/modules/bitgo/src/v2/coins/index.ts +++ b/modules/bitgo/src/v2/coins/index.ts @@ -61,7 +61,7 @@ import { Tstt } from '@bitgo/sdk-coin-stt'; import { Sgb, Tsgb } from '@bitgo/sdk-coin-sgb'; import { Sol, Tsol } from '@bitgo/sdk-coin-sol'; import { Starknet } from '@bitgo/sdk-coin-starknet'; -import { Stx, Tstx, Sip10Token } from '@bitgo/sdk-coin-stx'; +import { Stx, Tstx, Tstxsignet, Sip10Token } from '@bitgo/sdk-coin-stx'; import { Sui, Tsui, SuiToken } from '@bitgo/sdk-coin-sui'; import { Tao, Ttao, TaoToken } from '@bitgo/sdk-coin-tao'; import { Tempo, Ttempo, Tip20Token } from '@bitgo/sdk-coin-tempo'; @@ -138,7 +138,7 @@ export { Sol, Tsol }; export { Soneium, Tsoneium, SoneiumToken }; export { Starknet }; export { Tstt }; -export { Stx, Tstx, Sip10Token }; +export { Stx, Tstx, Tstxsignet, Sip10Token }; export { Sui, Tsui, SuiToken }; export { Tao, Ttao, TaoToken }; export { Tempo, Ttempo, Tip20Token }; diff --git a/modules/sdk-coin-stx/src/index.ts b/modules/sdk-coin-stx/src/index.ts index 13d94e072ae..b8bd93e844c 100644 --- a/modules/sdk-coin-stx/src/index.ts +++ b/modules/sdk-coin-stx/src/index.ts @@ -1,6 +1,7 @@ export * as StxLib from './lib'; export * from './stx'; export * from './tstx'; +export * from './tstxsignet'; export * from './types'; export * from './register'; export * from './sip10Token'; diff --git a/modules/sdk-coin-stx/src/lib/index.ts b/modules/sdk-coin-stx/src/lib/index.ts index 2ca71e4d87d..e40977f6929 100644 --- a/modules/sdk-coin-stx/src/lib/index.ts +++ b/modules/sdk-coin-stx/src/lib/index.ts @@ -1,5 +1,6 @@ export { AddressVersion, AddressHashMode } from '@stacks/transactions'; export * from './keyPair'; +export * from './network'; export * from './transaction'; export * from './transactionBuilderFactory'; export * from './sbtcWithdrawBuilder'; diff --git a/modules/sdk-coin-stx/src/lib/network.ts b/modules/sdk-coin-stx/src/lib/network.ts new file mode 100644 index 00000000000..9396789504a --- /dev/null +++ b/modules/sdk-coin-stx/src/lib/network.ts @@ -0,0 +1,28 @@ +import { BaseCoin as CoinConfig, NetworkType, StacksNetwork as StaticsStacksNetwork } from '@bitgo/statics'; +import { StacksMainnet, StacksNetwork, StacksTestnet } from '@stacks/network'; + +/** + * Build the `@stacks/network` object a transaction is serialized against. + * + * The stock Stacks mainnet/testnet networks hardcode their chain ID and transaction version. A + * statics network that describes a different Stacks deployment (for example the Stacks + * Foundation `staking-testnet`, chain ID 1280, on a private Bitcoin signet) declares + * `chainId` / `transactionVersion`; those are applied on top of the stock network so the + * serialized transaction carries the chain ID the node expects. + * + * When statics omits both fields the returned network is exactly the stock one, so existing + * `stx` / `tstx` transactions stay byte-identical. + */ +export function toStacksNetwork(coinConfig: Readonly): StacksNetwork { + const staticsNetwork = coinConfig.network as StaticsStacksNetwork; + const network = staticsNetwork.type === NetworkType.MAINNET ? new StacksMainnet() : new StacksTestnet(); + + if (staticsNetwork.chainId !== undefined) { + network.chainId = staticsNetwork.chainId; + } + if (staticsNetwork.transactionVersion !== undefined) { + network.version = staticsNetwork.transactionVersion; + } + + return network; +} diff --git a/modules/sdk-coin-stx/src/lib/transactionBuilder.ts b/modules/sdk-coin-stx/src/lib/transactionBuilder.ts index a27c198fa9d..dd5f73357f9 100644 --- a/modules/sdk-coin-stx/src/lib/transactionBuilder.ts +++ b/modules/sdk-coin-stx/src/lib/transactionBuilder.ts @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js'; import BigNum from 'bn.js'; -import { BaseCoin as CoinConfig, NetworkType } from '@bitgo/statics'; +import { BaseCoin as CoinConfig } from '@bitgo/statics'; import { AuthType, BufferReader, @@ -13,7 +13,7 @@ import { StacksMessageType, PubKeyEncoding, } from '@stacks/transactions'; -import { StacksNetwork, StacksTestnet, StacksMainnet } from '@stacks/network'; +import { StacksNetwork } from '@stacks/network'; import { BaseAddress, BaseFee, @@ -31,6 +31,7 @@ import { KeyPair } from './keyPair'; import { SignatureData } from './iface'; import { isValidAddress, removeHexPrefix, isValidMemo, isValidPublicKey } from './utils'; import { ANCHOR_MODE, DEFAULT_MULTISIG_SIG_NUMBER } from './constants'; +import { toStacksNetwork } from './network'; export abstract class TransactionBuilder extends BaseTransactionBuilder { private _transaction: Transaction; @@ -51,7 +52,7 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder { this._fromPubKeys = []; this._signatures = []; this._numberSignatures = DEFAULT_MULTISIG_SIG_NUMBER; - this._network = _coinConfig.network.type === NetworkType.MAINNET ? new StacksMainnet() : new StacksTestnet(); + this._network = toStacksNetwork(_coinConfig); this._transaction = new Transaction(_coinConfig); } diff --git a/modules/sdk-coin-stx/src/register.ts b/modules/sdk-coin-stx/src/register.ts index e3e685e2f13..bd176e90b19 100644 --- a/modules/sdk-coin-stx/src/register.ts +++ b/modules/sdk-coin-stx/src/register.ts @@ -1,11 +1,13 @@ import { BitGoBase } from '@bitgo/sdk-core'; import { Stx } from './stx'; import { Tstx } from './tstx'; +import { Tstxsignet } from './tstxsignet'; import { Sip10Token } from './sip10Token'; export const register = (sdk: BitGoBase): void => { sdk.register('stx', Stx.createInstance); sdk.register('tstx', Tstx.createInstance); + sdk.register('tstxsignet', Tstxsignet.createInstance); Sip10Token.createTokenConstructors().forEach(({ name, coinConstructor }) => { sdk.register(name, coinConstructor); }); diff --git a/modules/sdk-coin-stx/src/sip10Token.ts b/modules/sdk-coin-stx/src/sip10Token.ts index a4d19967d7f..a89387c4660 100644 --- a/modules/sdk-coin-stx/src/sip10Token.ts +++ b/modules/sdk-coin-stx/src/sip10Token.ts @@ -2,7 +2,7 @@ import _ from 'lodash'; import BigNumber from 'bignumber.js'; import { BitGoBase, CoinConstructor, NamedCoinConstructor, VerifyTransactionOptions } from '@bitgo/sdk-core'; -import { BaseCoin as StaticsBaseCoin, coins, NetworkType, Sip10TokenConfig, tokens } from '@bitgo/statics'; +import { BaseCoin as StaticsBaseCoin, coins, Sip10TokenConfig, tokens } from '@bitgo/statics'; import { Stx } from './stx'; import { TransactionBuilderFactory } from './lib'; @@ -13,7 +13,9 @@ export class Sip10Token extends Stx { public readonly tokenConfig: Sip10TokenConfig; constructor(bitgo: BitGoBase, tokenConfig: Sip10TokenConfig) { - const staticsCoin = tokenConfig.network === NetworkType.MAINNET ? coins.get('stx') : coins.get('tstx'); + // tokenConfig.coin is derived by statics from the token's network, so it stays correct for + // deployments beyond the stock mainnet/testnet pair (e.g. tstxsignet, chain ID 1280). + const staticsCoin = coins.get(tokenConfig.coin); super(bitgo, staticsCoin); this.tokenConfig = tokenConfig; } diff --git a/modules/sdk-coin-stx/src/tstxsignet.ts b/modules/sdk-coin-stx/src/tstxsignet.ts new file mode 100644 index 00000000000..193b431655a --- /dev/null +++ b/modules/sdk-coin-stx/src/tstxsignet.ts @@ -0,0 +1,40 @@ +/** + * Testnet Stacks Signet + * + * @format + */ +import { BaseCoin, BitGoBase, Environments } from '@bitgo/sdk-core'; +import { BaseCoin as StaticsBaseCoin } from '@bitgo/statics'; + +import { Stx } from './stx'; + +export class Tstxsignet extends Stx { + protected readonly _staticsCoin: Readonly; + + constructor(bitgo: BitGoBase, staticsCoin?: Readonly) { + super(bitgo, staticsCoin); + + if (!staticsCoin) { + throw new Error('missing required constructor parameter staticsCoin'); + } + + this._staticsCoin = staticsCoin; + } + + static createInstance(bitgo: BitGoBase, staticsCoin?: Readonly): BaseCoin { + return new Tstxsignet(bitgo, staticsCoin); + } + + /** + * The Stacks staking-testnet RPC. It is a distinct chain (chain ID 1280) that only exists on + * testnet-derived environments; environments without it configured (e.g. prod) cannot talk to + * this network. + */ + override getPublicNodeUrl(): string { + const nodeUrl = Environments[this.bitgo.getEnv()].stxSignetNodeUrl; + if (!nodeUrl) { + throw new Error(`no stx signet node url configured for env ${this.bitgo.getEnv()}`); + } + return nodeUrl; + } +} diff --git a/modules/sdk-coin-stx/test/unit/network.ts b/modules/sdk-coin-stx/test/unit/network.ts new file mode 100644 index 00000000000..d0e98c6198c --- /dev/null +++ b/modules/sdk-coin-stx/test/unit/network.ts @@ -0,0 +1,55 @@ +import assert from 'assert'; + +import { BaseCoin as CoinConfig, coins } from '@bitgo/statics'; +import { pubKeyfromPrivKey, publicKeyToString } from '@stacks/transactions'; + +import { StxLib } from '../../src'; + +const { toStacksNetwork, TransactionBuilderFactory } = StxLib; + +describe('Stacks network plumbing', function () { + it('keeps the stock mainnet chain identity for stx', function () { + const network = toStacksNetwork(coins.get('stx')); + assert.strictEqual(network.chainId, 1); + assert.strictEqual(network.version, 0); + }); + + it('keeps the stock testnet chain identity for tstx', function () { + const network = toStacksNetwork(coins.get('tstx')); + assert.strictEqual(network.chainId, 2147483648); + assert.strictEqual(network.version, 128); + }); + + it('applies the tstxsignet chain identity declared in statics', function () { + const network = toStacksNetwork(coins.get('tstxsignet')); + assert.strictEqual(network.chainId, 1280); + assert.strictEqual(network.version, 0x80); + }); + + // staking-testnet rejects transactions carrying the public testnet chain ID with + // "SignatureValidation: invalid chain ID 2147483648 (expected 1280)", so the chain ID and + // version have to reach the serialized bytes: version byte 0x80 followed by chain ID 1280. + it('serializes the tstxsignet chain ID and version into the transaction', async function () { + const txHex = await buildTransfer(coins.get('tstxsignet')); + assert.strictEqual(txHex.slice(0, 10), '8000000500'); + }); + + it('leaves tstx transactions on the stock testnet chain ID', async function () { + const txHex = await buildTransfer(coins.get('tstx')); + assert.strictEqual(txHex.slice(0, 10), '8080000000'); + }); +}); + +async function buildTransfer(config: Readonly): Promise { + const publicKey = publicKeyToString( + pubKeyfromPrivKey('0f4fad1041051740108cdf523c346199e99d80cc845a4ea8d4d29b1a81fd22fc01') + ); + const builder = new TransactionBuilderFactory(config).getTransferBuilder(); + builder.to('STHT87WKW17VGMPFFNB6QD1ABNKGB1600ZE7YVW4'); + builder.amount('1000'); + builder.fee({ fee: '200' }); + builder.nonce(0); + builder.fromPubKey([publicKey]); + const transaction = await builder.build(); + return transaction.toBroadcastFormat(); +} diff --git a/modules/sdk-coin-stx/test/unit/tstxsignet.ts b/modules/sdk-coin-stx/test/unit/tstxsignet.ts new file mode 100644 index 00000000000..d4d63b070a0 --- /dev/null +++ b/modules/sdk-coin-stx/test/unit/tstxsignet.ts @@ -0,0 +1,28 @@ +import assert from 'assert'; + +import { BitGoAPI } from '@bitgo/sdk-api'; +import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; + +import { Tstxsignet } from '../../src'; + +describe('tstxsignet', function () { + let bitgo: TestBitGoAPI; + + before(function () { + bitgo = TestBitGo.decorate(BitGoAPI, { env: 'mock' }); + bitgo.initializeTestVars(); + bitgo.safeRegister('tstxsignet', Tstxsignet.createInstance); + }); + + it('uses the statics tstxsignet coin', function () { + const basecoin = bitgo.coin('tstxsignet'); + assert.strictEqual(basecoin.getChain(), 'tstxsignet'); + assert.strictEqual(basecoin.getFullName(), 'Testnet Stacks (Signet)'); + assert.strictEqual(basecoin.getFamily(), 'stx'); + }); + + it('resolves the staking-testnet public node url', function () { + const basecoin = bitgo.coin('tstxsignet') as Tstxsignet; + assert.strictEqual(basecoin.getPublicNodeUrl(), 'https://api.staking-testnet.hiro.so'); + }); +}); diff --git a/modules/sdk-core/src/bitgo/environments.ts b/modules/sdk-core/src/bitgo/environments.ts index cd74104bfd9..757c2f5414e 100644 --- a/modules/sdk-core/src/bitgo/environments.ts +++ b/modules/sdk-core/src/bitgo/environments.ts @@ -91,6 +91,11 @@ interface EnvironmentTemplate { soneiumExplorerBaseUrl?: string; soneiumExplorerApiToken?: string; stxNodeUrl: string; + /** + * Stacks staking-testnet RPC (chain ID 1280 on a private Bitcoin signet). Only testnet-derived + * environments configure it; the tstxsignet coin is unusable in environments without it. + */ + stxSignetNodeUrl?: string; vetNodeUrl: string; // The key here is coinName and it will be same for both mainnet and testnet (eg: 'phrs') evm?: { @@ -619,6 +624,7 @@ const testnetBase: EnvironmentTemplate = { }, }, stxNodeUrl: 'https://api.testnet.hiro.so', + stxSignetNodeUrl: 'https://api.staking-testnet.hiro.so', vetNodeUrl: 'https://sync-testnet.vechain.org', xtzExplorerBaseUrl: 'https://api.ghostnet.tzkt.io', xtzRpcUrl: 'https://rpc.tzkt.io/ghostnet', diff --git a/modules/statics/src/allCoinsAndTokens.ts b/modules/statics/src/allCoinsAndTokens.ts index 0a8f93ff04f..3f86ac0e668 100644 --- a/modules/statics/src/allCoinsAndTokens.ts +++ b/modules/statics/src/allCoinsAndTokens.ts @@ -843,6 +843,16 @@ export const allCoinsAndTokens = [ BaseUnit.STX, STX_FEATURES ), + account( + 'ec52d215-34c0-4aca-b49b-2eeaab8f9615', + 'tstxsignet', + 'Testnet Stacks (Signet)', + Networks.test.stxSignet, + 6, + UnderlyingAsset.STX, + BaseUnit.STX, + STX_FEATURES + ), account( '92185a03-356f-4b75-9213-af1c92fe5393', 'sol', diff --git a/modules/statics/src/networks.ts b/modules/statics/src/networks.ts index 3bfb6c70fb6..7a8fd865f16 100644 --- a/modules/statics/src/networks.ts +++ b/modules/statics/src/networks.ts @@ -202,6 +202,13 @@ export interface StacksNetwork extends AccountNetwork { readonly sendmanymemoContractAddress: string; readonly stakingContractAddress: string; readonly sbtcWithdrawalContractAddress: string; + /** + * Chain ID carried in the serialized transaction (bytes 1-4). Omitted for the stock Stacks + * mainnet/testnet deployments, whose chain ID is implied by the network type. + */ + readonly chainId?: number; + /** Transaction version byte. Omitted for the stock Stacks mainnet/testnet deployments. */ + readonly transactionVersion?: number; } // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -1366,6 +1373,29 @@ class StxTestnet extends Testnet implements StacksNetwork { sbtcWithdrawalContractAddress = 'SN69P7RZRKK8ERQCCABHT2JWKB2S4DHH9H74231T'; } +/** + * Stacks Foundation `staking-testnet`: a PoX-5 testnet running on a private Bitcoin signet. + * + * It is a distinct Stacks network (chain ID 1280) that shares the public testnet transaction + * version and address versions, so a transaction built for the public testnet is rejected by its + * node with `SignatureValidation: invalid chain ID 2147483648 (expected 1280)`. The chain ID and + * transaction version therefore travel in statics instead of being implied by the network type. + */ +class StxSignet extends Testnet implements StacksNetwork { + name = 'StxSignet'; + family = CoinFamily.STX; + // No block explorer is deployed for this network. + explorerUrl = undefined; + // Boot address; the PoX-5 contract is `.pox-5` (verified via /v2/pox). + stakingContractAddress = 'ST000000000000000000002AMW42H'; + // sBTC deployer; the sBTC token contract is `.sbtc-token`. + sbtcWithdrawalContractAddress = 'SN3R84XZYA63QS28932XQF3G1J8R9PC3W76P9CSQS'; + // `send-many-memo` is not deployed on this network. + sendmanymemoContractAddress = ''; + chainId = 1280; + transactionVersion = 0x80; +} + class SUSD extends Mainnet implements AccountNetwork { name = 'SUSD'; family = CoinFamily.SUSD; @@ -3186,6 +3216,7 @@ export const Networks = { sui: Object.freeze(new SuiTestnet()), near: Object.freeze(new NearTestnet()), stx: Object.freeze(new StxTestnet()), + stxSignet: Object.freeze(new StxSignet()), stt: Object.freeze(new SomniaTestnet()), scrolleth: Object.freeze(new ScrollEthTestnet()), soneium: Object.freeze(new SoneiumTestnet()), diff --git a/modules/statics/src/ofc.ts b/modules/statics/src/ofc.ts index 159dec31aee..ff6823bff33 100644 --- a/modules/statics/src/ofc.ts +++ b/modules/statics/src/ofc.ts @@ -1383,7 +1383,7 @@ export function ofcStxToken( suffix: string = name.replace(/^ofc/, '').toUpperCase(), network: OfcNetwork = Networks.main.ofc, isToken = true, - addressCoin = 'stx', + addressCoin = name.replace(/^ofc/, '').split(':')[0], primaryKeyCurve: KeyCurve = KeyCurve.Secp256k1 ) { const filteredFeatures = getFilteredFeatures(suffix); @@ -1438,7 +1438,7 @@ export function tofcStxToken( suffix: string = name.replace(/^ofc/, '').toUpperCase(), network: OfcNetwork = Networks.test.ofc, isToken = true, - addressCoin = 'tstx', + addressCoin = name.replace(/^ofc/, '').split(':')[0], primaryKeyCurve: KeyCurve = KeyCurve.Secp256k1 ) { const filteredFeatures = getFilteredFeatures(suffix); diff --git a/modules/statics/src/tokenConfig.ts b/modules/statics/src/tokenConfig.ts index bb9f02212ed..33f7ff2fa05 100644 --- a/modules/statics/src/tokenConfig.ts +++ b/modules/statics/src/tokenConfig.ts @@ -1096,9 +1096,23 @@ const getFormattedVetNFTCollections = (customCoinMap = coins) => }, []); function getSip10TokenConfig(coin: Sip10Token): Sip10TokenConfig { + let baseCoin: string; + switch (coin.network.name) { + case Networks.main.stx.name: + baseCoin = 'stx'; + break; + case Networks.test.stx.name: + baseCoin = 'tstx'; + break; + case Networks.test.stxSignet.name: + baseCoin = 'tstxsignet'; + break; + default: + throw new Error(`SIP-10 token ${coin.name} has an unsupported network`); + } return { type: coin.name, - coin: coin.network.type === NetworkType.MAINNET ? 'stx' : 'tstx', + coin: baseCoin, network: coin.network.type === NetworkType.MAINNET ? 'Mainnet' : 'Testnet', name: coin.fullName, assetId: coin.assetId, diff --git a/modules/statics/test/unit/fixtures/expectedColdFeatures.ts b/modules/statics/test/unit/fixtures/expectedColdFeatures.ts index e8e2627a0f3..ca0ecf6f068 100644 --- a/modules/statics/test/unit/fixtures/expectedColdFeatures.ts +++ b/modules/statics/test/unit/fixtures/expectedColdFeatures.ts @@ -63,6 +63,7 @@ export const expectedColdFeatures = { 'tpearl', 'trbtc', 'tstx', + 'tstxsignet', 'txlm', 'txrp', 'txtz', diff --git a/modules/statics/test/unit/networks.ts b/modules/statics/test/unit/networks.ts index 0d20e224bae..8c713cb5700 100644 --- a/modules/statics/test/unit/networks.ts +++ b/modules/statics/test/unit/networks.ts @@ -7,6 +7,7 @@ import { Networks, NetworkType, PolyxNetwork, + StacksNetwork, } from '../../src/networks'; Object.entries(Networks).forEach(([category, networks]) => { @@ -196,3 +197,26 @@ describe('DynamicNetwork and getNetwork', function () { (() => getNetwork('NonExistentNetworkXYZ')).should.throw('Network NonExistentNetworkXYZ not found'); }); }); + +describe('StxSignet network (Stacks staking-testnet)', function () { + it('declares chain ID 1280 and testnet transaction version', function () { + const network = Networks.test.stxSignet as StacksNetwork; + network.should.have.property('chainId', 1280); + network.should.have.property('transactionVersion', 0x80); + }); + + it('has the pox-5 boot and sBTC deployer contract addresses', function () { + const network = Networks.test.stxSignet as StacksNetwork; + network.stakingContractAddress.should.equal('ST000000000000000000002AMW42H'); + network.sbtcWithdrawalContractAddress.should.equal('SN3R84XZYA63QS28932XQF3G1J8R9PC3W76P9CSQS'); + }); + + it('keeps the stock stx networks free of a declared chain identity', function () { + // stx/tstx transactions must stay byte-identical, so their networks must not declare a + // chainId/transactionVersion that would override the stock @stacks/network values. + (Networks.main.stx as StacksNetwork).should.not.have.property('chainId'); + (Networks.main.stx as StacksNetwork).should.not.have.property('transactionVersion'); + (Networks.test.stx as StacksNetwork).should.not.have.property('chainId'); + (Networks.test.stx as StacksNetwork).should.not.have.property('transactionVersion'); + }); +}); diff --git a/modules/web-demo/src/components/Coins/coinFactory.ts b/modules/web-demo/src/components/Coins/coinFactory.ts index d34956b9e33..7ad3dec84a7 100644 --- a/modules/web-demo/src/components/Coins/coinFactory.ts +++ b/modules/web-demo/src/components/Coins/coinFactory.ts @@ -151,7 +151,8 @@ class CoinFactory { return this.cacheAndRegister(name, sdk, register); } case 'stx': - case 'tstx': { + case 'tstx': + case 'tstxsignet': { const { register } = await import('@bitgo/sdk-coin-stx'); return this.cacheAndRegister(name, sdk, register); }