Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: @demex-sdk/polynetwork #12

Open
wants to merge 23 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c33f0f3
initialise wallet package with basic connection helpers
stevenkhong Nov 19, 2024
9698321
add wip signing handlers
stevenkhong Nov 21, 2024
e58dba0
Set up file structure of polynetwork folder
sarah-thong Nov 22, 2024
930413c
Migrate required utils
sarah-thong Nov 23, 2024
2172a31
Add ETHClient (wip)
sarah-thong Nov 23, 2024
28a4255
Add all other polynetwork helper clients
sarah-thong Nov 24, 2024
e7db96c
Resolve missing dependencies in bridge helper classes
sarah-thong Nov 24, 2024
2880592
Add more helper classes for Neo Legacy and Neo3 clients
sarah-thong Nov 24, 2024
fb7aef3
Resolve missing dependencies
sarah-thong Nov 24, 2024
280558d
Rename clients folder to helpers
sarah-thong Nov 24, 2024
e14865b
Create PolynetworkClient class
sarah-thong Nov 24, 2024
0789471
Merge branch 'staging' into feat/bridge-helpers
sarah-thong Dec 5, 2024
1879306
Resolve code conflict
sarah-thong Dec 5, 2024
c3ddd55
Remove unnecessary ledger dependencies
sarah-thong Dec 5, 2024
b90b392
Remove unnecessary ledger packages
sarah-thong Dec 5, 2024
19642a6
Fix build issues
sarah-thong Dec 11, 2024
e045375
Remove unused helper functions
sarah-thong Dec 20, 2024
7972465
Delete unnecessary json files and data fields
sarah-thong Dec 20, 2024
6585c0c
Create initialize function to initialize helper data
sarah-thong Dec 20, 2024
40db44b
Polish polynetwork client initialisation
sarah-thong Dec 20, 2024
c2ddd35
Merge branch 'staging' into feat/bridge-helpers
sarah-thong Dec 23, 2024
b793533
Fix build errors caused by staging conflicts
sarah-thong Dec 23, 2024
c12b336
Merge branch 'staging' into feat/bridge-helpers
stevenkhong Jan 2, 2025
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
2 changes: 1 addition & 1 deletion packages/bridge/tsconfig.cjs.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"baseUrl": "."
},
"extends": "../../tsconfig.cjs.json",
"include": ["src/"]
"include": ["src/", "src/**/*.json"]
}
2 changes: 1 addition & 1 deletion packages/bridge/tsconfig.es.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"baseUrl": "."
},
"extends": "../../tsconfig.es.json",
"include": ["src/"]
"include": ["src/", "src/**/*.json"]
}
2 changes: 1 addition & 1 deletion packages/bridge/tsconfig.types.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"rootDir": "src",
},
"extends": "../../tsconfig.types.json",
"include": ["src/"]
"include": ["src/", "src/**/*.json"]
}
9 changes: 9 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@
"module": "./dist-es/index.js",
"types": "./dist-types/index.d.ts",
"dependencies": {
"@cityofzion/neon-core-next": "npm:@cityofzion/neon-core@^5.0.0",
"@cosmjs/stargate": "^0.32.4",
"@cosmjs/tendermint-rpc": "^0.32.4",
"@demex-sdk/codecs": "0.0.8",
"@improbable-eng/grpc-web": "^0.15.0",
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
"base58check": "^2.0.0",
"bignumber.js": "^9.1.2",
"bip32": "^2.0.6",
"bip39": "^3.1.0",
"ethers": "^6.13.4",
"secp256k1": "^5.0.1",
"secp256r1": "^0.0.3",
"tslib": "^2.6.2",
"typescript": "^5.7.2"
},
Expand All @@ -42,6 +48,9 @@
"dist-*/**"
],
"devDependencies": {
"@types/crypto-js": "4.2.0",
"@types/secp256k1": "^4.0.6",
"@types/wif": "^2.0.2",
"concurrently": "7.0.0",
"eslint": "^9.13.0"
}
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/constant/query.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Query } from "@demex-sdk/codecs";

export const PGN_1K = Query.PageRequest.fromPartial({ limit: 1000 });
export const SHIFT_DEC_DECIMALS = 18;
export const PGN_10K = Query.PageRequest.fromPartial({ limit: 10000 });

