Skip to content

Commit

Permalink
Feat/axelar bridge (#457)
Browse files Browse the repository at this point in the history
* Gen-codecs from carbon 42559b8 branch

* Add alias for bridge module

* Add bridge to carbon query client

* Add bridge queries and scaffolding for Axelar Bridge Client

* WIP deposit test script

* revert devnet changes

* integrate Axelar bridge into existing getBridges

* test deposit

* Fix build error

* Implement AxelarBridgeClient Withdraw rpc

* sync codecs

* Deposit with custom network

* Add mantle rpc url

* Refactor getBridges

* gen codec for v2.47.0

* Fix mantle network

* v0.11.13-beta.1

* Fix amino WithdrawTokenTx

* Fix rpc url

* Remove test script

* Remove unnecessary code

* Fix PR comments

* Revert devnet network rpc changes

* Fix hydrogen bridge blockchain

* Update blockchain constants

* Revert amino changes

* Fix return type

* Remove unused imports

* Fix hydrogen transfers result formatter

* Add fallback to formattedBlockchainName

* Add error catching to axelar bridge connections query

* Add test axelar withdraw script

* Add bridge module

* fix duration parsing for amino + fix duration to be string in eip712 types

* remove old code for amino duration type parsing

* Refactor common variable and withdraw params

* Add typeCheck for Duration interface, remove duplicate code

* Fix testnet config

* Add relay fees query

* Feat/2.50.0 codec (#516)

* update verifyingContract hex due to validation from metamask

* v0.11.15

* generate codec for v2.50.0

* regenerate bridge alias for new refund address msgs

* remove deprecated oracleId field from assAsset msg in cdp

---------

Co-authored-by: Randy <[email protected]>

* revert rpcurls for devnet

* v0.11.16-beta.1

* regen codec for new poly blacklist query (#518)

* Refactor axelar bridge from connections

* v0.11.16-beta.2

---------

Co-authored-by: huy9x101 <[email protected]>
Co-authored-by: yan-soon <[email protected]>
Co-authored-by: Thong Yuan Yu Sarah <[email protected]>
Co-authored-by: Randy <[email protected]>
Co-authored-by: Soon Xiang, Yan <[email protected]>
Co-authored-by: Randy <[email protected]>
  • Loading branch information
7 people authored Sep 23, 2024
1 parent 8ef492f commit 993fc0d
Show file tree
Hide file tree
Showing 45 changed files with 15,994 additions and 723 deletions.
51 changes: 51 additions & 0 deletions examples/_test-amino-bridge-withdraw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import BigNumber from "bignumber.js";
import * as BIP39 from "bip39";
import Long from "long";
import { BridgeModule, CarbonSDK, CoinModule } from "./_sdk";
import "./_setup";

(async () => {
const mnemonics = process.env.MNEMONICS ?? BIP39.generateMnemonic();
console.log("mnemonics", mnemonics);

const sdk = await CarbonSDK.instanceWithMnemonic(mnemonics, { network: CarbonSDK.Network.LocalHost });
console.log('sdk initialized')

const externalTokensResult = await sdk.query.bridge.ExternalTokenAll({ bridgeId: new Long(0), chainId: '', denom: '' })

const externalToken = externalTokensResult.externalTokens[0]
const externalTokenCarbonDenom = externalToken?.denom ?? ''

const body = JSON.stringify({
address: sdk?.wallet?.bech32Address,
coins: ['1000000000swth'],
})
await fetch(sdk.networkConfig.faucetUrl, { method: 'POST', body })
console.log('Minted SWTH for gas')

const tokenDp = sdk.token.getDecimals(externalTokenCarbonDenom) ?? 0
const params: CoinModule.MintTokenParams = {
amount: new BigNumber(10000).shiftedBy(tokenDp),
denom: externalTokenCarbonDenom,
}
const mintBrdgToken = await sdk.coin.mintToken(params)
console.log('Minted brdg tokens', mintBrdgToken)

const balancesResult = await sdk.query.coin.Balances({ address: sdk.wallet.bech32Address })
console.log('balances:', balancesResult)

const withdrawParams: BridgeModule.WithdrawParams = {
connectionId: externalToken?.connectionId ?? '',
receiver: sdk.wallet.evmHexAddress,
tokenDenom: externalToken?.denom ?? '',
tokenAmount: new BigNumber(10).shiftedBy(tokenDp),
relayDenom: 'swth',
relayAmount: new BigNumber(100),
expirySeconds: 1000,
}
const result = await sdk.bridge.withdraw(withdrawParams)

console.log('withdraw tokens:', result);
})().catch((e) => {
console.log({ e })
}).finally(() => process.exit(0));
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "carbon-js-sdk",
"version": "0.11.16",
"version": "0.11.16-beta.2",
"description": "TypeScript SDK for Carbon blockchain",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down Expand Up @@ -89,4 +89,4 @@
"eslint": "^8.42.0",
"prettier": "2.5.1"
}
}
}
59 changes: 30 additions & 29 deletions scripts/generate-carbon-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,36 @@ const [pwd, modelsFile] = files.splice(files.length - 2, 2);
const MODEL_BLACKLIST = ['MsgClientImpl', 'protobufPackage', 'GenesisState', 'QueryClientImpl']

