@decibeltrade/sdk 0.1.3 → 0.1.4

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decibeltrade/sdk",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {
package/readme.md CHANGED
@@ -872,6 +872,621 @@ const bot = new TradingBot("your-private-key", "your-subaccount-address");
872
872
  bot.start();
873
873
  ```
874
874
 
875
+ ## Smart Contract Transactions
876
+
877
+ For developers who need direct access to Decibel's smart contracts, this section shows how to build and submit transactions directly using the Aptos SDK.
878
+
879
+ ### Core Transaction Infrastructure
880
+
881
+ #### Base Transaction Manager
882
+
883
+ ```typescript
884
+ import {
885
+ Account,
886
+ AccountAddress,
887
+ AccountAuthenticator,
888
+ Aptos,
889
+ AptosConfig,
890
+ CommittedTransactionResponse,
891
+ InputGenerateTransactionPayloadData,
892
+ MoveString,
893
+ Network,
894
+ PendingTransactionResponse,
895
+ SimpleTransaction,
896
+ createObjectAddress,
897
+ } from "@aptos-labs/ts-sdk";
898
+
899
+ class DecibelTransactionManager {
900
+ private aptos: Aptos;
901
+ private config: DecibelConfig;
902
+ private skipSimulate: boolean;
903
+ private noFeePayer: boolean;
904
+
905
+ constructor(
906
+ config: DecibelConfig,
907
+ private account: Account,
908
+ options?: {
909
+ skipSimulate?: boolean;
910
+ noFeePayer?: boolean;
911
+ nodeApiKey?: string;
912
+ },
913
+ ) {
914
+ this.config = config;
915
+ this.skipSimulate = options?.skipSimulate ?? false;
916
+ this.noFeePayer = options?.noFeePayer ?? false;
917
+
918
+ const aptosConfig = new AptosConfig({
919
+ network: config.network,
920
+ fullnode: config.fullnodeUrl,
921
+ clientConfig: { API_KEY: options?.nodeApiKey },
922
+ });
923
+ this.aptos = new Aptos(aptosConfig);
924
+ }
925
+
926
+ private async getSimulatedTransaction(
927
+ payload: InputGenerateTransactionPayloadData,
928
+ sender: AccountAddress,
929
+ ): Promise<SimpleTransaction> {
930
+ const transaction = await this.aptos.transaction.build.simple({
931
+ sender,
932
+ data: payload,
933
+ });
934
+
935
+ const [simulationResult] = await this.aptos.transaction.simulate.simple({
936
+ transaction,
937
+ options: {
938
+ estimateMaxGasAmount: true,
939
+ estimateGasUnitPrice: true,
940
+ },
941
+ });
942
+
943
+ if (!simulationResult?.max_gas_amount || !simulationResult?.gas_unit_price) {
944
+ throw new Error("Transaction simulation failed - no gas estimates returned");
945
+ }
946
+
947
+ return await this.aptos.transaction.build.simple({
948
+ sender,
949
+ data: payload,
950
+ options: {
951
+ maxGasAmount: Number(simulationResult.max_gas_amount),
952
+ gasUnitPrice: Number(simulationResult.gas_unit_price),
953
+ },
954
+ });
955
+ }
956
+
957
+ private async submitTransaction(
958
+ transaction: SimpleTransaction,
959
+ senderAuthenticator: AccountAuthenticator,
960
+ ): Promise<PendingTransactionResponse> {
961
+ if (this.noFeePayer) {
962
+ return await this.aptos.transaction.submit.simple({
963
+ transaction,
964
+ senderAuthenticator,
965
+ });
966
+ } else {
967
+ return await this.submitFeePaidTransaction(transaction, senderAuthenticator);
968
+ }
969
+ }
970
+
971
+ private async submitFeePaidTransaction(
972
+ transaction: SimpleTransaction,
973
+ senderAuthenticator: AccountAuthenticator,
974
+ ): Promise<PendingTransactionResponse> {
975
+ const signatureBcs = Array.from(senderAuthenticator.bcsToBytes());
976
+ const transactionBcs = Array.from(transaction.rawTransaction.bcsToBytes());
977
+
978
+ const response = await fetch(this.config.gasStationUrl + "/transactions", {
979
+ method: "POST",
980
+ headers: {
981
+ "Content-Type": "application/json",
982
+ },
983
+ body: JSON.stringify({
984
+ signature: signatureBcs,
985
+ transaction: transactionBcs,
986
+ }),
987
+ });
988
+
989
+ if (!response.ok) {
990
+ throw new Error(`Fee payer service error: ${response.status}`);
991
+ }
992
+
993
+ return (await response.json()) as PendingTransactionResponse;
994
+ }
995
+
996
+ async sendTransaction(
997
+ payload: InputGenerateTransactionPayloadData,
998
+ accountOverride?: Account,
999
+ ): Promise<CommittedTransactionResponse> {
1000
+ const signer = accountOverride ?? this.account;
1001
+ const sender = signer.accountAddress;
1002
+
1003
+ let transaction: SimpleTransaction;
1004
+
1005
+ if (!this.skipSimulate) {
1006
+ transaction = await this.getSimulatedTransaction(payload, sender);
1007
+ } else {
1008
+ const withFeePayer = !this.noFeePayer;
1009
+ transaction = await this.aptos.transaction.build.simple({
1010
+ sender,
1011
+ data: payload,
1012
+ withFeePayer,
1013
+ });
1014
+ }
1015
+
1016
+ const senderAuthenticator = this.aptos.transaction.sign({
1017
+ signer,
1018
+ transaction,
1019
+ });
1020
+
1021
+ const pendingTransaction = await this.submitTransaction(transaction, senderAuthenticator);
1022
+ return await this.aptos.waitForTransaction({
1023
+ transactionHash: pendingTransaction.hash,
1024
+ });
1025
+ }
1026
+ }
1027
+ ```
1028
+
1029
+ ### Utility Functions
1030
+
1031
+ ```typescript
1032
+ /**
1033
+ * Get market address from market name
1034
+ */
1035
+ function getMarketAddress(marketName: string, perpEngineGlobalAddr: string): AccountAddress {
1036
+ const marketNameBytes = new MoveString(marketName).bcsToBytes();
1037
+ return createObjectAddress(AccountAddress.fromString(perpEngineGlobalAddr), marketNameBytes);
1038
+ }
1039
+
1040
+ /**
1041
+ * Get primary subaccount address for a user account
1042
+ */
1043
+ function getPrimarySubaccountAddress(userAddress: AccountAddress): string {
1044
+ const seed = new TextEncoder().encode("decibel_dex_primary");
1045
+ return createObjectAddress(userAddress, seed).toString();
1046
+ }
1047
+
1048
+ /**
1049
+ * Extract order ID from transaction events
1050
+ */
1051
+ function extractOrderIdFromTransaction(
1052
+ txResponse: CommittedTransactionResponse,
1053
+ subaccountAddr: string,
1054
+ ): string | null {
1055
+ try {
1056
+ if ("events" in txResponse && Array.isArray(txResponse.events)) {
1057
+ for (const event of txResponse.events) {
1058
+ if (event.type.includes("::market_types::OrderEvent")) {
1059
+ const orderEvent = event.data as any;
1060
+ if (orderEvent.user === subaccountAddr) {
1061
+ return orderEvent.order_id;
1062
+ }
1063
+ }
1064
+ }
1065
+ }
1066
+ return null;
1067
+ } catch (error) {
1068
+ console.error("Error extracting order ID:", error);
1069
+ return null;
1070
+ }
1071
+ }
1072
+ ```
1073
+
1074
+ ### Account Management Transactions
1075
+
1076
+ #### Create Subaccount
1077
+
1078
+ ```typescript
1079
+ async function createSubaccount(
1080
+ transactionManager: DecibelTransactionManager,
1081
+ config: DecibelConfig,
1082
+ ): Promise<CommittedTransactionResponse> {
1083
+ return await transactionManager.sendTransaction({
1084
+ function: `${config.deployment.package}::dex_accounts::create_new_subaccount`,
1085
+ typeArguments: [],
1086
+ functionArguments: [],
1087
+ });
1088
+ }
1089
+ ```
1090
+
1091
+ #### Deposit Collateral
1092
+
1093
+ ```typescript
1094
+ async function depositCollateral(
1095
+ transactionManager: DecibelTransactionManager,
1096
+ config: DecibelConfig,
1097
+ amount: number,
1098
+ subaccountAddr: string,
1099
+ ): Promise<CommittedTransactionResponse> {
1100
+ return await transactionManager.sendTransaction({
1101
+ function: `${config.deployment.package}::dex_accounts::deposit_to_subaccount_at`,
1102
+ typeArguments: [],
1103
+ functionArguments: [subaccountAddr, config.deployment.usdc, amount],
1104
+ });
1105
+ }
1106
+ ```
1107
+
1108
+ #### Withdraw Collateral
1109
+
1110
+ ```typescript
1111
+ async function withdrawCollateral(
1112
+ transactionManager: DecibelTransactionManager,
1113
+ config: DecibelConfig,
1114
+ amount: number,
1115
+ subaccountAddr?: string,
1116
+ ): Promise<CommittedTransactionResponse> {
1117
+ const subaccount =
1118
+ subaccountAddr ?? getPrimarySubaccountAddress(transactionManager.account.accountAddress);
1119
+
1120
+ return await transactionManager.sendTransaction({
1121
+ function: `${config.deployment.package}::dex_accounts::withdraw_from_subaccount`,
1122
+ typeArguments: [],
1123
+ functionArguments: [subaccount, config.deployment.usdc, amount],
1124
+ });
1125
+ }
1126
+ ```
1127
+
1128
+ #### Configure Market Settings
1129
+
1130
+ ```typescript
1131
+ async function configureMarketSettings(
1132
+ transactionManager: DecibelTransactionManager,
1133
+ config: DecibelConfig,
1134
+ marketAddr: string,
1135
+ subaccountAddr: string,
1136
+ isCross: boolean,
1137
+ userLeverage: number,
1138
+ ): Promise<CommittedTransactionResponse> {
1139
+ return await transactionManager.sendTransaction({
1140
+ function: `${config.deployment.package}::dex_accounts::configure_user_settings_for_market`,
1141
+ typeArguments: [],
1142
+ functionArguments: [subaccountAddr, marketAddr, isCross, userLeverage],
1143
+ });
1144
+ }
1145
+ ```
1146
+
1147
+ ### Order Management Transactions
1148
+
1149
+ #### Place Order
1150
+
1151
+ ```typescript
1152
+ interface PlaceOrderResult {
1153
+ success: boolean;
1154
+ orderId?: string;
1155
+ transactionHash: string | null;
1156
+ error?: string;
1157
+ }
1158
+
1159
+ async function placeOrder(
1160
+ transactionManager: DecibelTransactionManager,
1161
+ config: DecibelConfig,
1162
+ params: {
1163
+ marketName: string;
1164
+ price: number;
1165
+ size: number;
1166
+ isBuy: boolean;
1167
+ timeInForce: number;
1168
+ isReduceOnly: boolean;
1169
+ clientOrderId?: number;
1170
+ stopPrice?: number;
1171
+ tpTriggerPrice?: number;
1172
+ tpLimitPrice?: number;
1173
+ slTriggerPrice?: number;
1174
+ slLimitPrice?: number;
1175
+ builderAddr?: string;
1176
+ builderFee?: number;
1177
+ subaccountAddr?: string;
1178
+ accountOverride?: Account;
1179
+ },
1180
+ ): Promise<PlaceOrderResult> {
1181
+ try {
1182
+ const marketAddr = getMarketAddress(params.marketName, config.deployment.perpEngineGlobal);
1183
+ const subaccountAddr =
1184
+ params.subaccountAddr ??
1185
+ getPrimarySubaccountAddress(transactionManager.account.accountAddress);
1186
+
1187
+ const txResponse = await transactionManager.sendTransaction(
1188
+ {
1189
+ function: `${config.deployment.package}::dex_accounts::place_order_to_subaccount`,
1190
+ typeArguments: [],
1191
+ functionArguments: [
1192
+ subaccountAddr,
1193
+ marketAddr.toString(),
1194
+ params.price,
1195
+ params.size,
1196
+ params.isBuy,
1197
+ params.timeInForce,
1198
+ params.isReduceOnly,
1199
+ params.clientOrderId,
1200
+ params.stopPrice,
1201
+ params.tpTriggerPrice,
1202
+ params.tpLimitPrice,
1203
+ params.slTriggerPrice,
1204
+ params.slLimitPrice,
1205
+ params.builderAddr,
1206
+ params.builderFee,
1207
+ ],
1208
+ },
1209
+ params.accountOverride,
1210
+ );
1211
+
1212
+ const orderId = extractOrderIdFromTransaction(txResponse, subaccountAddr);
1213
+
1214
+ return {
1215
+ success: true,
1216
+ orderId: orderId || undefined,
1217
+ transactionHash: txResponse.hash,
1218
+ };
1219
+ } catch (error) {
1220
+ return {
1221
+ success: false,
1222
+ transactionHash: null,
1223
+ error: error instanceof Error ? error.message : "Unknown error",
1224
+ };
1225
+ }
1226
+ }
1227
+ ```
1228
+
1229
+ #### Cancel Order
1230
+
1231
+ ```typescript
1232
+ async function cancelOrder(
1233
+ transactionManager: DecibelTransactionManager,
1234
+ config: DecibelConfig,
1235
+ params: {
1236
+ orderId: number;
1237
+ marketName?: string;
1238
+ marketAddr?: string;
1239
+ subaccountAddr?: string;
1240
+ accountOverride?: Account;
1241
+ },
1242
+ ): Promise<CommittedTransactionResponse> {
1243
+ const marketAddr =
1244
+ params.marketAddr ??
1245
+ (params.marketName
1246
+ ? getMarketAddress(params.marketName, config.deployment.perpEngineGlobal).toString()
1247
+ : "");
1248
+
1249
+ if (!marketAddr) {
1250
+ throw new Error("Either marketName or marketAddr must be provided");
1251
+ }
1252
+
1253
+ const subaccountAddr =
1254
+ params.subaccountAddr ?? getPrimarySubaccountAddress(transactionManager.account.accountAddress);
1255
+
1256
+ return await transactionManager.sendTransaction(
1257
+ {
1258
+ function: `${config.deployment.package}::dex_accounts::cancel_order_to_subaccount`,
1259
+ typeArguments: [],
1260
+ functionArguments: [subaccountAddr, params.orderId, marketAddr],
1261
+ },
1262
+ params.accountOverride,
1263
+ );
1264
+ }
1265
+ ```
1266
+
1267
+ #### Place TWAP Order
1268
+
1269
+ ```typescript
1270
+ async function placeTwapOrder(
1271
+ transactionManager: DecibelTransactionManager,
1272
+ config: DecibelConfig,
1273
+ params: {
1274
+ marketName: string;
1275
+ size: number;
1276
+ isBuy: boolean;
1277
+ isReduceOnly: boolean;
1278
+ twapFrequencySeconds: number;
1279
+ twapDurationSeconds: number;
1280
+ subaccountAddr?: string;
1281
+ accountOverride?: Account;
1282
+ },
1283
+ ): Promise<CommittedTransactionResponse> {
1284
+ const marketAddr = getMarketAddress(params.marketName, config.deployment.perpEngineGlobal);
1285
+ const subaccountAddr =
1286
+ params.subaccountAddr ?? getPrimarySubaccountAddress(transactionManager.account.accountAddress);
1287
+
1288
+ return await transactionManager.sendTransaction(
1289
+ {
1290
+ function: `${config.deployment.package}::dex_accounts::place_twap_order_to_subaccount`,
1291
+ typeArguments: [],
1292
+ functionArguments: [
1293
+ subaccountAddr,
1294
+ marketAddr.toString(),
1295
+ params.size,
1296
+ params.isBuy,
1297
+ params.isReduceOnly,
1298
+ params.twapFrequencySeconds,
1299
+ params.twapDurationSeconds,
1300
+ ],
1301
+ },
1302
+ params.accountOverride,
1303
+ );
1304
+ }
1305
+ ```
1306
+
1307
+ ### Position Management Transactions
1308
+
1309
+ #### Place TP/SL Order
1310
+
1311
+ ```typescript
1312
+ async function placeTpSlOrderForPosition(
1313
+ transactionManager: DecibelTransactionManager,
1314
+ config: DecibelConfig,
1315
+ params: {
1316
+ marketAddr: string;
1317
+ tpTriggerPrice?: number;
1318
+ tpLimitPrice?: number;
1319
+ tpSize?: number;
1320
+ slTriggerPrice?: number;
1321
+ slLimitPrice?: number;
1322
+ slSize?: number;
1323
+ subaccountAddr?: string;
1324
+ accountOverride?: Account;
1325
+ },
1326
+ ): Promise<CommittedTransactionResponse> {
1327
+ const subaccountAddr =
1328
+ params.subaccountAddr ?? getPrimarySubaccountAddress(transactionManager.account.accountAddress);
1329
+
1330
+ return await transactionManager.sendTransaction(
1331
+ {
1332
+ function: `${config.deployment.package}::dex_accounts::place_tp_sl_order_for_position`,
1333
+ typeArguments: [],
1334
+ functionArguments: [
1335
+ subaccountAddr,
1336
+ params.marketAddr,
1337
+ params.tpTriggerPrice,
1338
+ params.tpLimitPrice,
1339
+ params.tpSize,
1340
+ params.slTriggerPrice,
1341
+ params.slLimitPrice,
1342
+ params.slSize,
1343
+ ],
1344
+ },
1345
+ params.accountOverride,
1346
+ );
1347
+ }
1348
+ ```
1349
+
1350
+ ### Trading Delegation
1351
+
1352
+ #### Delegate Trading
1353
+
1354
+ ```typescript
1355
+ async function delegateTradingTo(
1356
+ transactionManager: DecibelTransactionManager,
1357
+ config: DecibelConfig,
1358
+ params: {
1359
+ accountToDelegateTo: string;
1360
+ subaccountAddr?: string;
1361
+ },
1362
+ ): Promise<CommittedTransactionResponse> {
1363
+ const subaccountAddr =
1364
+ params.subaccountAddr ?? getPrimarySubaccountAddress(transactionManager.account.accountAddress);
1365
+
1366
+ return await transactionManager.sendTransaction({
1367
+ function: `${config.deployment.package}::dex_accounts::delegate_trading_to_for_subaccount`,
1368
+ typeArguments: [],
1369
+ functionArguments: [subaccountAddr, params.accountToDelegateTo],
1370
+ });
1371
+ }
1372
+ ```
1373
+
1374
+ #### Revoke Delegation
1375
+
1376
+ ```typescript
1377
+ async function revokeDelegation(
1378
+ transactionManager: DecibelTransactionManager,
1379
+ config: DecibelConfig,
1380
+ params: {
1381
+ accountToRevoke: string;
1382
+ subaccountAddr?: string;
1383
+ },
1384
+ ): Promise<CommittedTransactionResponse> {
1385
+ const subaccountAddr =
1386
+ params.subaccountAddr ?? getPrimarySubaccountAddress(transactionManager.account.accountAddress);
1387
+
1388
+ return await transactionManager.sendTransaction({
1389
+ function: `${config.deployment.package}::dex_accounts::revoke_delegation`,
1390
+ typeArguments: [],
1391
+ functionArguments: [subaccountAddr, params.accountToRevoke],
1392
+ });
1393
+ }
1394
+ ```
1395
+
1396
+ ### Complete Working Example
1397
+
1398
+ ```typescript
1399
+ import { Account } from "@aptos-labs/ts-sdk";
1400
+
1401
+ async function basicTradingExample() {
1402
+ const privateKey = "your-private-key-here";
1403
+ const account = Account.fromPrivateKey({ privateKey });
1404
+ const transactionManager = new DecibelTransactionManager(NETNA_CONFIG, account, {
1405
+ skipSimulate: false,
1406
+ noFeePayer: false,
1407
+ });
1408
+
1409
+ try {
1410
+ // Create a subaccount
1411
+ console.log("Creating subaccount...");
1412
+ const createTx = await createSubaccount(transactionManager, NETNA_CONFIG);
1413
+ console.log("Subaccount created:", createTx.hash);
1414
+
1415
+ // Get the primary subaccount address
1416
+ const subaccountAddr = getPrimarySubaccountAddress(account.accountAddress);
1417
+ console.log("Primary subaccount address:", subaccountAddr);
1418
+
1419
+ // Deposit collateral (1000 USDC = 1000000000 if 6 decimals)
1420
+ console.log("Depositing collateral...");
1421
+ const depositTx = await depositCollateral(
1422
+ transactionManager,
1423
+ NETNA_CONFIG,
1424
+ 1000000000,
1425
+ subaccountAddr,
1426
+ );
1427
+ console.log("Deposit successful:", depositTx.hash);
1428
+
1429
+ // Configure market settings for BTC-USD
1430
+ const btcMarketAddr = getMarketAddress("BTC-USD", NETNA_CONFIG.deployment.perpEngineGlobal);
1431
+ console.log("Configuring market settings...");
1432
+ const configTx = await configureMarketSettings(
1433
+ transactionManager,
1434
+ NETNA_CONFIG,
1435
+ btcMarketAddr.toString(),
1436
+ subaccountAddr,
1437
+ true, // Use cross-margin
1438
+ 1000, // 10x leverage (1000 basis points)
1439
+ );
1440
+ console.log("Market configured:", configTx.hash);
1441
+
1442
+ // Place a limit buy order for 0.1 BTC at $45,000
1443
+ console.log("Placing buy order...");
1444
+ const orderResult = await placeOrder(transactionManager, NETNA_CONFIG, {
1445
+ marketName: "BTC-USD",
1446
+ price: 45000,
1447
+ size: 0.1,
1448
+ isBuy: true,
1449
+ timeInForce: TimeInForce.GoodTillCanceled,
1450
+ isReduceOnly: false,
1451
+ subaccountAddr,
1452
+ });
1453
+
1454
+ if (orderResult.success) {
1455
+ console.log("Order placed successfully!");
1456
+ console.log("Order ID:", orderResult.orderId);
1457
+ console.log("Transaction:", orderResult.transactionHash);
1458
+
1459
+ // Cancel the order
1460
+ if (orderResult.orderId) {
1461
+ console.log("Canceling order...");
1462
+ const cancelTx = await cancelOrder(transactionManager, NETNA_CONFIG, {
1463
+ orderId: parseInt(orderResult.orderId),
1464
+ marketName: "BTC-USD",
1465
+ subaccountAddr,
1466
+ });
1467
+ console.log("Order canceled:", cancelTx.hash);
1468
+ }
1469
+ } else {
1470
+ console.error("Order failed:", orderResult.error);
1471
+ }
1472
+ } catch (error) {
1473
+ console.error("Error in trading example:", error);
1474
+ }
1475
+ }
1476
+ ```
1477
+
1478
+ ### Best Practices
1479
+
1480
+ 1. **Error Handling**: Always wrap transaction calls in try-catch blocks and handle different types of errors appropriately.
1481
+
1482
+ 2. **Gas Management**: Use `skipSimulate: false` for gas estimation in production. Set appropriate gas limits for complex transactions.
1483
+
1484
+ 3. **Subaccount Management**: Use primary subaccount for simple use cases. Create separate subaccounts for different strategies. Always verify subaccount addresses before transactions.
1485
+
1486
+ 4. **Market Address Handling**: Cache market addresses to avoid repeated calculations. Verify market names are correct before generating addresses.
1487
+
1488
+ 5. **Order Management**: Store order IDs for later cancellation. Use client order IDs for easier tracking. Implement proper order status monitoring.
1489
+
875
1490
  ## Resources
876
1491
 
877
1492
  - 📚 [Full Documentation](https://docs.decibel.trade) - Complete API and transaction documentation