export const SHIFT_DEC_DECIMALS = 18;
1 change: 1 addition & 0 deletions packages/core/src/env/address.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Bech32AddrType = "main" | "validator" | "consensus";
22 changes: 22 additions & 0 deletions packages/core/src/env/chain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Codecs from "@demex-sdk/codecs";
import { TokenClient } from "../helpers";

export type PolyNetworkBridge = Codecs.Carbon.Coin.Bridge;;

export interface AxelarBridge extends Codecs.Carbon.Coin.Bridge {
chain_id_name: string;
bridgeAddress: string;
};

export interface BridgeMap {
polynetwork: PolyNetworkBridge[]
axelar: AxelarBridge[]
};

export type Blockchain = ReturnType<TokenClient['getAllBlockchainNames']>[number] | "Native" | "Carbon" | "Tradehub" | "Ibc" | "Polynetwork";

export const BRIDGE_IDS = {
polynetwork: 1,
ibc: 2,
axelar: 3,
};
14 changes: 14 additions & 0 deletions packages/core/src/env/crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const BIP44_PURPOSE = 44;
export const NEO_COIN_TYPE = 0x00000378;
export const ETH_COIN_TYPE = 0x0000003c;

export const SWTH_COIN_TYPE = 118;

export const DenomPrefix = {
LPToken: "clpt",
CDPToken: "cibt",
};

export const regexCdpDenom = RegExp(`^${DenomPrefix.CDPToken}/`, "i");
export const regexLPDenom = RegExp(`^${DenomPrefix.LPToken}/(\\d+)$`, "i");
export const ibcTokenRegex = /^ibc\/([a-f\d]+)$/i;
5 changes: 4 additions & 1 deletion packages/core/src/env/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export * from "./network";
export * from "./address";
export * from "./chain";
export * from "./crypto";
export * from "./network";
47 changes: 36 additions & 11 deletions packages/core/src/env/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,36 @@ export interface NetworkConfig {
grpcUrl: string
wsUrl: string

bech32Prefix: string
bech32Prefix: string;
}

export interface NetworkMap<T> {
[Network.MainNet]: T;
[Network.TestNet]: T;
[Network.DevNet]: T;
[Network.Local]: T;
};

export const CarbonChainIDs: NetworkMap<string> = {
[Network.MainNet]: "carbon-1",
[Network.TestNet]: "carbon-testnet-42070",
[Network.DevNet]: "carbon-devnet-39911",
[Network.Local]: "carbon-localhost",
} as const;

export const CarbonEVMChainIDs: NetworkMap<string> = {
[Network.MainNet]: "carbon_9790-1",
[Network.TestNet]: "carbon_9792-1",
[Network.DevNet]: "carbon_9791-1",
[Network.Local]: "carbon_9999-1",
} as const;

