@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 +1 -1
- package/readme.md +615 -0
- package/decibel_transactions.md +0 -1049
package/package.json
CHANGED
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
|