@dhedge/v2-sdk 2.2.0 → 2.2.2

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": "@dhedge/v2-sdk",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "license": "MIT",
5
5
  "description": "🛠 An SDK for building applications on top of dHEDGE V2",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,191 @@
1
+ [
2
+ {
3
+ "inputs": [
4
+ {
5
+ "components": [
6
+ {
7
+ "internalType": "AccountId",
8
+ "name": "account",
9
+ "type": "uint256"
10
+ },
11
+ {
12
+ "internalType": "ReserveKey",
13
+ "name": "key",
14
+ "type": "uint248"
15
+ },
16
+ {
17
+ "internalType": "address",
18
+ "name": "receiver",
19
+ "type": "address"
20
+ },
21
+ {
22
+ "internalType": "uint256",
23
+ "name": "assets",
24
+ "type": "uint256"
25
+ },
26
+ {
27
+ "internalType": "bytes",
28
+ "name": "extraData",
29
+ "type": "bytes"
30
+ }
31
+ ],
32
+ "internalType": "struct BorrowParams",
33
+ "name": "params",
34
+ "type": "tuple"
35
+ }
36
+ ],
37
+ "name": "borrow",
38
+ "outputs": [
39
+ {
40
+ "internalType": "uint256",
41
+ "name": "debtShares",
42
+ "type": "uint256"
43
+ }
44
+ ],
45
+ "stateMutability": "nonpayable",
46
+ "type": "function"
47
+ },
48
+ {
49
+ "inputs": [
50
+ {
51
+ "components": [
52
+ {
53
+ "internalType": "AccountId",
54
+ "name": "account",
55
+ "type": "uint256"
56
+ },
57
+ {
58
+ "internalType": "ReserveKey",
59
+ "name": "key",
60
+ "type": "uint248"
61
+ },
62
+ {
63
+ "internalType": "enum TokenType",
64
+ "name": "withCollateralType",
65
+ "type": "uint8"
66
+ },
67
+ {
68
+ "internalType": "uint256",
69
+ "name": "assets",
70
+ "type": "uint256"
71
+ },
72
+ {
73
+ "internalType": "uint256",
74
+ "name": "shares",
75
+ "type": "uint256"
76
+ },
77
+ {
78
+ "internalType": "bytes",
79
+ "name": "extraData",
80
+ "type": "bytes"
81
+ }
82
+ ],
83
+ "internalType": "struct RepayParams",
84
+ "name": "params",
85
+ "type": "tuple"
86
+ }
87
+ ],
88
+ "name": "repay",
89
+ "outputs": [
90
+ {
91
+ "internalType": "uint256",
92
+ "name": "assetsRepaid",
93
+ "type": "uint256"
94
+ }
95
+ ],
96
+ "stateMutability": "nonpayable",
97
+ "type": "function"
98
+ },
99
+ {
100
+ "inputs": [
101
+ {
102
+ "components": [
103
+ {
104
+ "internalType": "AccountId",
105
+ "name": "account",
106
+ "type": "uint256"
107
+ },
108
+ {
109
+ "internalType": "uint256",
110
+ "name": "tokenId",
111
+ "type": "uint256"
112
+ },
113
+ {
114
+ "internalType": "uint256",
115
+ "name": "assets",
116
+ "type": "uint256"
117
+ },
118
+ {
119
+ "internalType": "bytes",
120
+ "name": "extraData",
121
+ "type": "bytes"
122
+ }
123
+ ],
124
+ "internalType": "struct SupplyParams",
125
+ "name": "params",
126
+ "type": "tuple"
127
+ }
128
+ ],
129
+ "name": "supply",
130
+ "outputs": [
131
+ {
132
+ "internalType": "uint256",
133
+ "name": "shares",
134
+ "type": "uint256"
135
+ }
136
+ ],
137
+ "stateMutability": "nonpayable",
138
+ "type": "function"
139
+ },
140
+ {
141
+ "inputs": [
142
+ {
143
+ "components": [
144
+ {
145
+ "internalType": "AccountId",
146
+ "name": "account",
147
+ "type": "uint256"
148
+ },
149
+ {
150
+ "internalType": "uint256",
151
+ "name": "tokenId",
152
+ "type": "uint256"
153
+ },
154
+ {
155
+ "internalType": "address",
156
+ "name": "receiver",
157
+ "type": "address"
158
+ },
159
+ {
160
+ "internalType": "uint256",
161
+ "name": "assets",
162
+ "type": "uint256"
163
+ },
164
+ {
165
+ "internalType": "uint256",
166
+ "name": "shares",
167
+ "type": "uint256"
168
+ },
169
+ {
170
+ "internalType": "bytes",
171
+ "name": "extraData",
172
+ "type": "bytes"
173
+ }
174
+ ],
175
+ "internalType": "struct WithdrawParams",
176
+ "name": "params",
177
+ "type": "tuple"
178
+ }
179
+ ],
180
+ "name": "withdraw",
181
+ "outputs": [
182
+ {
183
+ "internalType": "uint256",
184
+ "name": "assets",
185
+ "type": "uint256"
186
+ }
187
+ ],
188
+ "stateMutability": "nonpayable",
189
+ "type": "function"
190
+ }
191
+ ]
@@ -0,0 +1,30 @@
1
+ [
2
+ {
3
+ "inputs": [
4
+ { "name": "tokenIn", "type": "address" },
5
+ { "name": "amountIn", "type": "uint256" },
6
+ { "name": "tokenOut", "type": "address" },
7
+ { "name": "amountOutMinimum", "type": "uint256" },
8
+ { "name": "attestationSignature", "type": "bytes" },
9
+ {
10
+ "components": [
11
+ { "name": "chainId", "type": "uint256" },
12
+ { "name": "attestationId", "type": "uint256" },
13
+ { "name": "userId", "type": "bytes32" },
14
+ { "name": "asset", "type": "address" },
15
+ { "name": "price", "type": "uint256" },
16
+ { "name": "quantity", "type": "uint256" },
17
+ { "name": "expiration", "type": "uint256" },
18
+ { "name": "side", "type": "uint8" },
19
+ { "name": "additionalData", "type": "bytes32" }
20
+ ],
21
+ "name": "quote",
22
+ "type": "tuple"
23
+ }
24
+ ],
25
+ "name": "swapExactInWithAttestation",
26
+ "outputs": [],
27
+ "stateMutability": "nonpayable",
28
+ "type": "function"
29
+ }
30
+ ]
package/src/config.ts CHANGED
@@ -61,7 +61,8 @@ export const routerAddress: AddressDappNetworkMap = {
61
61
  [Dapp.TOROS]: "0xA5679C4272A056Bb83f039961fae7D99C48529F5",
62
62
  [Dapp.ODOS]: "0x0D05a7D3448512B78fa8A9e46c4872C88C4a0D05",
63
63
  [Dapp.PENDLE]: "0x888888888889758F76e7103c6CbF23ABbF58F946",
64
- [Dapp.KYBERSWAP]: "0x6131B5fae19EA4f9D964eAc0408E4408b66337b5"
64
+ [Dapp.KYBERSWAP]: "0x6131B5fae19EA4f9D964eAc0408E4408b66337b5",
65
+ [Dapp.DYTM]: "0x0fF1CEE337d7af25eEF4c1a7A2CaF83f98d80001"
65
66
  },
