@dhedge/v2-sdk 2.1.6 → 2.1.8
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/dist/config.d.ts +1 -0
- package/dist/entities/pool.d.ts +11 -0
- package/dist/services/cowSwap/index.d.ts +10 -0
- package/dist/services/hyperliquid/index.d.ts +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/v2-sdk.cjs.development.js +393 -112
- package/dist/v2-sdk.cjs.development.js.map +1 -1
- package/dist/v2-sdk.cjs.production.min.js +1 -1
- package/dist/v2-sdk.cjs.production.min.js.map +1 -1
- package/dist/v2-sdk.esm.js +393 -112
- package/dist/v2-sdk.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/config.ts +6 -1
- package/src/entities/pool.ts +94 -4
- package/src/services/cowSwap/index.ts +281 -0
- package/src/services/hyperliquid/index.ts +8 -6
- package/src/test/constants.ts +1 -1
- package/src/test/cowswap.test.ts +81 -0
- package/src/types.ts +2 -1
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -35,7 +35,8 @@ export const routerAddress: AddressDappNetworkMap = {
|
|
|
35
35
|
[Dapp.ARRAKIS]: "0xc73fb100a995b33f9fa181d420f4c8d74506df66",
|
|
36
36
|
[Dapp.TOROS]: "0x45b90480D6F643dE2f128db091A357C3c90399f2",
|
|
37
37
|
[Dapp.ODOS]: "0x0D05a7D3448512B78fa8A9e46c4872C88C4a0D05",
|
|
38
|
-
[Dapp.KYBERSWAP]: "0x6131B5fae19EA4f9D964eAc0408E4408b66337b5"
|
|
38
|
+
[Dapp.KYBERSWAP]: "0x6131B5fae19EA4f9D964eAc0408E4408b66337b5",
|
|
39
|
+
[Dapp.COWSWAP]: "0x893223Be4e941fAA8A8DB93D0C097fCac37801ce"
|
|
39
40
|
},
|
|
40
41
|
[Network.OPTIMISM]: {
|
|
41
42
|
[Dapp.UNISWAPV3]: "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45",
|
|
@@ -88,6 +89,10 @@ export const routerAddress: AddressDappNetworkMap = {
|
|
|
88
89
|
}
|
|
89
90
|
};
|
|
90
91
|
|
|
92
|
+
export const gpv2SettlementAddress: Partial<Record<Network, string>> = {
|
|
93
|
+
[Network.POLYGON]: "0x9008D19f58AAbD9eD0D60971565AA8510560ab41"
|
|
94
|
+
};
|
|
95
|
+
|
|
91
96
|
export const dappFactoryAddress: AddressDappNetworkMap = {
|
|
92
97
|
[Network.POLYGON]: {
|
|
93
98
|
[Dapp.SUSHISWAP]: "0xc35DADB65012eC5796536bD9864eD8773aBc74C4",
|
package/src/entities/pool.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
MaxUint128,
|
|
19
19
|
nonfungiblePositionManagerAddress,
|
|
20
20
|
routerAddress,
|
|
21
|
+
gpv2SettlementAddress,
|
|
21
22
|
stakingAddress,
|
|
22
23
|
SYNTHETIX_TRACKING_CODE,
|
|
23
24
|
limitOrderAddress
|
|
@@ -69,7 +70,7 @@ import {
|
|
|
69
70
|
getExitVestTxData,
|
|
70
71
|
getRewardsTxDta
|
|
71
72
|
} from "../services/ramses/vesting";
|
|
72
|
-
import { getPoolTxOrGasEstimate } from "../utils/contract";
|
|
73
|
+
import { getPoolTxOrGasEstimate, isSdkOptionsBoolean } from "../utils/contract";
|
|
73
74
|
import {
|
|
74
75
|
cancelOrderViaFlatMoney,
|
|
75
76
|
mintUnitViaFlatMoney,
|
|
@@ -96,14 +97,18 @@ import {
|
|
|
96
97
|
hasActiveTorosLimitOrder
|
|
97
98
|
} from "../services/toros/limitOrder";
|
|
98
99
|
import { getKyberSwapTxData } from "../services/kyberSwap";
|
|
100
|
+
import { getCowSwapTxData } from "../services/cowSwap";
|
|
99
101
|
import {
|
|
100
102
|
getClosePositionHyperliquidTxData,
|
|
101
103
|
getDepositHyperliquidTxData,
|
|
102
104
|
getLimitOrderHyperliquidTxData,
|
|
103
|
-
|
|
105
|
+
getSendAssetHyperliquidTxData,
|
|
104
106
|
getWithdrawSpotHyperliquidTxData
|
|
105
107
|
} from "../services/hyperliquid";
|
|
106
|
-
import {
|
|
108
|
+
import {
|
|
109
|
+
CORE_WRITER_ADDRESS,
|
|
110
|
+
SPOT_DEX_ID
|
|
111
|
+
} from "../services/hyperliquid/constants";
|
|
107
112
|
|
|
108
113
|
export class Pool {
|
|
109
114
|
public readonly poolLogic: Contract;
|
|
@@ -471,6 +476,60 @@ export class Pool {
|
|
|
471
476
|
slippage
|
|
472
477
|
));
|
|
473
478
|
break;
|
|
479
|
+
case Dapp.COWSWAP: {
|
|
480
|
+
const cowSwapEstimateGas = isSdkOptionsBoolean(sdkOptions)
|
|
481
|
+
? sdkOptions
|
|
482
|
+
: sdkOptions.estimateGas;
|
|
483
|
+
if (
|
|
484
|
+
cowSwapEstimateGas ||
|
|
485
|
+
(!isSdkOptionsBoolean(sdkOptions) && sdkOptions.onlyGetTxData)
|
|
486
|
+
) {
|
|
487
|
+
throw new Error(
|
|
488
|
+
"CowSwap requires two sequential transactions (submit + preSign) and does not support estimateGas or onlyGetTxData"
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
const {
|
|
492
|
+
encodedTypedData,
|
|
493
|
+
preSignTxData,
|
|
494
|
+
minAmountOut: cowMinOut
|
|
495
|
+
} = await getCowSwapTxData(
|
|
496
|
+
this,
|
|
497
|
+
assetFrom,
|
|
498
|
+
assetTo,
|
|
499
|
+
amountIn,
|
|
500
|
+
slippage
|
|
501
|
+
);
|
|
502
|
+
// Tx 1: manager calls submit() directly on TypedStructuredDataValidator (not via pool.execTransaction)
|
|
503
|
+
const validatorContract = new Contract(
|
|
504
|
+
routerAddress[this.network][dapp] as string,
|
|
505
|
+
[
|
|
506
|
+
"function submit(address _poolLogic, uint8 _dataType, bytes memory _structuredData) external"
|
|
507
|
+
],
|
|
508
|
+
this.signer
|
|
509
|
+
);
|
|
510
|
+
const submitTx = await validatorContract.submit(
|
|
511
|
+
this.address,
|
|
512
|
+
1 /* COWSWAP_ORDER */,
|
|
513
|
+
encodedTypedData,
|
|
514
|
+
...(options ? [options] : [])
|
|
515
|
+
);
|
|
516
|
+
const submitReceipt = await submitTx.wait(3);
|
|
517
|
+
if (submitReceipt.status === 0) {
|
|
518
|
+
return submitReceipt;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Tx 2: pool.execTransaction → setPreSignature() on GPv2Settlement — guard checks stored digest, solvers execute
|
|
522
|
+
return getPoolTxOrGasEstimate(
|
|
523
|
+
this,
|
|
524
|
+
[
|
|
525
|
+
gpv2SettlementAddress[this.network],
|
|
526
|
+
preSignTxData,
|
|
527
|
+
options,
|
|
528
|
+
cowMinOut
|
|
529
|
+
],
|
|
530
|
+
sdkOptions
|
|
531
|
+
);
|
|
532
|
+
}
|
|
474
533
|
default:
|
|
475
534
|
const iUniswapV2Router = new ethers.utils.Interface(
|
|
476
535
|
IUniswapV2Router.abi
|
|
@@ -2220,7 +2279,38 @@ export class Pool {
|
|
|
2220
2279
|
this,
|
|
2221
2280
|
[
|
|
2222
2281
|
CORE_WRITER_ADDRESS,
|
|
2223
|
-
|
|
2282
|
+
getSendAssetHyperliquidTxData(dexId, SPOT_DEX_ID, this.address, amount),
|
|
2283
|
+
options
|
|
2284
|
+
],
|
|
2285
|
+
sdkOptions
|
|
2286
|
+
);
|
|
2287
|
+
return tx;
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
/** Move USDC from HyperCore spot wallet to a trading dex.
|
|
2291
|
+
*
|
|
2292
|
+
* @param {number} dexId Destination dex ID where USDC will be moved to
|
|
2293
|
+
* - 0: Core Perp dex (standard perps like BTC, ETH)
|
|
2294
|
+
* - 1: xyz HIP-3 dex (builder perps like TSLA, GOLD)
|
|
2295
|
+
* @param {BigNumber | string} amount USDC amount to transfer (6 decimals, e.g. "1000000" = 1 USDC)
|
|
2296
|
+
* @param {any} options Transaction options
|
|
2297
|
+
* @param {SDKOptions} sdkOptions SDK options including estimateGas
|
|
2298
|
+
* @returns {Promise<any>} Transaction
|
|
2299
|
+
*/
|
|
2300
|
+
|
|
2301
|
+
async spotToPerpHyperliquid(
|
|
2302
|
+
dexId: number,
|
|
2303
|
+
amount: BigNumber | string,
|
|
2304
|
+
options: any = null,
|
|
2305
|
+
sdkOptions: SDKOptions = {
|
|
2306
|
+
estimateGas: false
|
|
2307
|
+
}
|
|
2308
|
+
): Promise<any> {
|
|
2309
|
+
const tx = await getPoolTxOrGasEstimate(
|
|
2310
|
+
this,
|
|
2311
|
+
[
|
|
2312
|
+
CORE_WRITER_ADDRESS,
|
|
2313
|
+
getSendAssetHyperliquidTxData(SPOT_DEX_ID, dexId, this.address, amount),
|
|
2224
2314
|
options
|
|
2225
2315
|
],
|
|
2226
2316
|
sdkOptions
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import { ethers } from "ethers";
|
|
4
|
+
import { ApiError } from "../..";
|
|
5
|
+
import { networkChainIdMap, gpv2SettlementAddress } from "../../config";
|
|
6
|
+
import { Pool } from "../../entities";
|
|
7
|
+
import BN from "bignumber.js";
|
|
8
|
+
|
|
9
|
+
export const KIND_SELL = ethers.utils.keccak256(
|
|
10
|
+
ethers.utils.toUtf8Bytes("sell")
|
|
11
|
+
);
|
|
12
|
+
export const KIND_BUY = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("buy"));
|
|
13
|
+
export const BALANCE_ERC20 = ethers.utils.keccak256(
|
|
14
|
+
ethers.utils.toUtf8Bytes("erc20")
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
// Matches CowSwapOrderTypeHashLib.ORDER_TYPE_HASH exactly:
|
|
18
|
+
// kind/sellTokenBalance/buyTokenBalance are "string" in the type string
|
|
19
|
+
// but encoded as bytes32 values in abi.encode — must replicate this exactly
|
|
20
|
+
const ORDER_TYPE_HASH = ethers.utils.keccak256(
|
|
21
|
+
ethers.utils.toUtf8Bytes(
|
|
22
|
+
"Order(" +
|
|
23
|
+
"address sellToken," +
|
|
24
|
+
"address buyToken," +
|
|
25
|
+
"address receiver," +
|
|
26
|
+
"uint256 sellAmount," +
|
|
27
|
+
"uint256 buyAmount," +
|
|
28
|
+
"uint32 validTo," +
|
|
29
|
+
"bytes32 appData," +
|
|
30
|
+
"uint256 feeAmount," +
|
|
31
|
+
"string kind," +
|
|
32
|
+
"bool partiallyFillable," +
|
|
33
|
+
"string sellTokenBalance," +
|
|
34
|
+
"string buyTokenBalance" +
|
|
35
|
+
")"
|
|
36
|
+
)
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Matches CowSwapOrderTypeHashLib.EIP712_DOMAIN_TYPEHASH
|
|
40
|
+
const EIP712_DOMAIN_TYPEHASH = ethers.utils.keccak256(
|
|
41
|
+
ethers.utils.toUtf8Bytes(
|
|
42
|
+
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
|
|
43
|
+
)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const cowSwapApiNetworkMap: Record<string, string> = {
|
|
47
|
+
polygon: "polygon"
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const GPv2Settlement_ABI = [
|
|
51
|
+
"function setPreSignature(bytes calldata orderUid, bool signed) external"
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
// Replicates CowSwapOrderTypeHashLib.hashOrder
|
|
55
|
+
function hashOrder(order: {
|
|
56
|
+
sellToken: string;
|
|
57
|
+
buyToken: string;
|
|
58
|
+
receiver: string;
|
|
59
|
+
sellAmount: string;
|
|
60
|
+
buyAmount: string;
|
|
61
|
+
validTo: number;
|
|
62
|
+
appData: string;
|
|
63
|
+
feeAmount: string;
|
|
64
|
+
kind: string;
|
|
65
|
+
partiallyFillable: boolean;
|
|
66
|
+
sellTokenBalance: string;
|
|
67
|
+
buyTokenBalance: string;
|
|
68
|
+
}): string {
|
|
69
|
+
return ethers.utils.keccak256(
|
|
70
|
+
ethers.utils.defaultAbiCoder.encode(
|
|
71
|
+
[
|
|
72
|
+
"bytes32",
|
|
73
|
+
"address",
|
|
74
|
+
"address",
|
|
75
|
+
"address",
|
|
76
|
+
"uint256",
|
|
77
|
+
"uint256",
|
|
78
|
+
"uint32",
|
|
79
|
+
"bytes32",
|
|
80
|
+
"uint256",
|
|
81
|
+
"bytes32",
|
|
82
|
+
"bool",
|
|
83
|
+
"bytes32",
|
|
84
|
+
"bytes32"
|
|
85
|
+
],
|
|
86
|
+
[
|
|
87
|
+
ORDER_TYPE_HASH,
|
|
88
|
+
order.sellToken,
|
|
89
|
+
order.buyToken,
|
|
90
|
+
order.receiver,
|
|
91
|
+
order.sellAmount,
|
|
92
|
+
order.buyAmount,
|
|
93
|
+
order.validTo,
|
|
94
|
+
order.appData,
|
|
95
|
+
order.feeAmount,
|
|
96
|
+
order.kind,
|
|
97
|
+
order.partiallyFillable,
|
|
98
|
+
order.sellTokenBalance,
|
|
99
|
+
order.buyTokenBalance
|
|
100
|
+
]
|
|
101
|
+
)
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Replicates CowSwapOrderTypeHashLib.domainSeparator
|
|
106
|
+
function computeDomainSeparator(
|
|
107
|
+
name: string,
|
|
108
|
+
version: string,
|
|
109
|
+
chainId: number,
|
|
110
|
+
verifyingContract: string
|
|
111
|
+
): string {
|
|
112
|
+
return ethers.utils.keccak256(
|
|
113
|
+
ethers.utils.defaultAbiCoder.encode(
|
|
114
|
+
["bytes32", "bytes32", "bytes32", "uint256", "address"],
|
|
115
|
+
[
|
|
116
|
+
EIP712_DOMAIN_TYPEHASH,
|
|
117
|
+
ethers.utils.keccak256(ethers.utils.toUtf8Bytes(name)),
|
|
118
|
+
ethers.utils.keccak256(ethers.utils.toUtf8Bytes(version)),
|
|
119
|
+
chainId,
|
|
120
|
+
verifyingContract
|
|
121
|
+
]
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Replicates CowSwapOrderTypeHashLib.getDigest
|
|
127
|
+
function computeOrderDigest(domainSep: string, orderHash: string): string {
|
|
128
|
+
return ethers.utils.keccak256(
|
|
129
|
+
ethers.utils.solidityPack(
|
|
130
|
+
["bytes2", "bytes32", "bytes32"],
|
|
131
|
+
["0x1901", domainSep, orderHash]
|
|
132
|
+
)
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function getCowSwapTxData(
|
|
137
|
+
pool: Pool,
|
|
138
|
+
assetFrom: string,
|
|
139
|
+
assetTo: string,
|
|
140
|
+
amountIn: ethers.BigNumber | string,
|
|
141
|
+
slippage: number,
|
|
142
|
+
kind: "sell" | "buy" = "sell"
|
|
143
|
+
): Promise<{
|
|
144
|
+
encodedTypedData: string;
|
|
145
|
+
preSignTxData: string;
|
|
146
|
+
minAmountOut: string;
|
|
147
|
+
}> {
|
|
148
|
+
const chainId = networkChainIdMap[pool.network];
|
|
149
|
+
const network = cowSwapApiNetworkMap[pool.network];
|
|
150
|
+
if (!network) {
|
|
151
|
+
throw new Error(`CowSwap is not supported on network: ${pool.network}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const gpv2Settlement = gpv2SettlementAddress[pool.network];
|
|
155
|
+
if (!gpv2Settlement) {
|
|
156
|
+
throw new Error(
|
|
157
|
+
`GPv2Settlement address not configured for network: ${pool.network}`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const baseUrl = `https://api.cow.fi/${network}/api/v1`;
|
|
162
|
+
|
|
163
|
+
// 1. Get quote
|
|
164
|
+
let quoteResponse: any;
|
|
165
|
+
try {
|
|
166
|
+
const quoteResult = await axios.post(`${baseUrl}/quote`, {
|
|
167
|
+
sellToken: assetFrom,
|
|
168
|
+
buyToken: assetTo,
|
|
169
|
+
...(kind === "sell"
|
|
170
|
+
? { sellAmountBeforeFee: amountIn.toString() }
|
|
171
|
+
: { buyAmountAfterFee: amountIn.toString() }),
|
|
172
|
+
from: pool.address,
|
|
173
|
+
receiver: pool.address,
|
|
174
|
+
kind,
|
|
175
|
+
signingScheme: "presign",
|
|
176
|
+
partiallyFillable: false,
|
|
177
|
+
sellTokenBalance: "erc20",
|
|
178
|
+
buyTokenBalance: "erc20"
|
|
179
|
+
});
|
|
180
|
+
quoteResponse = quoteResult.data;
|
|
181
|
+
} catch (e) {
|
|
182
|
+
throw new ApiError("CowSwap quote request failed");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const { sellAmount, buyAmount, validTo } = quoteResponse.quote;
|
|
186
|
+
|
|
187
|
+
const buyAmountWithSlippage = ethers.BigNumber.from(
|
|
188
|
+
new BN(buyAmount.toString())
|
|
189
|
+
.times(new BN(1).minus(new BN(slippage).div(100)))
|
|
190
|
+
.toFixed(0, BN.ROUND_DOWN)
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const orderValues = {
|
|
194
|
+
sellToken: assetFrom,
|
|
195
|
+
buyToken: assetTo,
|
|
196
|
+
receiver: pool.address,
|
|
197
|
+
sellAmount,
|
|
198
|
+
buyAmount: buyAmountWithSlippage.toString(),
|
|
199
|
+
validTo,
|
|
200
|
+
appData: ethers.constants.HashZero,
|
|
201
|
+
feeAmount: "0",
|
|
202
|
+
kind: kind === "sell" ? KIND_SELL : KIND_BUY,
|
|
203
|
+
partiallyFillable: false,
|
|
204
|
+
sellTokenBalance: BALANCE_ERC20,
|
|
205
|
+
buyTokenBalance: BALANCE_ERC20
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// 2. Compute digest matching CowSwapOrderTypeHashLib.getDigest exactly
|
|
209
|
+
const domainSep = computeDomainSeparator(
|
|
210
|
+
"Gnosis Protocol",
|
|
211
|
+
"v2",
|
|
212
|
+
chainId,
|
|
213
|
+
gpv2Settlement
|
|
214
|
+
);
|
|
215
|
+
const orderHash = hashOrder(orderValues);
|
|
216
|
+
const orderDigest = computeOrderDigest(domainSep, orderHash);
|
|
217
|
+
|
|
218
|
+
// orderUid = abi.encodePacked(orderDigest, owner, validTo) — 32 + 20 + 4 = 56 bytes
|
|
219
|
+
const orderUid = ethers.utils.solidityPack(
|
|
220
|
+
["bytes32", "address", "uint32"],
|
|
221
|
+
[orderDigest, pool.address, validTo]
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// 3. Submit order to CowSwap API — solver waits for on-chain presign before executing
|
|
225
|
+
try {
|
|
226
|
+
await axios.post(`${baseUrl}/orders`, {
|
|
227
|
+
sellToken: assetFrom,
|
|
228
|
+
buyToken: assetTo,
|
|
229
|
+
receiver: pool.address,
|
|
230
|
+
sellAmount,
|
|
231
|
+
buyAmount: buyAmountWithSlippage.toString(),
|
|
232
|
+
validTo,
|
|
233
|
+
appData: ethers.constants.HashZero,
|
|
234
|
+
feeAmount: "0",
|
|
235
|
+
kind,
|
|
236
|
+
partiallyFillable: false,
|
|
237
|
+
signingScheme: "presign",
|
|
238
|
+
signature: pool.address,
|
|
239
|
+
sellTokenBalance: "erc20",
|
|
240
|
+
buyTokenBalance: "erc20",
|
|
241
|
+
from: pool.address,
|
|
242
|
+
quoteId: quoteResponse.id
|
|
243
|
+
});
|
|
244
|
+
} catch (e) {
|
|
245
|
+
throw new ApiError("CowSwap order submission failed");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// 4. Encode CowSwapTypedData for submit() on TypedStructuredDataValidator
|
|
249
|
+
const typedData = {
|
|
250
|
+
domain: {
|
|
251
|
+
name: "Gnosis Protocol",
|
|
252
|
+
version: "v2",
|
|
253
|
+
chainId,
|
|
254
|
+
verifyingContract: gpv2Settlement
|
|
255
|
+
},
|
|
256
|
+
order: orderValues
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const encodedTypedData = ethers.utils.defaultAbiCoder.encode(
|
|
260
|
+
[
|
|
261
|
+
"tuple(" +
|
|
262
|
+
"tuple(string name, string version, uint256 chainId, address verifyingContract) domain," +
|
|
263
|
+
"tuple(address sellToken, address buyToken, address receiver, uint256 sellAmount, uint256 buyAmount, uint32 validTo, bytes32 appData, uint256 feeAmount, bytes32 kind, bool partiallyFillable, bytes32 sellTokenBalance, bytes32 buyTokenBalance) order" +
|
|
264
|
+
")"
|
|
265
|
+
],
|
|
266
|
+
[typedData]
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// 5. Encode setPreSignature() — guard checks isValidatedHash(pool, orderDigest)
|
|
270
|
+
const settlementIface = new ethers.utils.Interface(GPv2Settlement_ABI);
|
|
271
|
+
const preSignTxData = settlementIface.encodeFunctionData("setPreSignature", [
|
|
272
|
+
orderUid,
|
|
273
|
+
true
|
|
274
|
+
]);
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
encodedTypedData,
|
|
278
|
+
preSignTxData,
|
|
279
|
+
minAmountOut: buyAmountWithSlippage.toString()
|
|
280
|
+
};
|
|
281
|
+
}
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
LIMIT_ORDER_ACTION,
|
|
7
7
|
LIMIT_ORDER_TIF_IOC,
|
|
8
8
|
SEND_ASSET_ACTION,
|
|
9
|
-
SPOT_DEX_ID,
|
|
10
9
|
SPOT_SEND_ACTION,
|
|
11
10
|
USDC_CORE_ADDRESS,
|
|
12
11
|
USDC_TOKEN_ID
|
|
@@ -50,12 +49,15 @@ export const getWithdrawSpotHyperliquidTxData = (
|
|
|
50
49
|
);
|
|
51
50
|
return coreWriter.encodeFunctionData("sendRawAction", [rawTXData]);
|
|
52
51
|
};
|
|
53
|
-
export const
|
|
54
|
-
|
|
52
|
+
export const getSendAssetHyperliquidTxData = (
|
|
53
|
+
sourceDex: number,
|
|
54
|
+
destinationDex: number,
|
|
55
55
|
receiver: string,
|
|
56
56
|
amount: ethers.BigNumber | string
|
|
57
57
|
): string => {
|
|
58
|
-
|
|
58
|
+
// Convert 6-decimal EVM USDC to 8-decimal HyperCore USDC (spot, main perp, and xyz)
|
|
59
|
+
// Transfer USDC between dexes (perp, spot, xyz)
|
|
60
|
+
const coreAmount = ethers.BigNumber.from(amount).mul(100);
|
|
59
61
|
//From Perp to Spot
|
|
60
62
|
const innerEncoded = ethers.utils.defaultAbiCoder.encode(
|
|
61
63
|
//destination, subAccount, sourceDex, destinationDex, token, amount
|
|
@@ -63,8 +65,8 @@ export const getPerpToSpotHyperliquidTxData = (
|
|
|
63
65
|
[
|
|
64
66
|
receiver,
|
|
65
67
|
ethers.constants.AddressZero,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
sourceDex,
|
|
69
|
+
destinationDex,
|
|
68
70
|
USDC_TOKEN_ID,
|
|
69
71
|
coreAmount
|
|
70
72
|
]
|
package/src/test/constants.ts
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
|
+
|
|
3
|
+
import { Dhedge, Pool } from "..";
|
|
4
|
+
|
|
5
|
+
import { Dapp, Network } from "../types";
|
|
6
|
+
import { CONTRACT_ADDRESS, TEST_POOL } from "./constants";
|
|
7
|
+
import { getTxOptions } from "./txOptions";
|
|
8
|
+
import { TestingRunParams, testingHelper } from "./utils/testingHelper";
|
|
9
|
+
import { balanceDelta } from "./utils/token";
|
|
10
|
+
|
|
11
|
+
const testCowswap = ({ wallet, network }: TestingRunParams) => {
|
|
12
|
+
const USDC = CONTRACT_ADDRESS[network].USDC;
|
|
13
|
+
const WETH = CONTRACT_ADDRESS[network].WETH;
|
|
14
|
+
|
|
15
|
+
let dhedge: Dhedge;
|
|
16
|
+
let pool: Pool;
|
|
17
|
+
jest.setTimeout(100000);
|
|
18
|
+
|
|
19
|
+
describe(`pool on ${network}`, () => {
|
|
20
|
+
beforeAll(async () => {
|
|
21
|
+
dhedge = new Dhedge(wallet, network);
|
|
22
|
+
pool = await dhedge.loadPool(TEST_POOL[network]);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// it("approves unlimited USDC on Cowswap", async () => {
|
|
26
|
+
// await pool.approveSpender(
|
|
27
|
+
// "0xC92E8bdf79f0507f65a392b0ab4667716BFE0110", //Vault relayer
|
|
28
|
+
// USDC,
|
|
29
|
+
// MAX_AMOUNT,
|
|
30
|
+
// await getTxOptions(network)
|
|
31
|
+
// );
|
|
32
|
+
// const usdcAllowanceDelta = await allowanceDelta(
|
|
33
|
+
// pool.address,
|
|
34
|
+
// USDC,
|
|
35
|
+
// "0xC92E8bdf79f0507f65a392b0ab4667716BFE0110", //Vault relayer
|
|
36
|
+
// pool.signer
|
|
37
|
+
// );
|
|
38
|
+
// await expect(usdcAllowanceDelta.gt(0).toBe(true));
|
|
39
|
+
// });
|
|
40
|
+
|
|
41
|
+
// it("gets gas estimation for 2 USDC into WETH on Cowswap", async () => {
|
|
42
|
+
// const gasEstimate = await pool.trade(
|
|
43
|
+
// Dapp.COWSWAP,
|
|
44
|
+
// USDC,
|
|
45
|
+
// WETH,
|
|
46
|
+
// "2000000",
|
|
47
|
+
// 1,
|
|
48
|
+
// await getTxOptions(network),
|
|
49
|
+
// true
|
|
50
|
+
// );
|
|
51
|
+
// console.log(
|
|
52
|
+
// "Gas estimate for trading 2 USDC to WETH on Cowswap:",
|
|
53
|
+
// gasEstimate
|
|
54
|
+
// );
|
|
55
|
+
// expect(gasEstimate.minAmountOut).not.toBeNull();
|
|
56
|
+
// });
|
|
57
|
+
|
|
58
|
+
it("trades 2 USDC into WETH on Cowswap", async () => {
|
|
59
|
+
await pool.trade(
|
|
60
|
+
Dapp.COWSWAP,
|
|
61
|
+
USDC,
|
|
62
|
+
WETH,
|
|
63
|
+
"2000000",
|
|
64
|
+
0.5,
|
|
65
|
+
await getTxOptions(network)
|
|
66
|
+
);
|
|
67
|
+
const wethBalanceDelta = await balanceDelta(
|
|
68
|
+
pool.address,
|
|
69
|
+
WETH,
|
|
70
|
+
pool.signer
|
|
71
|
+
);
|
|
72
|
+
expect(wethBalanceDelta.gt(0)).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
testingHelper({
|
|
78
|
+
network: Network.POLYGON,
|
|
79
|
+
testingRun: testCowswap,
|
|
80
|
+
onFork: false
|
|
81
|
+
});
|