diff --git a/contract_manager/src/core/chains.ts b/contract_manager/src/core/chains.ts index 1d4a26c577..5dafa7c2d7 100644 --- a/contract_manager/src/core/chains.ts +++ b/contract_manager/src/core/chains.ts @@ -263,6 +263,9 @@ export class CosmWasmChain extends Chain { gasPrice: this.gasPrice, id: this.id, mainnet: this.mainnet, + ...(this.nativeToken !== undefined && { + nativeToken: this.nativeToken, + }), prefix: this.prefix, type: CosmWasmChain.type, wormholeChainName: this.wormholeChainName, @@ -441,6 +444,9 @@ export class SuiChain extends Chain { endpointType: this.endpointType, id: this.id, mainnet: this.mainnet, + ...(this.nativeToken !== undefined && { + nativeToken: this.nativeToken, + }), rpcUrl: this.rpcUrl, type: SuiChain.type, wormholeChainName: this.wormholeChainName, @@ -936,6 +942,9 @@ export class IotaChain extends Chain { endpointType: this.endpointType, id: this.id, mainnet: this.mainnet, + ...(this.nativeToken !== undefined && { + nativeToken: this.nativeToken, + }), rpcUrl: this.rpcUrl, type: IotaChain.type, wormholeChainName: this.wormholeChainName, @@ -1013,6 +1022,9 @@ export class SvmChain extends Chain { return { id: this.id, mainnet: this.mainnet, + ...(this.nativeToken !== undefined && { + nativeToken: this.nativeToken, + }), rpcUrl: this.rpcUrl, type: SvmChain.type, wormholeChainName: this.wormholeChainName, @@ -1131,6 +1143,9 @@ export class EvmChain extends Chain { return { id: this.id, mainnet: this.mainnet, + ...(this.nativeToken !== undefined && { + nativeToken: this.nativeToken, + }), networkId: this.networkId, rpcUrl: this.rpcUrl, type: EvmChain.type, @@ -1311,6 +1326,9 @@ export class AptosChain extends Chain { return { id: this.id, mainnet: this.mainnet, + ...(this.nativeToken !== undefined && { + nativeToken: this.nativeToken, + }), rpcUrl: this.rpcUrl, type: AptosChain.type, wormholeChainName: this.wormholeChainName, @@ -1402,6 +1420,9 @@ export class FuelChain extends Chain { gqlUrl: this.gqlUrl, id: this.id, mainnet: this.mainnet, + ...(this.nativeToken !== undefined && { + nativeToken: this.nativeToken, + }), type: FuelChain.type, wormholeChainName: this.wormholeChainName, }; @@ -1519,6 +1540,7 @@ export class TonChain extends Chain { wormholeChainName: string, nativeToken: TokenId | undefined, private rpcUrl: string, + private networkId: string, ) { super(id, mainnet, wormholeChainName, nativeToken); } @@ -1583,6 +1605,10 @@ export class TonChain extends Chain { return { id: this.id, mainnet: this.mainnet, + ...(this.nativeToken !== undefined && { + nativeToken: this.nativeToken, + }), + networkId: this.networkId, rpcUrl: this.rpcUrl, type: TonChain.type, wormholeChainName: this.wormholeChainName, @@ -1597,6 +1623,7 @@ export class TonChain extends Chain { parsed.wormholeChainName ?? "", parsed.nativeToken, parsed.rpcUrl ?? "", + parsed.networkId ?? "", ); } @@ -1647,6 +1674,9 @@ export class NearChain extends Chain { return { id: this.id, mainnet: this.mainnet, + ...(this.nativeToken !== undefined && { + nativeToken: this.nativeToken, + }), networkId: this.networkId, rpcUrl: this.rpcUrl, type: NearChain.type, diff --git a/contract_manager/tests/chains.test.ts b/contract_manager/tests/chains.test.ts new file mode 100644 index 0000000000..cf0480927c --- /dev/null +++ b/contract_manager/tests/chains.test.ts @@ -0,0 +1,153 @@ +import { describe, expect, it } from "vitest"; +import { + AptosChain, + CosmWasmChain, + EvmChain, + FuelChain, + IotaChain, + NearChain, + SuiChain, + SvmChain, + TonChain, +} from "../src/core/chains"; + +type ChainFactory = { + fromJson: (parsed: Record) => { + getNativeToken: () => string | undefined; + toJson: () => Record; + }; +}; + +describe("Chain Serialization Contract Symmetry", () => { + const chainTestCases = [ + { + cls: CosmWasmChain, + name: "CosmWasmChain", + sample: { + endpoint: "http://localhost", + feeDenom: "uosmo", + gasPrice: "0.025", + id: "osmosis", + mainnet: true, + nativeToken: "OSMO", + prefix: "osmo", + type: CosmWasmChain.type, + wormholeChainName: "osmosis", + }, + }, + { + cls: SuiChain, + name: "SuiChain", + sample: { + endpointType: "json-rpc", + id: "sui", + mainnet: true, + nativeToken: "SUI", + rpcUrl: "http://localhost", + type: SuiChain.type, + wormholeChainName: "sui", + }, + }, + { + cls: IotaChain, + name: "IotaChain", + sample: { + endpointType: "json-rpc", + id: "iota", + mainnet: true, + nativeToken: "IOTA", + rpcUrl: "http://localhost", + type: IotaChain.type, + wormholeChainName: "iota", + }, + }, + { + cls: SvmChain, + name: "SvmChain", + sample: { + id: "solana", + mainnet: true, + nativeToken: "SOL", + rpcUrl: "http://localhost", + type: SvmChain.type, + wormholeChainName: "solana", + }, + }, + { + cls: EvmChain, + name: "EvmChain", + sample: { + id: "ethereum", + mainnet: true, + nativeToken: "ETH", + networkId: 1, + rpcUrl: "http://localhost", + type: EvmChain.type, + }, + }, + { + cls: AptosChain, + name: "AptosChain", + sample: { + id: "aptos", + mainnet: true, + nativeToken: "APT", + rpcUrl: "http://localhost", + type: AptosChain.type, + wormholeChainName: "aptos", + }, + }, + { + cls: FuelChain, + name: "FuelChain", + sample: { + gqlUrl: "http://localhost", + id: "fuel", + mainnet: true, + nativeToken: "ETH", + type: FuelChain.type, + wormholeChainName: "fuel_mainnet", + }, + }, + { + cls: TonChain, + name: "TonChain", + sample: { + id: "ton", + mainnet: true, + nativeToken: "TON", + networkId: "0x1", + rpcUrl: "http://localhost", + type: TonChain.type, + wormholeChainName: "ton_mainnet", + }, + }, + { + cls: NearChain, + name: "NearChain", + sample: { + id: "near", + mainnet: true, + nativeToken: "NEAR", + networkId: "mainnet", + rpcUrl: "http://localhost", + type: NearChain.type, + wormholeChainName: "near", + }, + }, + ]; + + for (const { name, cls, sample } of chainTestCases) { + it(`should preserve nativeToken in ${name} through fromJson -> toJson -> fromJson`, () => { + const factory = cls as unknown as ChainFactory; + const chain = factory.fromJson(sample); + expect(chain.getNativeToken()).toEqual(sample.nativeToken); + + const json = chain.toJson(); + expect(json.nativeToken).toEqual(sample.nativeToken); + + const roundTripChain = factory.fromJson(json); + expect(roundTripChain.getNativeToken()).toEqual(sample.nativeToken); + }); + } +});