66
67
  [Network.BASE]: {
67
68
  [Dapp.ONEINCH]: "0x111111125421ca6dc452d289314280a0f8842a65",
@@ -77,7 +78,9 @@ export const routerAddress: AddressDappNetworkMap = {
77
78
  [Dapp.ODOS]: "0x0D05a7D3448512B78fa8A9e46c4872C88C4a0D05",
78
79
  [Dapp.PENDLE]: "0x888888888889758F76e7103c6CbF23ABbF58F946",
79
80
  [Dapp.ONEINCH]: "0x111111125421ca6dc452d289314280a0f8842a65",
80
- [Dapp.KYBERSWAP]: "0x6131B5fae19EA4f9D964eAc0408E4408b66337b5"
81
+ [Dapp.KYBERSWAP]: "0x6131B5fae19EA4f9D964eAc0408E4408b66337b5",
82
+ [Dapp.ONDO]: "0xde41399145F23936b03dD1474eC16c1519c0DC2a",
83
+ [Dapp.DYTM]: "0x0ff1ce991105DD1a8e26bC8a974Fd0154398Df0f"
81
84
  },
82
85
  [Network.PLASMA]: {
83
86
  [Dapp.AAVEV3]: "0x925a2A7214Ed92428B5b1B090F80b25700095e12",
@@ -275,3 +278,19 @@ export const OdosSwapFeeRecipient = {
275
278
  [Network.PLASMA]: "",
276
279
  [Network.HYPERLIQUID]: ""
277
280
  };
281
+
282
+ export const dytmContractAddresses: Readonly<Partial<
283
+ Record<
284
+ Network,
285
+ {
286
+ Periphery?: string;
287
+ }
288
+ >
289
+ >> = {
290
+ [Network.ARBITRUM]: {
291
+ Periphery: "0x1FBe7Bb394BE505C08e9ee419Dd166c71004e51B"
292
+ },
293
+ [Network.ETHEREUM]: {
294
+ Periphery: "0xbc632a1A4bD878D7e7D14ea6CfE0DDe3Fc291D1a"
295
+ }
296
+ };
@@ -77,7 +77,17 @@ import {
77
77
  } from "../services/pancake/staking";
78
78
  import { getOdosSwapTxData } from "../services/odos";
79
79
  import { getPendleMintTxData, getPendleSwapTxData } from "../services/pendle";
80
- import { getCompleteWithdrawalTxData } from "../services/toros/completeWithdrawal";
80
+ import {
81
+ getCompleteWithdrawalTxData,
82
+ TrackedAsset
83
+ } from "../services/toros/completeWithdrawal";
84
+ import {
85
+ getDytmBorrowTxData,
86
+ getDytmDepositTxData,
87
+ getDytmRepayTxData,
88
+ getDytmWithdrawTxData
89
+ } from "../services/dytm";
90
+
81
91
  import {
82
92
  getCreateLimitOrderTxData,
83
93
  getModifyLimitOrderTxData,
@@ -87,6 +97,7 @@ import {
87
97
  } from "../services/toros/limitOrder";
88
98
  import { getKyberSwapTxData } from "../services/kyberSwap";
89
99
  import { getCowSwapTxData } from "../services/cowSwap";
100
+ import { getOndoSwapTxData } from "../services/ondo";
90
101
  import {
91
102
  getClosePositionHyperliquidTxData,
92
103
  getDepositHyperliquidTxData,
@@ -416,13 +427,13 @@ export class Pool {
416
427
  );
417
428
  break;
418
429
  case Dapp.TOROS:
419
- swapTxData = await getEasySwapperTxData(
430
+ ({ swapTxData, minAmountOut } = await getEasySwapperTxData(
420
431
  this,
421
432
  assetFrom,
422
433
  assetTo,
423
434
  ethers.BigNumber.from(amountIn),
424
435
  slippage
425
- );
436
+ ));
426
437
  break;
427
438
  case Dapp.ODOS:
428
439
  ({ swapTxData, minAmountOut } = await getOdosSwapTxData(
@@ -451,6 +462,15 @@ export class Pool {
451
462
  slippage
452
463
  ));
453
464
  break;
465
+ case Dapp.ONDO:
466
+ ({ swapTxData, minAmountOut } = await getOndoSwapTxData(
467
+ this,
468
+ assetFrom,
469
+ assetTo,
470
+ amountIn.toString(),
471
+ slippage
472
+ ));
473
+ break;
454
474
  case Dapp.COWSWAP: {
455
475
  const cowSwapEstimateGas = isSdkOptionsBoolean(sdkOptions)
456
476
  ? sdkOptions
@@ -786,13 +806,18 @@ export class Pool {
786
806
  estimateGas: false
787
807
  }
788
808
  ): Promise<any> {
789
- const iLendingPool = new ethers.utils.Interface(ILendingPool.abi);
790
- const depositTxData = iLendingPool.encodeFunctionData(Transaction.DEPOSIT, [
791
- asset,
792
- amount,
793
- this.address,
794
- referralCode
795
- ]);
809
+ let depositTxData: string;
810
+ if (dapp === Dapp.DYTM) {
811
+ depositTxData = getDytmDepositTxData(this, asset, amount);
812
+ } else {
813
+ const iLendingPool = new ethers.utils.Interface(ILendingPool.abi);
814
+ depositTxData = iLendingPool.encodeFunctionData(Transaction.DEPOSIT, [
815
+ asset,
816
+ amount,
817
+ this.address,
818
+ referralCode
819
+ ]);
820
+ }
796
821
 
797
822
  const tx = await getPoolTxOrGasEstimate(
798
823
  this,
@@ -853,11 +878,17 @@ export class Pool {
853
878
  estimateGas: false
854
879
  }
855
880
  ): Promise<any> {
856
- const iLendingPool = new ethers.utils.Interface(ILendingPool.abi);
857
- const withdrawTxData = iLendingPool.encodeFunctionData(
858
- Transaction.WITHDRAW,
859
- [asset, amount, this.address]
860
- );
881
+ let withdrawTxData: string;
882
+ if (dapp === Dapp.DYTM) {
883
+ withdrawTxData = await getDytmWithdrawTxData(this, asset, amount);
884
+ } else {
885
+ const iLendingPool = new ethers.utils.Interface(ILendingPool.abi);
886
+ withdrawTxData = iLendingPool.encodeFunctionData(Transaction.WITHDRAW, [
887
+ asset,
888
+ amount,
889
+ this.address
890
+ ]);
891
+ }
861
892
 
862
893
  const tx = await getPoolTxOrGasEstimate(
863
894
  this,
@@ -920,14 +951,19 @@ export class Pool {
920
951
  estimateGas: false
921
952
  }
922
953
  ): Promise<any> {
923
- const iLendingPool = new ethers.utils.Interface(ILendingPool.abi);
924
- const borrowTxData = iLendingPool.encodeFunctionData(Transaction.BORROW, [
925
- asset,
926
- amount,
927
- 2,
928
- referralCode,
929
- this.address
930
- ]);
954
+ let borrowTxData: string;
955
+ if (dapp === Dapp.DYTM) {
956
+ borrowTxData = getDytmBorrowTxData(this, asset, amount);
957
+ } else {
958
+ const iLendingPool = new ethers.utils.Interface(ILendingPool.abi);
959
+ borrowTxData = iLendingPool.encodeFunctionData(Transaction.BORROW, [
960
+ asset,
961
+ amount,
962
+ 2,
963
+ referralCode,
964
+ this.address
965
+ ]);
966
+ }
931
967
  const tx = await getPoolTxOrGasEstimate(
932
968
  this,
933
969
  [routerAddress[this.network][dapp], borrowTxData, options],
@@ -954,13 +990,18 @@ export class Pool {
954
990
  estimateGas: false
955
991
  }
956
992
  ): Promise<any> {
957
- const iLendingPool = new ethers.utils.Interface(ILendingPool.abi);
958
- const repayTxData = iLendingPool.encodeFunctionData(Transaction.REPAY, [
959
- asset,
960
- amount,
961
- 2,
962
- this.address
963
- ]);
993
+ let repayTxData: string;
994
+ if (dapp === Dapp.DYTM) {
995
+ repayTxData = await getDytmRepayTxData(this, asset, amount);
996
+ } else {
997
+ const iLendingPool = new ethers.utils.Interface(ILendingPool.abi);
998
+ repayTxData = iLendingPool.encodeFunctionData(Transaction.REPAY, [
999
+ asset,
1000
+ amount,
1001
+ 2,
1002
+ this.address
1003
+ ]);
1004
+ }
964
1005
  const tx = await getPoolTxOrGasEstimate(
965
1006
  this,
966
1007
  [routerAddress[this.network][dapp], repayTxData, options],
@@ -1953,6 +1994,7 @@ export class Pool {
1953
1994
  * @param {number} slippage Slippage tolerance in %
1954
1995
  * @param {any} options Transaction options
1955
1996
  * @param {SDKOptions} sdkOptions SDK options including estimateGas
1997
+ * @param {any} trackedAssets Tracked assets information (only for tx data generation)
1956
1998
  * @returns {Promise<any>} Transaction
1957
1999
  */
1958
2000
  async completeTorosWithdrawal(
@@ -1961,13 +2003,15 @@ export class Pool {
1961
2003
  options: any = null,
1962
2004
  sdkOptions: SDKOptions = {
1963
2005
  estimateGas: false
1964
- }
2006
+ },
2007
+ trackedAssets: TrackedAsset[] = []
1965
2008
  ): Promise<any> {
1966
2009
  const txData = await getCompleteWithdrawalTxData(
1967
2010
  this,
1968
2011
  destinationToken,
1969
2012
  slippage * 100,
1970
- false
2013
+ false,
2014
+ trackedAssets
1971
2015
  );
1972
2016
  const tx = await getPoolTxOrGasEstimate(
1973
2017
  this,
@@ -0,0 +1,77 @@
1
+ import { ethers } from "ethers";
2
+ import IOffice from "../../abi/dytm/IOffice.json";
3
+ import { Pool } from "../../entities";
4
+
5
+ const iOffice = new ethers.utils.Interface(IOffice);
6
+
7
+ export function toDebtId(key: ethers.BigNumber): ethers.BigNumber {
8
+ const tokenTypeBits = ethers.BigNumber.from(3).shl(248);
9
+ return tokenTypeBits.or(key);
10
+ }
11
+
12
+ export const getDytmDepositTxData = (
13
+ pool: Pool,
14
+ asset: string,
15
+ amount: ethers.BigNumber | string
16
+ ): string => {
17
+ return iOffice.encodeFunctionData("supply", [
18
+ {
19
+ account: ethers.BigNumber.from(pool.address), // uint256 (converted address)
20
+ tokenId: asset,
21
+ assets: amount,
22
+ extraData: "0x"
23
+ }
24
+ ]);
25
+ };
26
+
27
+ export const getDytmBorrowTxData = (
28
+ pool: Pool,
29
+ asset: string,
30
+ amount: ethers.BigNumber | string
31
+ ): string => {
32
+ return iOffice.encodeFunctionData("borrow", [
33
+ {
34
+ account: ethers.BigNumber.from(pool.address), // uint256 (converted address)
35
+ key: asset,
36
+ receiver: pool.address,
37
+ assets: amount,
38
+ extraData: "0x"
39
+ }
40
+ ]);
41
+ };
42
+
43
+ export const getDytmRepayTxData = async (
44
+ pool: Pool,
45
+ asset: string,
46
+ amount: ethers.BigNumber | string
47
+ ): Promise<string> => {
48
+ return iOffice.encodeFunctionData("repay", [
49
+ {
50
+ account: ethers.BigNumber.from(pool.address), // uint256 (converted address)
51
+ key: asset,
52
+ withCollateralType: "0",
53
+ assets: amount,
54
+ shares: "0",
55
+ extraData: "0x"
56
+ }
57
+ ]);
58
+ };
59
+ export const getDytmWithdrawTxData = async (
60
+ pool: Pool,
61
+ asset: string,
62
+ amount: ethers.BigNumber | string
63
+ ): Promise<string> => {
64
+ const isMaxAmount = ethers.BigNumber.from(amount).eq(
65
+ ethers.constants.MaxUint256
66
+ );
67
+ return iOffice.encodeFunctionData("withdraw", [
68
+ {
69
+ account: ethers.BigNumber.from(pool.address), // uint256 (converted address)
70
+ tokenId: asset,
71
+ assets: isMaxAmount ? 0 : amount,
72
+ shares: isMaxAmount ? amount : 0,
73
+ receiver: pool.address,
74
+ extraData: "0x"
75
+ }
76
+ ]);
77
+ };
@@ -0,0 +1,142 @@
1
+ import axios from "axios";
2
+ import { ethers } from "ethers";
3
+ import BigNumber from "bignumber.js";
4
+ import { ApiError, Pool } from "../..";
5
+ import IOndoGMSwap from "../../abi/ondo/IOndoGMSwap.json";
6
+
7
+ const ONDO_API_URL = "https://api.gm.ondo.finance/v1/attestations";
8
+
9
+ // Ethereum mainnet USDC — Ondo is Ethereum-only
10
+ const USDC_ETHEREUM = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";
11
+
12
+ type OndoAttestation = {
13
+ attestationId: string;
14
+ userId: string;
15
+ chainId: string;
16
+ assetAddress: string;
17
+ side: string;
18
+ tokenAmount: string;
19
+ price: string;
20
+ expiration: number;
21
+ signature: string;
22
+ additionalData: string;
23
+ };
24
+
25
+ const iface = new ethers.utils.Interface(IOndoGMSwap);
26
+
27
+ // Ondo returns userId as a left-aligned 32-byte hex (significant bytes first,
28
+ // e.g. 0x474d...0000), so right-pad to preserve byte order and fix it to 32 bytes.
29
+ function toBytes32(s: string): string {
30
+ const hex = s.startsWith("0x") ? s.slice(2) : s;
31
+ return "0x" + hex.padEnd(64, "0").slice(0, 64);
32
+ }
33
+
34
+ async function postOndoAttestation(
35
+ symbol: string,
36
+ side: "buy" | "sell",
37
+ amount: { notionalValue: string } | { tokenAmount: string },
38
+ apiKey: string
39
+ ): Promise<OndoAttestation> {
40
+ try {
41
+ const { data } = await axios.post(
42
+ ONDO_API_URL,
43
+ { chainId: "ethereum-1", symbol, side, ...amount, duration: "short" },
44
+ { headers: { "x-api-key": apiKey } }
45
+ );
46
+ return data as OndoAttestation;
47
+ } catch (error) {
48
+ // Surface Ondo's structured error (e.g. ASSET_NOT_FOUND, MARKET_CLOSED)
49
+ if (axios.isAxiosError(error) && error.response) {
50
+ const { code, message } = error.response.data ?? {};
51
+ throw new ApiError(
52
+ `Ondo attestation request failed (${error.response.status}): ${code ??
53
+ ""} ${message ?? JSON.stringify(error.response.data)}`.trim()
54
+ );
55
+ }
56
+ throw new ApiError(
57
+ `Ondo attestation request failed: ${
58
+ error instanceof Error ? error.message : String(error)
59
+ }`
60
+ );
61
+ }
62
+ }
63
+
64
+ export async function getOndoSwapTxData(
65
+ pool: Pool,
66
+ tokenIn: string,
67
+ tokenOut: string,
68
+ amountIn: string,
69
+ slippage: number
70
+ ): Promise<{ swapTxData: string; minAmountOut: string }> {
71
+ const apiKey = process.env.ONDO_API_KEY;
72
+ if (!apiKey) throw new Error("ONDO_API_KEY environment variable is not set");
73
+
74
+ const amount = new BigNumber(amountIn);
75
+ const isMint = tokenIn.toLowerCase() === USDC_ETHEREUM.toLowerCase();
76
+ const gmToken = isMint ? tokenOut : tokenIn;
77
+ const slippageBps = Math.round(slippage * 100);
78
+
79
+ const tokenContract = new ethers.Contract(
80
+ gmToken,
81
+ ["function symbol() view returns (string)"],
82
+ pool.signer
83
+ );
84
+ const symbol: string = await tokenContract.symbol();
85
+
86
+ // For mint: notionalValue is the USD amount to subscribe. USDonManager values
87
+ // USDC at par (1 USDC = 1 USDon), so pass the USDC amount as-is (6dp -> decimal).
88
+ // Discounting by a USDC market price would strand the gap as USDon in the swapper
89
+ // (and revert via ExcessiveAmountIn once it exceeds the 1 USDC tolerance).
90
+ // For redeem: pass the GM token amount directly as tokenAmount.
91
+ const attestationAmount = isMint
92
+ ? { notionalValue: amount.div(1e6).toFixed(18) }
93
+ : { tokenAmount: amount.div(1e18).toFixed(18) };
94
+
95
+ const attestation = await postOndoAttestation(
96
+ symbol,
97
+ isMint ? "buy" : "sell",
98
+ attestationAmount,
99
+ apiKey
100
+ );
101
+
102
+ const signature =
103
+ "0x" + Buffer.from(attestation.signature, "base64").toString("hex");
104
+ const additionalData = ethers.utils.hexZeroPad(
105
+ "0x" + Buffer.from(attestation.additionalData, "base64").toString("hex"),
106
+ 32
107
+ );
108
+ const quantity = new BigNumber(attestation.tokenAmount);
109
+ const priceD18 = new BigNumber(attestation.price);
110
+
111
+ let minAmountOut: BigNumber;
112
+ if (isMint) {
113
+ minAmountOut = quantity.times(10000 - slippageBps).div(10000);
114
+ } else {
115
+ // USDC out (6 dec) = quantity_D18 * price_D18 / 1e30
116
+ const usdcOut = quantity.times(priceD18).div(1e30);
117
+ minAmountOut = usdcOut.times(10000 - slippageBps).div(10000);
118
+ }
119
+
120
+ const quote = {
121
+ chainId: 1,
122
+ attestationId: attestation.attestationId,
123
+ userId: toBytes32(attestation.userId),
124
+ asset: attestation.assetAddress,
125
+ price: priceD18.toFixed(0),
126
+ quantity: quantity.toFixed(0),
127
+ expiration: attestation.expiration,
128
+ side: Number(attestation.side),
129
+ additionalData
130
+ };
131
+
132
+ const swapTxData = iface.encodeFunctionData("swapExactInWithAttestation", [
133
+ tokenIn,
134
+ amount.toFixed(0),
135
+ tokenOut,
136
+ minAmountOut.toFixed(0),
137
+ signature,
138
+ quote
139
+ ]);
140
+
141
+ return { swapTxData, minAmountOut: minAmountOut.toFixed(0) };
142
+ }