const whitelistCarbonExports: { [name: string]: string } = {
'Admin': 'Switcheo/carbon/admin',
'Alliance': 'alliance/alliance',
'Bank': 'Switcheo/carbon/bank',
'Book': 'Switcheo/carbon/book',
'Broker': 'Switcheo/carbon/broker',
'Cdp': 'Switcheo/carbon/cdp',
'Coin': 'Switcheo/carbon/coin',
'Erc20': 'Switcheo/carbon/erc20',
'Evmbank': 'Switcheo/carbon/evmbank',
'Evmcontract': 'Switcheo/carbon/evmcontract',
'Evmmerge': 'Switcheo/carbon/evmmerge',
'Fee': 'Switcheo/carbon/fee',
'Inflation': 'Switcheo/carbon/inflation',
'Insurance': 'Switcheo/carbon/insurance',
'Leverage': 'Switcheo/carbon/leverage',
'Liquidation': 'Switcheo/carbon/liquidation',
'Liquiditypool': 'Switcheo/carbon/liquiditypool',
'Market': 'Switcheo/carbon/market',
'Marketstats': 'Switcheo/carbon/marketstats',
'Misc': 'Switcheo/carbon/misc',
'Oracle': 'Switcheo/carbon/oracle',
'Order': 'Switcheo/carbon/order',
'Perpspool': 'Switcheo/carbon/perpspool',
'Position': 'Switcheo/carbon/position',
'Pricing': 'Switcheo/carbon/pricing',
'Profile': 'Switcheo/carbon/profile',
'Sequence': 'Switcheo/carbon/sequence',
'Subaccount': 'Switcheo/carbon/subaccount'
};
'Admin': 'Switcheo/carbon/admin',
'Alliance': 'alliance/alliance',
'Bank': 'Switcheo/carbon/bank',
'Book': 'Switcheo/carbon/book',
'Bridge': 'Switcheo/carbon/bridge',
'Broker': 'Switcheo/carbon/broker',
'Cdp': 'Switcheo/carbon/cdp',
'Coin': 'Switcheo/carbon/coin',
'Erc20': 'Switcheo/carbon/erc20',
'Evmbank': 'Switcheo/carbon/evmbank',
'Evmcontract': 'Switcheo/carbon/evmcontract',
'Evmmerge': 'Switcheo/carbon/evmmerge',
'Fee': 'Switcheo/carbon/fee',
'Inflation': 'Switcheo/carbon/inflation',
'Insurance': 'Switcheo/carbon/insurance',
'Leverage': 'Switcheo/carbon/leverage',
'Liquidation': 'Switcheo/carbon/liquidation',
'Liquiditypool': 'Switcheo/carbon/liquiditypool',
'Market': 'Switcheo/carbon/market',
'Marketstats': 'Switcheo/carbon/marketstats',
'Misc': 'Switcheo/carbon/misc',
'Oracle': 'Switcheo/carbon/oracle',
'Order': 'Switcheo/carbon/order',
'Perpspool': 'Switcheo/carbon/perpspool',
'Position': 'Switcheo/carbon/position',
'Pricing': 'Switcheo/carbon/pricing',
'Profile': 'Switcheo/carbon/profile',
'Sequence': 'Switcheo/carbon/sequence',
'Subaccount': 'Switcheo/carbon/subaccount'
};

