Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions modules/statics/src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3931,3 +3931,49 @@ export abstract class BaseCoin {
return baseFeatures.filter((feature) => !excludedFeatures.includes(feature));
}
}

export interface DynamicCoinConstructorOptions {
id: string;
fullName: string;
name: string;
alias?: string;
prefix?: string;
suffix?: string;
denom?: string;
baseUnit: string;
kind: string;
isToken: boolean;
features: string[];
decimalPlaces: number;
asset: string;
network: BaseNetwork;
primaryKeyCurve: string;
}

/**
* Concrete coin class for AMS-discovered chains not yet registered in local statics.
*
* Extends {@link BaseCoin} directly with empty required/disallowed
* feature sets — AMS is the source of truth for features. Accepts string-typed enum
* fields and casts internally (safe since CoinKind, CoinFeature, UnderlyingAsset,
* KeyCurve are all string enums).
*/
export class DynamicCoin extends BaseCoin {
protected requiredFeatures(): Set<CoinFeature> {
return new Set();
}

protected disallowedFeatures(): Set<CoinFeature> {
return new Set();
}

constructor(options: DynamicCoinConstructorOptions) {
super({
...options,
kind: options.kind as CoinKind,
features: options.features as CoinFeature[],
asset: options.asset as UnderlyingAsset,
primaryKeyCurve: options.primaryKeyCurve as KeyCurve,
});
}
}
34 changes: 32 additions & 2 deletions modules/statics/src/coins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ import {
erc721Token,
} from './account';
import { ofcToken } from './ofc';
import { BaseCoin, CoinFeature } from './base';
import { BaseCoin, CoinFeature, DynamicCoin } from './base';
import { AmsTokenConfig, TrimmedAmsTokenConfig } from './tokenConfig';
import { CoinMap } from './map';
import { Networks, NetworkType } from './networks';
import { DynamicNetwork, DynamicNetworkOptions, Networks, NetworkType } from './networks';
import { networkFeatureMapForTokens } from './networkFeatureMapForTokens';
import { ofcErc20Coins, tOfcErc20Coins } from './coins/ofcErc20Coins';
import { ofcCoins } from './coins/ofcCoins';
Expand Down Expand Up @@ -133,6 +133,9 @@ export function createToken(token: AmsTokenConfig): Readonly<BaseCoin> | undefin
const family = token.family;
const initializer = initializerMap[family] as (...args: unknown[]) => Readonly<BaseCoin>;
if (!initializer) {
if (!token.isToken) {
return buildDynamicCoin(token);
}
return undefined;
}

Expand Down Expand Up @@ -352,6 +355,33 @@ export function createToken(token: AmsTokenConfig): Readonly<BaseCoin> | undefin
}
}

/**
* Build a real DynamicCoin + DynamicNetwork instance for AMS-discovered base chains
* whose family is not yet registered in the SDK's initializerMap.
* Called from createToken() as a fallback when no initializer exists and isToken is false.
*/
function buildDynamicCoin(token: AmsTokenConfig): Readonly<BaseCoin> {
const network = Object.freeze(new DynamicNetwork(token.network as DynamicNetworkOptions));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to fetch this network from a network factory which returns networks ingested by ams as well.

return Object.freeze(
new DynamicCoin({
id: token.id,
name: token.name,
fullName: token.fullName,
decimalPlaces: token.decimalPlaces,
asset: token.asset as string,
isToken: token.isToken,
features: token.features as string[],
network,
primaryKeyCurve: (token.primaryKeyCurve as string) ?? 'secp256k1',
prefix: token.prefix ?? '',
suffix: token.suffix ?? token.name.toUpperCase(),
baseUnit: (token.baseUnit as string) ?? 'wei',
kind: (token.kind as string) ?? 'crypto',
alias: token.alias,
})
);
}