export const defaultNetworkConfig: Record<Network, NetworkConfig> = {
[Network.MainNet]: {
network: Network.MainNet,
chainId: "carbon-1",
evmChainId: "carbon_9790-1",
chainId: CarbonChainIDs[Network.MainNet],
evmChainId: CarbonEVMChainIDs[Network.MainNet],

tmRpcUrl: "https://tm-api.carbon.network",
restUrl: "https://api.carbon.network",
grpcUrl: "grpc.carbon.network",
Expand All @@ -32,8 +54,9 @@ export const defaultNetworkConfig: Record<Network, NetworkConfig> = {
},
[Network.TestNet]: {
network: Network.TestNet,
chainId: "carbon-testnet-42070",
evmChainId: "carbon_9792-1",
chainId: CarbonChainIDs[Network.TestNet],
evmChainId: CarbonEVMChainIDs[Network.TestNet],

tmRpcUrl: "https://test-tm-api.carbon.network",
restUrl: "https://test-api.carbon.network",
grpcUrl: "test-grpc.carbon.network",
Expand All @@ -43,8 +66,9 @@ export const defaultNetworkConfig: Record<Network, NetworkConfig> = {
},
[Network.DevNet]: {
network: Network.DevNet,
chainId: "carbon-devnet-39911",
evmChainId: "carbon_9791-1",
chainId: CarbonChainIDs[Network.DevNet],
evmChainId: CarbonEVMChainIDs[Network.DevNet],

tmRpcUrl: "https://dev-tm-api.carbon.network",
restUrl: "https://dev-api.carbon.network",
grpcUrl: "dev-grpc.carbon.network",
Expand All @@ -54,21 +78,22 @@ export const defaultNetworkConfig: Record<Network, NetworkConfig> = {
},
[Network.Local]: {
network: Network.Local,
chainId: "carbon-localhost",
evmChainId: "carbon_9999-1",
chainId: CarbonChainIDs[Network.Local],
evmChainId: CarbonEVMChainIDs[Network.Local],

tmRpcUrl: "http://localhost:26657",
restUrl: "http://localhost:1317",
grpcUrl: "localhost:9090",
wsUrl: "ws://localhost:5001",

bech32Prefix: "tswth",
},
}
};

export const evmChainIds: Record<string, string> = Object.values(defaultNetworkConfig).reduce(
(acc, { chainId, evmChainId }) => ({
...acc,
[chainId]: evmChainId,
}),
{}
);
);
1 change: 1 addition & 0 deletions packages/core/src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./token";
192 changes: 192 additions & 0 deletions packages/core/src/helpers/token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import Codecs from "@demex-sdk/codecs";
import Long from "long";
import { PGN_10K } from "../constant";
import { AxelarBridge, Blockchain, BRIDGE_IDS, BridgeMap, ibcTokenRegex, Network, NetworkConfig, PolyNetworkBridge, regexCdpDenom, regexLPDenom } from "../env";
import { DemexQueryClient } from "../query";
import { OptionalNetworkMap, SimpleMap } from "../util";

export const TokenBlacklist: OptionalNetworkMap<string[]> = {
[Network.MainNet]: [
"brkl.1.2.337f55", // wrong token address
"zusdt.1.18.1cbca1", // duplicated token
],
};

export class TokenClient {
private tokens: SimpleMap<Codecs.Carbon.Coin.Token> | null = null;
private bridges: BridgeMap | null = null;

private constructor(public readonly query: DemexQueryClient, public readonly networkConfig: NetworkConfig) { }

public static instance(query: DemexQueryClient, networkConfig: NetworkConfig) {
return new TokenClient(query, networkConfig);
}

public async initialize(): Promise<void> {
try {
await Promise.all([
this.getAllTokens(),
this.getBridges(),
]);
} catch (err) {
const errorTyped = err as Error;
console.error("failed to query token and bridge info:", errorTyped.message);
}
}

public async getAllTokens(): Promise<Codecs.Carbon.Coin.Token[]> {
if (!this.tokens) {
this.tokens = {};
const result = await this.query.coin.TokenAll({ pagination: PGN_10K });
const tokenBlacklist = TokenBlacklist[this.networkConfig.network] ?? [];
result.tokens.forEach((token: Codecs.Carbon.Coin.Token) => {
if (tokenBlacklist.includes(token.denom)) return;
this.tokens![token.denom] = token;
});
}
return Object.values(this.tokens) ?? [];
};

public async getBridges(): Promise<BridgeMap> {
if (!this.bridges) {
const allBridges = await this.query.coin.BridgeAll({ pagination: PGN_10K });
const axelarBridges = await this.mapBridgesFromConnections()

const polynetworkBridges = allBridges.bridges.reduce((prev: PolyNetworkBridge[], bridge: Codecs.Carbon.Coin.Bridge) => {
if (bridge.bridgeId.toNumber() !== BRIDGE_IDS.polynetwork) return prev;
prev.push({ ...bridge } as PolyNetworkBridge);
return prev;
}, [])

this.bridges = {
polynetwork: polynetworkBridges,
axelar: axelarBridges,
};
}
return this.bridges!;
}

async mapBridgesFromConnections(): Promise<AxelarBridge[]> {
const newBridges: AxelarBridge[] = []
try {
const results: Codecs.Carbon.Bridge.QueryAllConnectionsResponse = await this.query.bridge.ConnectionAll({
bridgeId: new Long(0),
pagination: PGN_10K,
});
const connections = results.connections
connections.forEach((connection: Codecs.Carbon.Bridge.Connection) => {
newBridges.push({
name: `${connection.chainDisplayName} via Axelar`,
bridgeId: new Long(BRIDGE_IDS.axelar),
chainId: new Long(BRIDGE_IDS.axelar),
bridgeAddress: connection.connectionId,
chain_id_name: connection.chainId,
chainName: connection.chainDisplayName,
bridgeName: 'Axelar',
bridgeAddresses: [],
enabled: connection.isEnabled,
});
});
} catch (err) {
console.error(err)
} finally {
const chainMap: SimpleMap<string> = {};

newBridges.forEach((bridge) => {
const chainId = bridge.chain_id_name;
if (chainMap[chainId]) {
bridge.chainName = chainMap[chainId];
} else {
chainMap[chainId] = bridge.chainName;
}
});
}
return newBridges;
};

public getBlockchain(denom: string | undefined): Blockchain | undefined {
if (!denom || !this.tokens?.[denom]) return undefined
const token = this.tokens[denom];
if (this.isNativeToken(denom) || this.isNativeStablecoin(denom) || TokenClient.isPoolToken(denom) || TokenClient.isCdpToken(denom) || this.isGroupedToken(denom)) {
// native denoms "swth" and "usc" should be native.
// pool and cdp tokens are on the Native blockchain, hence 0
return 'Native'
}

if (this.isBridgedToken(denom)) {
// brdg tokens will all be chain_id 0 which will also be deprecated in future
// hence for brdg tokens cannot use chain_id to differentiate between blockchains
const chainName = this.bridges?.axelar.find((bridge: AxelarBridge) => bridge.bridgeAddress === token.bridgeAddress)?.chainName
return chainName
}
const bridge = this.getBridgeFromToken(token)
return bridge?.chainName;
};

public getBridgesFromBridgeId(bridgeId: number): Codecs.Carbon.Coin.Bridge[] | AxelarBridge[] | undefined {
switch (bridgeId) {
case BRIDGE_IDS.polynetwork:
return this.bridges?.polynetwork
case BRIDGE_IDS.axelar:
return this.bridges?.axelar
default:
return undefined
}
};

public getBridgeFromToken(token: Codecs.Carbon.Coin.Token | null): Codecs.Carbon.Coin.Bridge | undefined {
if (!token || !token.bridgeId) return undefined
const bridgeList = this.getBridgesFromBridgeId(token.bridgeId.toNumber())
return bridgeList?.find(bridge => token.chainId.equals(bridge.chainId))
};

public getPolynetworkBlockchainNames(): string[] {
return (this.bridges?.polynetwork ?? []).map((bridge: PolyNetworkBridge) => bridge.chainName);
};

public getAxelarBlockchainNames(): string[] {
return (this.bridges?.axelar ?? []).map((bridge: AxelarBridge) => bridge.chainName);
};

public getAllBlockchainNames(): string[] {
return this.getPolynetworkBlockchainNames().concat(this.getAxelarBlockchainNames());
};

public isNativeToken(denom: string): boolean {
return denom === "swth";
};

public isNativeStablecoin(denom: string): boolean {
return denom === "usc";
};

public static isPoolToken(denom: string): boolean {
return this.isPoolTokenNew(denom) || this.isPoolTokenLegacy(denom);
};

public static isPoolTokenNew(denom: string): boolean {
return denom.match(regexLPDenom) !== null;
};

public static isPoolTokenLegacy(denom: string): boolean {
return denom.match(/^([a-z\d.-]+)-(\d+)-([a-z\d.-]+)-(\d+)-lp\d+$/i) !== null;
};

public static isCdpToken(denom: string): boolean {
return denom.match(regexCdpDenom) !== null;
};

public static isIBCDenom(denom: string): boolean {
return denom.match(ibcTokenRegex) !== null;
};

public isBridgedToken(denom: string): boolean {
const bridgedTokenRegex = new RegExp(/^brdg\//)
return bridgedTokenRegex.test(denom)
};

public isGroupedToken(denom: string): boolean {
const groupedTokenRegex = new RegExp(/^cgt\/\d+$/)
return groupedTokenRegex.test(denom)
};
};
Loading