@dydxprotocol/v4-client-js 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,118 @@
1
+ import { EncodeObject } from '@cosmjs/proto-signing';
2
+ import Long from 'long';
3
+
4
+ import { Network } from '../src/clients/constants';
5
+ import LocalWallet from '../src/clients/modules/local-wallet';
6
+ import { NobleClient } from '../src/clients/noble-client';
7
+ import { ValidatorClient } from '../src/clients/validator-client';
8
+ import { BECH32_PREFIX, NOBLE_BECH32_PREFIX } from '../src/lib/constants';
9
+ import { sleep } from '../src/lib/utils';
10
+ import { DYDX_TEST_MNEMONIC } from './constants';
11
+
12
+ async function test(): Promise<void> {
13
+ const dydxClient = await ValidatorClient.connect(
14
+ Network.testnet().validatorConfig,
15
+ );
16
+
17
+ const dydxWallet = await LocalWallet.fromMnemonic(
18
+ DYDX_TEST_MNEMONIC,
19
+ BECH32_PREFIX,
20
+ );
21
+ const nobleWallet = await LocalWallet.fromMnemonic(
22
+ DYDX_TEST_MNEMONIC,
23
+ NOBLE_BECH32_PREFIX,
24
+ );
25
+
26
+ const client = new NobleClient('https://rpc.testnet.noble.strange.love');
27
+ await client.connect(nobleWallet);
28
+
29
+ if (nobleWallet.address === undefined || dydxWallet.address === undefined) {
30
+ throw new Error('Wallet not found');
31
+ }
32
+
33
+ // IBC to noble
34
+
35
+ const ibcToNobleMsg: EncodeObject = {
36
+ typeUrl: '/ibc.applications.transfer.v1.MsgTransfer',
37
+ value: {
38
+ sourcePort: 'transfer',
39
+ sourceChannel: 'channel-0',
40
+ token: {
41
+ denom:
42
+ 'ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5',
43
+ amount: '1000000',
44
+ },
45
+ sender: dydxWallet.address,
46
+ receiver: nobleWallet.address,
47
+ timeoutTimestamp: Long.fromNumber(
48
+ Math.floor(Date.now() / 1000) * 1e9 + 10 * 60 * 1e9,
49
+ ),
50
+ },
51
+ };
52
+
53
+ const msgs = [ibcToNobleMsg];
54
+ const encodeObjects: Promise<EncodeObject[]> = new Promise((resolve) => resolve(msgs),
55
+ );
56
+
57
+ await dydxClient.post.send(
58
+ dydxWallet,
59
+ () => {
60
+ return encodeObjects;
61
+ },
62
+ false,
63
+ undefined,
64
+ undefined,
65
+ );
66
+
67
+ await sleep(30000);
68
+
69
+ try {
70
+ const coins = await client.getAccountBalances();
71
+ console.log('Balances');
72
+ console.log(JSON.stringify(coins));
73
+
74
+ // IBC from noble
75
+
76
+ const ibcFromNobleMsg: EncodeObject = {
77
+ typeUrl: '/ibc.applications.transfer.v1.MsgTransfer',
78
+ value: {
79
+ sourcePort: 'transfer',
80
+ sourceChannel: 'channel-21',
81
+ token: {
82
+ denom: 'uusdc',
83
+ amount: coins[0].amount,
84
+ },
85
+ sender: nobleWallet.address,
86
+ receiver: dydxWallet.address,
87
+ timeoutTimestamp: Long.fromNumber(
88
+ Math.floor(Date.now() / 1000) * 1e9 + 10 * 60 * 1e9,
89
+ ),
90
+ },
91
+ };
92
+ const fee = await client.simulateTransaction([ibcFromNobleMsg]);
93
+
94
+ ibcFromNobleMsg.value.token.amount = (parseInt(ibcFromNobleMsg.value.token.amount, 10) -
95
+ Math.floor(parseInt(fee.amount[0].amount, 10) * 1.4)).toString();
96
+
97
+ await client.send([ibcFromNobleMsg]);
98
+ } catch (error) {
99
+ console.log(JSON.stringify(error.message));
100
+ }
101
+
102
+ await sleep(30000);
103
+
104
+ try {
105
+ const coin = await client.getAccountBalance('uusdc');
106
+ console.log('Balance');
107
+ console.log(JSON.stringify(coin));
108
+ } catch (error) {
109
+ console.log(JSON.stringify(error.message));
110
+ }
111
+ }
112
+
113
+ test()
114
+ .then(() => {})
115
+ .catch((error) => {
116
+ console.log(error.message);
117
+ console.log(error);
118
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dydxprotocol/v4-client-js",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "General client library for the new dYdX system (v4 decentralized)",
5
5
  "main": "build/src/index.js",
6
6
  "scripts": {
@@ -28,6 +28,7 @@ export default class LocalWallet {
28
28
  address?: string;
29
29
  pubKey?: Secp256k1Pubkey;
30
30
  signer?: TransactionSigner;
31
+ offlineSigner?: OfflineSigner;
31
32
 
32
33
  static async fromOfflineSigner(signer:OfflineSigner): Promise<LocalWallet> {
33
34
  const wallet = new LocalWallet();
@@ -42,6 +43,7 @@ export default class LocalWallet {
42
43
  }
43
44
 
44
45
  async setSigner(signer: OfflineSigner): Promise<void> {
46
+ this.offlineSigner = signer;
45
47
  const stargateClient = await SigningStargateClient.offline(
46
48
  signer,
47
49
  {
@@ -8,7 +8,7 @@ import { Order_Side, Order_TimeInForce } from '@dydxprotocol/v4-proto/src/codege
8
8
  import * as AuthModule from 'cosmjs-types/cosmos/auth/v1beta1/query';
9
9
  import Long from 'long';
10
10
 
11
- import { BECH32_PREFIX } from '../lib/constants';
11
+ import { BECH32_PREFIX, GAS_MULTIPLIER, NOBLE_BECH32_PREFIX } from '../lib/constants';
12
12
  import { UserError } from '../lib/errors';
13
13
  import { ByteArrayEncoding, encodeJson } from '../lib/helpers';
14
14
  import { deriveHDKeyFromEthereumSignature } from '../lib/onboarding';
@@ -19,6 +19,7 @@ import {
19
19
  } from './constants';
20
20
  import { FaucetClient } from './faucet-client';
21
21
  import LocalWallet from './modules/local-wallet';
22
+ import { NobleClient } from './noble-client';
22
23
  import { SubaccountInfo } from './subaccount';
23
24
  import { OrderFlags } from './types';
24
25
 
@@ -29,6 +30,9 @@ declare global {
29
30
  var faucetClient: FaucetClient | null;
30
31
  // eslint-disable-next-line vars-on-top, no-var
31
32
  var wallet: LocalWallet;
33
+
34
+ // eslint-disable-next-line vars-on-top, no-var
35
+ var nobleClient: NobleClient | undefined;
32
36
  }
33
37
 
34
38
  export async function connectClient(
@@ -53,6 +57,7 @@ export async function connectNetwork(
53
57
  validatorUrl,
54
58
  chainId,
55
59
  faucetUrl,
60
+ nobleValidatorUrl,
56
61
  USDC_DENOM,
57
62
  USDC_DECIMALS,
58
63
  USDC_GAS_DENOM,
@@ -90,6 +95,7 @@ export async function connectNetwork(
90
95
  } else {
91
96
  globalThis.faucetClient = null;
92
97
  }
98
+ globalThis.nobleClient = new NobleClient(nobleValidatorUrl);
93
99
  return encodeJson(config);
94
100
  } catch (e) {
95
101
  return wrappedError(e);
@@ -101,6 +107,12 @@ export async function connectWallet(
101
107
  ): Promise<string> {
102
108
  try {
103
109
  globalThis.wallet = await LocalWallet.fromMnemonic(mnemonic, BECH32_PREFIX);
110
+ const nobleWallet = await LocalWallet.fromMnemonic(
111
+ mnemonic,
112
+ NOBLE_BECH32_PREFIX,
113
+ );
114
+ await globalThis.nobleClient?.connect(nobleWallet);
115
+
104
116
  const address = globalThis.wallet.address!;
105
117
  return encodeJson({ address });
106
118
  } catch (e) {
@@ -988,3 +1000,54 @@ export async function getMarketPrice(
988
1000
  return wrappedError(e);
989
1001
  }
990
1002
  }
1003
+
1004
+ export async function getNobleBalance(): Promise<String> {
1005
+ try {
1006
+ const client = globalThis.nobleClient;
1007
+ if (client === undefined || !client.isConnected) {
1008
+ throw new UserError(
1009
+ 'client is not connected.',
1010
+ );
1011
+ }
1012
+ const coin = await client.getAccountBalance('uusdc');
1013
+ return encodeJson(coin);
1014
+ } catch (error) {
1015
+ return wrappedError(error);
1016
+ }
1017
+ }
1018
+
1019
+ export async function sendNobleIBC(squidPayload: string): Promise<String> {
1020
+ try {
1021
+ const client = globalThis.nobleClient;
1022
+ if (client === undefined || !client.isConnected) {
1023
+ throw new UserError(
1024
+ 'client is not connected.',
1025
+ );
1026
+ }
1027
+
1028
+ const decode = (str: string): string => Buffer.from(str, 'base64').toString('binary');
1029
+ const decoded = decode(squidPayload);
1030
+
1031
+ const json = JSON.parse(decoded);
1032
+
1033
+ const ibcMsg: EncodeObject = {
1034
+ typeUrl: json.msgTypeUrl, // '/ibc.applications.transfer.v1.MsgTransfer',
1035
+ value: json.msg,
1036
+ };
1037
+ const fee = await client.simulateTransaction([ibcMsg]);
1038
+
1039
+ // take out fee from amount before sweeping
1040
+ const amount = parseInt(ibcMsg.value.token.amount, 10) -
1041
+ Math.floor(parseInt(fee.amount[0].amount, 10) * GAS_MULTIPLIER);
1042
+
1043
+ if (amount <= 0) {
1044
+ throw new UserError('noble balance does not cover fees');
1045
+ }
1046
+
1047
+ ibcMsg.value.token.amount = amount.toString();
1048
+ const tx = await client.send([ibcMsg]);
1049
+ return encodeJson(tx);
1050
+ } catch (error) {
1051
+ return wrappedError(error);
1052
+ }
1053
+ }
@@ -0,0 +1,99 @@
1
+ import { EncodeObject, Registry, Coin } from '@cosmjs/proto-signing';
2
+ import {
3
+ calculateFee,
4
+ DeliverTxResponse,
5
+ GasPrice,
6
+ StdFee,
7
+ defaultRegistryTypes,
8
+ SigningStargateClient,
9
+ } from '@cosmjs/stargate';
10
+
11
+ import { GAS_MULTIPLIER } from './constants';
12
+ import LocalWallet from './modules/local-wallet';
13
+
14
+ export class NobleClient {
15
+ private wallet?: LocalWallet;
16
+ private restEndpoint: string;
17
+ private stargateClient?: SigningStargateClient;
18
+
19
+ constructor(restEndpoint: string) {
20
+ this.restEndpoint = restEndpoint;
21
+ }
22
+
23
+ get isConnected(): boolean {
24
+ return Boolean(this.stargateClient);
25
+ }
26
+
27
+ async connect(
28
+ wallet: LocalWallet,
29
+ ): Promise<void> {
30
+ if (wallet?.offlineSigner === undefined) {
31
+ throw new Error('Wallet signer not found');
32
+ }
33
+ this.wallet = wallet;
34
+ this.stargateClient = await SigningStargateClient.connectWithSigner(
35
+ this.restEndpoint,
36
+ wallet.offlineSigner,
37
+ { registry: new Registry(defaultRegistryTypes) },
38
+ );
39
+ }
40
+
41
+ getAccountBalances(): Promise<readonly Coin[]> {
42
+ if (!this.stargateClient || this.wallet?.address === undefined) {
43
+ throw new Error('stargateClient not initialized');
44
+ }
45
+ return this.stargateClient.getAllBalances(this.wallet.address);
46
+ }
47
+
48
+ getAccountBalance(denom: string): Promise<Coin> {
49
+ if (!this.stargateClient || this.wallet?.address === undefined) {
50
+ throw new Error('stargateClient not initialized');
51
+ }
52
+ return this.stargateClient.getBalance(this.wallet.address, denom);
53
+ }
54
+
55
+ async send(
56
+ messages: EncodeObject[],
57
+ gasPrice: GasPrice = GasPrice.fromString('0.025uusdc'),
58
+ memo?: string,
59
+ ): Promise<DeliverTxResponse> {
60
+ if (!this.stargateClient) {
61
+ throw new Error('NobleClient stargateClient not initialized');
62
+ }
63
+ if (this.wallet?.address === undefined) {
64
+ throw new Error('NobleClient wallet not initialized');
65
+ }
66
+ // Simulate to get the gas estimate
67
+ const fee = await this.simulateTransaction(messages, gasPrice, memo);
68
+
69
+ // Sign and broadcast the transaction
70
+ return this.stargateClient.signAndBroadcast(
71
+ this.wallet.address,
72
+ messages,
73
+ fee,
74
+ memo ?? '',
75
+ );
76
+ }
77
+
78
+ async simulateTransaction(
79
+ messages: readonly EncodeObject[],
80
+ gasPrice: GasPrice = GasPrice.fromString('0.025uusdc'),
81
+ memo?: string,
82
+ ): Promise<StdFee> {
83
+ if (!this.stargateClient) {
84
+ throw new Error('NobleClient stargateClient not initialized');
85
+ }
86
+ if (this.wallet?.address === undefined) {
87
+ throw new Error('NobleClient wallet not initialized');
88
+ }
89
+ // Get simulated response
90
+ const gasEstimate = await this.stargateClient.simulate(
91
+ this.wallet?.address,
92
+ messages,
93
+ memo,
94
+ );
95
+
96
+ // Calculate and return the fee
97
+ return calculateFee(Math.floor(gasEstimate * GAS_MULTIPLIER), gasPrice);
98
+ }
99
+ }
package/src/index.ts CHANGED
@@ -9,6 +9,7 @@ export * as onboarding from './lib/onboarding';
9
9
  export { default as LocalWallet } from './clients/modules/local-wallet';
10
10
  export { SubaccountInfo as SubaccountClient } from './clients/subaccount';
11
11
  export { CompositeClient } from './clients/composite-client';
12
+ export { NobleClient } from './clients/noble-client';
12
13
  export { IndexerClient } from './clients/indexer-client';
13
14
  export { ValidatorClient } from './clients/validator-client';
14
15
  export { FaucetClient } from './clients/faucet-client';
@@ -2,6 +2,7 @@ import { StdFee } from '@cosmjs/stargate';
2
2
 
3
3
  // Bech32 Prefix
4
4
  export const BECH32_PREFIX = 'dydx';
5
+ export const NOBLE_BECH32_PREFIX = 'noble';
5
6
 
6
7
  // Broadcast Defaults
7
8
  export const BROADCAST_POLL_INTERVAL_MS: number = 300;