function getAptTokenInitializer(token: AmsTokenConfig) {
if (token.assetId) {
// used for fungible-assets / legacy coins etc.
Expand Down
89 changes: 89 additions & 0 deletions modules/statics/src/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2539,6 +2539,95 @@ class TempoTestnet extends Testnet implements EthereumNetwork {
tokenOperationHashPrefix = '42431';
}

/**
* Constructor options for {@link DynamicNetwork}.
* Accepts string-typed `type` and `family` so AMS JSON can be passed directly.
* Fields mirror BaseNetwork + AccountNetwork + EthereumNetwork.
*/
export interface DynamicNetworkOptions {
// BaseNetwork
name: string;
type: string;
family: string;
explorerUrl?: string;
// AccountNetwork
accountExplorerUrl?: string;
blockExplorerUrl?: string;
// EthereumNetwork
chainId?: number;
batcherContractAddress?: string;
forwarderFactoryAddress?: string;
forwarderImplementationAddress?: string;
walletFactoryAddress?: string;
walletImplementationAddress?: string;
walletV2FactoryAddress?: string;
walletV2ImplementationAddress?: string;
walletV4FactoryAddress?: string;
walletV4ImplementationAddress?: string;
walletV2ForwarderFactoryAddress?: string;
walletV2ForwarderImplementationAddress?: string;
walletV4ForwarderFactoryAddress?: string;
walletV4ForwarderImplementationAddress?: string;
nativeCoinOperationHashPrefix?: string;
tokenOperationHashPrefix?: string;
}

/**
* Concrete network class for AMS-discovered chains not yet registered in local statics.
* Accepts string-typed type/family and casts to enums internally (safe since both are string enums).
* Currently covers BaseNetwork + AccountNetwork + EthereumNetwork fields.
*/
export class DynamicNetwork extends BaseNetwork {
public readonly name: string;
public readonly type: NetworkType;
public readonly family: CoinFamily;
public readonly explorerUrl: string | undefined;
public readonly accountExplorerUrl?: string;
public readonly blockExplorerUrl?: string;
public readonly chainId?: number;
public readonly batcherContractAddress?: string;
public readonly forwarderFactoryAddress?: string;
public readonly forwarderImplementationAddress?: string;
public readonly walletFactoryAddress?: string;
public readonly walletImplementationAddress?: string;
public readonly walletV2FactoryAddress?: string;
public readonly walletV2ImplementationAddress?: string;
public readonly walletV4FactoryAddress?: string;
public readonly walletV4ImplementationAddress?: string;
public readonly walletV2ForwarderFactoryAddress?: string;
public readonly walletV2ForwarderImplementationAddress?: string;
public readonly walletV4ForwarderFactoryAddress?: string;
public readonly walletV4ForwarderImplementationAddress?: string;
public readonly nativeCoinOperationHashPrefix?: string;
public readonly tokenOperationHashPrefix?: string;

constructor(options: DynamicNetworkOptions) {
super();
this.name = options.name;
this.type = options.type as NetworkType;
this.family = options.family as CoinFamily;
this.explorerUrl = options.explorerUrl;
this.accountExplorerUrl = options.accountExplorerUrl;
this.blockExplorerUrl = options.blockExplorerUrl;
this.chainId = options.chainId;
this.batcherContractAddress = options.batcherContractAddress;
this.forwarderFactoryAddress = options.forwarderFactoryAddress;
this.forwarderImplementationAddress = options.forwarderImplementationAddress;
this.walletFactoryAddress = options.walletFactoryAddress;
this.walletImplementationAddress = options.walletImplementationAddress;
this.walletV2FactoryAddress = options.walletV2FactoryAddress;
this.walletV2ImplementationAddress = options.walletV2ImplementationAddress;
this.walletV4FactoryAddress = options.walletV4FactoryAddress;
this.walletV4ImplementationAddress = options.walletV4ImplementationAddress;
this.walletV2ForwarderFactoryAddress = options.walletV2ForwarderFactoryAddress;
this.walletV2ForwarderImplementationAddress = options.walletV2ForwarderImplementationAddress;
this.walletV4ForwarderFactoryAddress = options.walletV4ForwarderFactoryAddress;
this.walletV4ForwarderImplementationAddress = options.walletV4ForwarderImplementationAddress;
this.nativeCoinOperationHashPrefix = options.nativeCoinOperationHashPrefix;
this.tokenOperationHashPrefix = options.tokenOperationHashPrefix;
}
}

export const Networks = {
main: {
ada: Object.freeze(new Ada()),
Expand Down
Loading