Skip to content

Commit 809b8bb

Browse files
authored
add dydx and fireblocks service (#84)
* add dydx and fireblocks service * add dydx service
1 parent bb787c1 commit 809b8bb

File tree

6 files changed

+276
-2
lines changed

6 files changed

+276
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Check out the [full documentation](https://docs.kiln.fi/v1/connect/overview).
1616
- SOL
1717
- XTZ
1818
- OSMO
19+
- DYDX
1920
- More protocol to come, don't hesitate to contact us ([email protected])
2021

2122
## Installation

src/integrations/fb_signer.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ type AssetId =
2929
| 'XTZ_TEST'
3030
| 'XTZ'
3131
| 'WND'
32-
| 'DOT';
32+
| 'DOT'
33+
| 'DV4TNT_TEST'
34+
| 'DYDX_DYDX'
35+
;
3336

3437
export class FbSigner {
3538
protected fireblocks: FireblocksSDK;

src/kiln.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { DotService } from './services/dot';
1010
import { XtzService } from './services/xtz';
1111
import { MaticService } from './services/matic';
1212
import { OsmoService } from './services/osmo';
13+
import { FireblocksService } from "./services/fireblocks";
14+
import { DydxService } from "./services/dydx";
1315

1416
type Config = {
1517
apiToken: string;
@@ -20,16 +22,18 @@ type Config = {
2022
export const KILN_VALIDATORS = v;
2123

2224
export class Kiln {
25+
fireblocks: FireblocksService;
26+
accounts: AccountService;
2327
eth: EthService;
2428
sol: SolService;
25-
accounts: AccountService;
2629
atom: AtomService;
2730
ada: AdaService;
2831
near: NearService;
2932
dot: DotService;
3033
xtz: XtzService;
3134
matic: MaticService;
3235
osmo: OsmoService;
36+
dydx: DydxService;
3337

3438
constructor({ testnet, apiToken, baseUrl }: Config) {
3539
api.defaults.headers.common.Authorization = `Bearer ${apiToken}`;
@@ -39,6 +43,7 @@ export class Kiln {
3943
? 'https://api.testnet.kiln.fi'
4044
: 'https://api.kiln.fi';
4145

46+
this.fireblocks = new FireblocksService({ testnet });
4247
this.accounts = new AccountService({ testnet });
4348
this.eth = new EthService({ testnet });
4449
this.sol = new SolService({ testnet });
@@ -49,5 +54,6 @@ export class Kiln {
4954
this.xtz = new XtzService({ testnet });
5055
this.matic = new MaticService({ testnet });
5156
this.osmo = new OsmoService({ testnet });
57+
this.dydx = new DydxService({ testnet });
5258
}
5359
}

src/services/dydx.ts

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import { Service } from './service';
2+
3+
import { ServiceProps } from '../types/service';
4+
import { Integration } from '../types/integrations';
5+
import api from '../api';
6+
import { DecodedTxRaw } from "@cosmjs/proto-signing";
7+
import { DydxSignedTx, DydxTxHash, DydxTxStatus, DydxTx } from "../types/dydx";
8+
9+
export class DydxService extends Service {
10+
11+
constructor({ testnet }: ServiceProps) {
12+
super({ testnet });
13+
}
14+
15+
/**
16+
* Convert DYDX to ADYDX
17+
* @param amountDydx
18+
*/
19+
dydxToAdydx(amountDydx: string): string {
20+
return (parseFloat(amountDydx) * 10 ** 18).toFixed();
21+
}
22+
23+
/**
24+
* Craft dydx staking transaction
25+
* @param accountId id of the kiln account to use for the stake transaction
26+
* @param pubkey wallet pubkey, this is different from the wallet address
27+
* @param validatorAddress validator address to delegate to
28+
* @param amountDydx how many tokens to stake in DYDX
29+
*/
30+
async craftStakeTx(
31+
accountId: string,
32+
pubkey: string,
33+
validatorAddress: string,
34+
amountDydx: number,
35+
): Promise<DydxTx> {
36+
try {
37+
const { data } = await api.post<DydxTx>(
38+
`/v1/dydx/transaction/stake`,
39+
{
40+
account_id: accountId,
41+
pubkey: pubkey,
42+
validator: validatorAddress,
43+
amount_adydx: this.dydxToAdydx(amountDydx.toString()),
44+
});
45+
return data;
46+
} catch (err: any) {
47+
throw new Error(err);
48+
}
49+
}
50+
51+
/**
52+
* Craft dydx withdraw rewards transaction
53+
* @param pubkey wallet pubkey, this is different from the wallet address
54+
* @param validatorAddress validator address to which the delegation has been made
55+
*/
56+
async craftWithdrawRewardsTx(
57+
pubkey: string,
58+
validatorAddress: string,
59+
): Promise<DydxTx> {
60+
try {
61+
const { data } = await api.post<DydxTx>(
62+
`/v1/dydx/transaction/withdraw-rewards`,
63+
{
64+
pubkey: pubkey,
65+
validator: validatorAddress,
66+
});
67+
return data;
68+
} catch (err: any) {
69+
throw new Error(err);
70+
}
71+
}
72+
73+
/**
74+
* Craft dydx unstaking transaction
75+
* @param pubkey wallet pubkey, this is different from the wallet address
76+
* @param validatorAddress validator address to which the delegation has been made
77+
* @param amountDydx how many tokens to undelegate in DYDX
78+
*/
79+
async craftUnstakeTx(
80+
pubkey: string,
81+
validatorAddress: string,
82+
amountDydx?: number,
83+
): Promise<DydxTx> {
84+
try {
85+
const { data } = await api.post<DydxTx>(
86+
`/v1/dydx/transaction/unstake`,
87+
{
88+
pubkey: pubkey,
89+
validator: validatorAddress,
90+
amount_adydx: amountDydx ? this.dydxToAdydx(amountDydx.toString()) : undefined,
91+
});
92+
return data;
93+
} catch (err: any) {
94+
throw new Error(err);
95+
}
96+
}
97+
98+
/**
99+
* Craft dydx redelegate transaction
100+
* @param accountId id of the kiln account to use for the new stake
101+
* @param pubkey wallet pubkey, this is different from the wallet address
102+
* @param validatorSourceAddress validator address of the current delegation
103+
* @param validatorDestinationAddress validator address to which the delegation will be moved
104+
* @param amountDydx how many tokens to redelegate in DYDX
105+
*/
106+
async craftRedelegateTx(
107+
accountId: string,
108+
pubkey: string,
109+
validatorSourceAddress: string,
110+
validatorDestinationAddress: string,
111+
amountDydx?: number,
112+
): Promise<DydxTx> {
113+
try {
114+
const { data } = await api.post<DydxTx>(
115+
`/v1/dydx/transaction/redelegate`,
116+
{
117+
account_id: accountId,
118+
pubkey: pubkey,
119+
validator_source: validatorSourceAddress,
120+
validator_destination: validatorDestinationAddress,
121+
amount_adydx: amountDydx ? this.dydxToAdydx(amountDydx.toString()) : undefined,
122+
});
123+
return data;
124+
} catch (err: any) {
125+
throw new Error(err);
126+
}
127+
}
128+
129+
/**
130+
* Sign transaction with given integration
131+
* @param integration custody solution to sign with
132+
* @param tx raw transaction
133+
* @param note note to identify the transaction in your custody solution
134+
*/
135+
async sign(integration: Integration, tx: DydxTx, note?: string): Promise<DydxSignedTx> {
136+
const payload = {
137+
rawMessageData: {
138+
messages: [
139+
{
140+
'content': tx.data.unsigned_tx_hash,
141+
},
142+
],
143+
},
144+
};
145+
const fbNote = note ? note : 'DYDX tx from @kilnfi/sdk';
146+
const signer = this.getFbSigner(integration);
147+
const fbTx = await signer.signWithFB(payload, this.testnet ? 'DV4TNT_TEST' : 'DYDX_DYDX', fbNote);
148+
const signature: string = fbTx.signedMessages![0].signature.fullSig;
149+
const { data } = await api.post<DydxSignedTx>(
150+
`/v1/dydx/transaction/prepare`,
151+
{
152+
pubkey: tx.data.pubkey,
153+
tx_body: tx.data.tx_body,
154+
tx_auth_info: tx.data.tx_auth_info,
155+
signature: signature,
156+
});
157+
data.data.fireblocks_tx = fbTx;
158+
return data;
159+
}
160+
161+
162+
/**
163+
* Broadcast transaction to the network
164+
* @param signedTx
165+
*/
166+
async broadcast(signedTx: DydxSignedTx): Promise<DydxTxHash> {
167+
try {
168+
const { data } = await api.post<DydxTxHash>(
169+
`/v1/dydx/transaction/broadcast`,
170+
{
171+
tx_serialized: signedTx.data.signed_tx_serialized,
172+
});
173+
return data;
174+
} catch (e: any) {
175+
throw new Error(e);
176+
}
177+
}
178+
179+
/**
180+
* Get transaction status
181+
* @param txHash
182+
*/
183+
async getTxStatus(txHash: string): Promise<DydxTxStatus> {
184+
try {
185+
const { data } = await api.get<DydxTxStatus>(
186+
`/v1/dydx/transaction/status?tx_hash=${txHash}`);
187+
return data;
188+
} catch (e: any) {
189+
throw new Error(e);
190+
}
191+
}
192+
193+
/**
194+
* Decode transaction
195+
* @param txSerialized transaction serialized
196+
*/
197+
async decodeTx(txSerialized: string): Promise<DecodedTxRaw> {
198+
try {
199+
const { data } = await api.get<DecodedTxRaw>(
200+
`/v1/dydx/transaction/decode?tx_serialized=${txSerialized}`);
201+
return data;
202+
} catch (error: any) {
203+
throw new Error(error);
204+
}
205+
}
206+
}

src/services/fireblocks.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Integration } from "../types/integrations";
2+
import { Service } from "./service";
3+
import { PublicKeyResponse } from "fireblocks-sdk";
4+
5+
export class FireblocksService extends Service {
6+
7+
/**
8+
* Get fireblocks wallet pubkey compressed
9+
* @param integration
10+
* @param assetId
11+
*/
12+
async getPubkey(integration: Integration, assetId: string): Promise<PublicKeyResponse> {
13+
const fbSdk = this.getFbSdk(integration);
14+
const data = await fbSdk.getPublicKeyInfoForVaultAccount({
15+
assetId: assetId,
16+
vaultAccountId: integration.vaultId,
17+
change: 0,
18+
addressIndex: 0,
19+
compressed: true,
20+
});
21+
return data;
22+
}
23+
}

src/types/dydx.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { IndexedTx, StdFee } from "@cosmjs/stargate";
2+
import { EncodeObject } from "@cosmjs/proto-signing";
3+
import { TransactionResponse } from "fireblocks-sdk";
4+
5+
export type DydxTx = {
6+
data: {
7+
unsigned_tx_hash: string;
8+
unsigned_tx_serialized: string;
9+
tx_body: string;
10+
tx_auth_info: string;
11+
pubkey: string;
12+
message: EncodeObject;
13+
fee: StdFee;
14+
}
15+
}
16+
17+
export type DydxSignedTx = {
18+
data: {
19+
fireblocks_tx: TransactionResponse;
20+
signed_tx_serialized: string;
21+
};
22+
};
23+
24+
export type DydxTxHash = {
25+
data: {
26+
tx_hash: string;
27+
};
28+
};
29+
30+
export type DydxTxStatus = {
31+
data: {
32+
status: 'success' | 'error',
33+
receipt: IndexedTx | null,
34+
}
35+
}

0 commit comments

Comments
 (0)