for (const exportName in whitelistCarbonExports) {
const directoryPath = whitelistCarbonExports[exportName];
Expand Down
1 change: 1 addition & 0 deletions scripts/generate-eip712-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ function getSolidityTyping(fieldType: string): string {

function convertGoogleProtobufTypingToPrimitive(fieldType: string): string {
switch (fieldType) {
case 'duration':
case 'timestamp':
case 'stringvalue':
return 'string'
Expand Down
2 changes: 1 addition & 1 deletion scripts/generate-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const prefixCarbonDir = (m: string) => `Switcheo/carbon/${m}`;

const polynetworkFolders = ['btcx', 'ccm', 'headersync', 'lockproxy'];

const carbonFolders = ['admin', 'bank', 'book', 'broker', 'cdp', 'coin',
const carbonFolders = ['admin', 'bank', 'book', 'bridge', 'broker', 'cdp', 'coin',
'erc20', 'evmbank', 'evmcontract', 'evmmerge', 'fee', 'inflation', 'insurance', 'leverage', 'liquidation',
'liquiditypool', 'market', 'marketstats', 'misc', 'oracle', 'order', 'perpspool',
'position', 'pricing', 'profile', 'sequence', 'subaccount'];
Expand Down
12 changes: 9 additions & 3 deletions src/CarbonSDK.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ import { Tendermint37Client } from "@cosmjs/tendermint-rpc";
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
import BigNumber from "bignumber.js";
import * as clients from "./clients";
import { CarbonQueryClient, ETHClient, HydrogenClient, InsightsQueryClient, NEOClient, TokenClient, ZILClient } from "./clients";
import { CarbonQueryClient, AxelarBridgeClient, ETHClient, HydrogenClient, InsightsQueryClient, NEOClient, TokenClient, ZILClient } from "./clients";
import GasFee from "./clients/GasFee";
import GrpcQueryClient from "./clients/GrpcQueryClient";
import N3Client from "./clients/N3Client";
import {
AdminModule,
AllianceModule,
BankModule,
BridgeModule,
BrokerModule,
CDPModule,
CoinModule,
Expand Down Expand Up @@ -144,13 +145,15 @@ class CarbonSDK {
xchain: XChainModule;
evm: EvmModule;
evmmerge: EvmMergeModule;
bridge: BridgeModule;

neo: NEOClient;
eth: ETHClient;
bsc: ETHClient;
arbitrum: ETHClient;
polygon: ETHClient;
okc: ETHClient;
axelarBridgeClient: AxelarBridgeClient;
zil: ZILClient;
n3: N3Client;
chainId: string;
Expand Down Expand Up @@ -213,6 +216,7 @@ class CarbonSDK {
this.xchain = new XChainModule(this);
this.evm = new EvmModule(this);
this.evmmerge = new EvmMergeModule(this);
this.bridge = new BridgeModule(this);

this.neo = NEOClient.instance({
configProvider: this,
Expand Down Expand Up @@ -258,6 +262,10 @@ class CarbonSDK {
blockchain: Blockchain.Okc,
tokenClient: this.token,
});

this.axelarBridgeClient = AxelarBridgeClient.instance({
configProvider: this,
})
}

public static async instance(opts: CarbonSDKInitOpts = DEFAULT_SDK_INIT_OPTS) {
Expand All @@ -266,10 +274,8 @@ class CarbonSDK {
const defaultTimeoutBlocks = opts.defaultTimeoutBlocks;
const networkConfig = GenericUtils.overrideConfig(NetworkConfigs[network], configOverride);
const tmClient: Tendermint37Client = opts.tmClient ?? new (Tendermint37Client as any)(new clients.BatchQueryClient(networkConfig.tmRpcUrl)); // fallback tmClient

let chainId = networkConfig.chainId; // fallback chain ID
let normalInit = true;

try {
chainId = (await tmClient.status())?.nodeInfo.network;
} catch (error) {
Expand Down
53 changes: 53 additions & 0 deletions src/clients/AxelarBridgeClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { NetworkConfigProvider } from "@carbon-sdk/constant";
import { ABIs } from "@carbon-sdk/eth";
import BigNumber from "bignumber.js";
import { ethers } from "ethers";

export interface AxelarBridgeClientOpts {
configProvider: NetworkConfigProvider;
}

export interface DepositParams {
contractAddress: string;
senderAddress: string;
receiverAddress: string;
amount: BigNumber;
depositTokenExternalAddress: string;
rpcUrl: string;
gasPriceGwei?: BigNumber;
gasLimit?: BigNumber;
signer: ethers.Signer;
nonce?: number
}

export interface EthersTransactionResponse extends ethers.Transaction {
wait: () => Promise<ethers.Transaction>;
}

export class AxelarBridgeClient {

private constructor(
public readonly configProvider: NetworkConfigProvider,
) { }

public static instance(opts: AxelarBridgeClientOpts) {
const { configProvider } = opts
return new AxelarBridgeClient(configProvider)
}

// lock deposit
public async deposit(params: DepositParams): Promise<EthersTransactionResponse> {
const { contractAddress, senderAddress, receiverAddress, depositTokenExternalAddress, amount, signer, rpcUrl } = params;
const rpcProvider = new ethers.providers.JsonRpcProvider(rpcUrl)
const contract = new ethers.Contract(contractAddress, ABIs.axelarBridge, rpcProvider)

return await contract.connect(signer).deposit(
senderAddress, // tokenSender
receiverAddress, // carbonReceiver bech32Address
depositTokenExternalAddress, // asset
amount.toString(10),
)
}
}

export default AxelarBridgeClient
3 changes: 3 additions & 0 deletions src/clients/CarbonQueryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { QueryClientImpl as CoinQueryClient } from "@carbon-sdk/codec/Switcheo/c
import { QueryClientImpl as AuthQueryClient } from "@carbon-sdk/codec/cosmos/auth/v1beta1/query";
import { QueryClientImpl as BankQueryClient } from "@carbon-sdk/codec/cosmos/bank/v1beta1/query";
import { QueryClientImpl as NativeBankQueryClient } from "@carbon-sdk/codec/Switcheo/carbon/bank/query";
import { QueryClientImpl as BridgeQueryClient } from "@carbon-sdk/codec/Switcheo/carbon/bridge/query";
import { ServiceClientImpl as CosmosTmClient } from "@carbon-sdk/codec/cosmos/base/tendermint/v1beta1/query";
import { QueryClientImpl as DistributionQueryClient } from "@carbon-sdk/codec/cosmos/distribution/v1beta1/query";
import { QueryClientImpl as EvidenceQueryClient } from "@carbon-sdk/codec/cosmos/evidence/v1beta1/query";
Expand Down Expand Up @@ -75,6 +76,7 @@ export interface CarbonQueryClientOpts {
class CarbonQueryClient {
adl: ADLQueryClient;
book: BookQueryClient;
bridge: BridgeQueryClient;
broker: BrokerQueryClient;
coin: CoinQueryClient;
cdp: CDPQueryClient;
Expand Down Expand Up @@ -130,6 +132,7 @@ class CarbonQueryClient {
this.adl = new ADLQueryClient(rpcClient);
this.alliance = new AllianceClient(rpcClient);
this.book = new BookQueryClient(rpcClient);
this.bridge = new BridgeQueryClient(rpcClient);
this.broker = new BrokerQueryClient(rpcClient);
this.coin = new CoinQueryClient(rpcClient);
this.cdp = new CDPQueryClient(rpcClient);
Expand Down
22 changes: 16 additions & 6 deletions src/clients/HydrogenClient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Network, NetworkConfig } from "@carbon-sdk/constant";
import { APIUtils, BlockchainUtils } from "@carbon-sdk/util";
import { getFormattedBlockchainName } from "@carbon-sdk/util/blockchain";
import dayjs from "dayjs";
import {
ChainTransaction,
Expand Down Expand Up @@ -28,6 +29,9 @@ export const HydrogenEndpoints = {

// Fee service api
fee_quote: "/fee_quote",

// Bridge fees
bridge_fee: "/bridge_fee",
};

const formatDateField = (value?: string | null) => {
Expand Down Expand Up @@ -102,6 +106,8 @@ const getBridgeBlockchainFromId = (bridgeId: number): BlockchainUtils.Blockchain
return 'Polynetwork'
case 2:
return 'Ibc'
case 3:
return 'Axelar'
default:
return 'Polynetwork'
}
Expand Down Expand Up @@ -130,13 +136,16 @@ class HydrogenClient {

public formatCrossChainTransferV2 = (value: any): CrossChainTransfer => {
if (typeof value !== "object") return value;
// 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 isBridgeToken = this.tokenClient.isBridgedToken(value.carbon_token_id)
return {
...value,
created_at: formatDateField(value.created_at?.toString()),
updated_at: formatDateField(value.updated_at?.toString()),
source_blockchain: this.tokenClient.getBlockchainV2FromIDs(Number(value.from_chain_id), value.bridge_id),
source_blockchain: isBridgeToken ? getFormattedBlockchainName(value.source_blockchain) : this.tokenClient.getBlockchainV2FromIDs(Number(value.from_chain_id), value.bridge_id),
bridging_blockchain: getBridgeBlockchainFromId(value.bridge_id),
destination_blockchain: this.tokenClient.getBlockchainV2FromIDs(Number(value.to_chain_id), value.bridge_id),
destination_blockchain: isBridgeToken ? getFormattedBlockchainName(value.destination_blockchain) : this.tokenClient.getBlockchainV2FromIDs(Number(value.to_chain_id), value.bridge_id),
source_event: this.formatChainEventV2(value.source_event, value.source_blockchain ?? ''),
bridging_event: this.formatChainEventV2(value.bridging_event, getBridgeBlockchainFromId(value.bridge_id)),
destination_event: this.formatChainEventV2(value.destination_event, value.destination_blockchain ?? ''),
Expand All @@ -146,8 +155,9 @@ class HydrogenClient {

public formatCrossChainTransferDetailedV2 = (value: any): CrossChainTransferDetailed => {
if (!value || typeof value !== "object") return value;
const source_blockchain = this.tokenClient.getBlockchainV2FromIDs(Number(value.from_chain_id), value.bridge_id)
const destination_blockchain = this.tokenClient.getBlockchainV2FromIDs(Number(value.to_chain_id), value.bridge_id)
const isBridgeToken = this.tokenClient.isBridgedToken(value.carbon_token_id)
const source_blockchain = isBridgeToken ? getFormattedBlockchainName(value.source_blockchain) : this.tokenClient.getBlockchainV2FromIDs(Number(value.from_chain_id), value.bridge_id)
const destination_blockchain = isBridgeToken ? getFormattedBlockchainName(value.destination_blockchain) : this.tokenClient.getBlockchainV2FromIDs(Number(value.to_chain_id), value.bridge_id)
const bridging_blockchain = getBridgeBlockchainFromId(value.bridge_id)
return {
...this.formatCrossChainTransferV2(value),
Expand Down Expand Up @@ -200,7 +210,7 @@ class HydrogenClient {
const request = this.apiManager.path(
"transfer_payloads",
{},
{...req}
{ ...req }
);
const response = await request.get();
const result = response.data;
Expand Down Expand Up @@ -258,7 +268,7 @@ class HydrogenClient {
...req,
}
);
const response = await request.post({ body: { fee_denoms: req.fee_denoms }});
const response = await request.post({ body: { fee_denoms: req.fee_denoms } });
const result = response.data;

return version === "V1" ? formatFeeQuote(result) : this.formatFeeQuoteV2(result, blockchain!);
Expand Down
Loading

0 comments on commit 993fc0d

Please sign in to comment.