@cowprotocol/sdk-trading-solana 0.1.0
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/README.md +38 -0
- package/dist/index.d.mts +172 -0
- package/dist/index.d.ts +172 -0
- package/dist/index.js +367 -0
- package/dist/index.mjs +329 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img width="400" src="https://github.com/cowprotocol/cow-sdk/raw/main/docs/images/CoW.png" alt="CoW Protocol logo" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# Solana Trading SDK
|
|
6
|
+
|
|
7
|
+
## Test coverage
|
|
8
|
+
|
|
9
|
+
| Statements | Branches | Functions | Lines |
|
|
10
|
+
| --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
11
|
+
|  |  |  |  |
|
|
12
|
+
|
|
13
|
+
CoW Protocol's Solana settlement support: Jupiter-sourced quotes and on-chain `CreateOrder`
|
|
14
|
+
posting against the CoW Protocol Solana settlement program.
|
|
15
|
+
|
|
16
|
+
**Experimental.** The Solana settlement program this package targets isn't deployed anywhere
|
|
17
|
+
reachable yet. See `docs/superpowers/specs/2026-09-01-sdk-trading-solana-extraction-design.md` in
|
|
18
|
+
the `cowswap` repo for background.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { SolanaTradingSdk } from '@cowprotocol/sdk-trading-solana'
|
|
24
|
+
|
|
25
|
+
const sdk = new SolanaTradingSdk({ signAndSend })
|
|
26
|
+
const { quoteResults, postSwapOrderFromQuote } = await sdk.getQuote({
|
|
27
|
+
ownerAddress,
|
|
28
|
+
receiverAddress,
|
|
29
|
+
sellTokenAddress,
|
|
30
|
+
sellTokenDecimals,
|
|
31
|
+
buyTokenAddress,
|
|
32
|
+
buyTokenDecimals,
|
|
33
|
+
amount,
|
|
34
|
+
kind,
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const result = await postSwapOrderFromQuote()
|
|
38
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { PublicKey, PublicKeyInitData, TransactionInstruction } from '@solana/web3.js';
|
|
2
|
+
import { OrderKind } from '@cowprotocol/sdk-order-book';
|
|
3
|
+
import { CowEnv } from '@cowprotocol/sdk-config';
|
|
4
|
+
import { QuoteResults, SwapAdvancedSettings, SigningStepManager, OrderPostingResult, QuoteAndPost } from '@cowprotocol/sdk-trading';
|
|
5
|
+
|
|
6
|
+
interface JupiterOrderRequest {
|
|
7
|
+
inputMint: string;
|
|
8
|
+
outputMint: string;
|
|
9
|
+
amount: string;
|
|
10
|
+
swapMode: 'ExactIn' | 'ExactOut';
|
|
11
|
+
clientPlatform?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Fields of Jupiter's `/order` response this SDK actually reads. Jupiter's own swap transaction/execute
|
|
15
|
+
* flow (`transaction`, `requestId`) is deliberately not modeled here — quotes are sourced from Jupiter,
|
|
16
|
+
* but orders are posted through the CoW Protocol settlement program, never through Jupiter's `/execute`.
|
|
17
|
+
*/
|
|
18
|
+
interface JupiterOrderResponse {
|
|
19
|
+
inputMint: string;
|
|
20
|
+
outputMint: string;
|
|
21
|
+
inAmount: string;
|
|
22
|
+
outAmount: string;
|
|
23
|
+
swapMode: 'ExactIn' | 'ExactOut';
|
|
24
|
+
slippageBps: number;
|
|
25
|
+
}
|
|
26
|
+
/** Client for Jupiter's public quote API. Quote-only: never used to submit or execute a swap. */
|
|
27
|
+
declare class JupiterAPI {
|
|
28
|
+
getOrder(request: JupiterOrderRequest): Promise<JupiterOrderResponse>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* TS port of `cow-settlement-interface`'s `OrderIntent` (interface/src/data/intent.rs, v0.3.0).
|
|
33
|
+
* Every field here has a Rust counterpart with the same name; keep them in sync if the settlement
|
|
34
|
+
* program's wire format changes.
|
|
35
|
+
*/
|
|
36
|
+
interface SolanaOrderIntent {
|
|
37
|
+
owner: PublicKey;
|
|
38
|
+
buyTokenAccount: PublicKey;
|
|
39
|
+
buyMint: PublicKey;
|
|
40
|
+
sellTokenAccount: PublicKey;
|
|
41
|
+
sellMint: PublicKey;
|
|
42
|
+
sellAmount: bigint;
|
|
43
|
+
buyAmount: bigint;
|
|
44
|
+
/** Unix timestamp seconds. */
|
|
45
|
+
validTo: number;
|
|
46
|
+
kind: OrderKind;
|
|
47
|
+
partiallyFillable: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Must be `true`: this is the flag the `CreateOrder` instruction authenticates against (the owner
|
|
50
|
+
* signs the transaction themselves). The alternative — an off-chain Ed25519-presigned order anyone can
|
|
51
|
+
* submit — is a different, unused authentication path.
|
|
52
|
+
*/
|
|
53
|
+
createdOnChain: boolean;
|
|
54
|
+
/** Exactly 32 bytes, opaque to the settlement program. */
|
|
55
|
+
appData: Uint8Array;
|
|
56
|
+
}
|
|
57
|
+
/** Canonical byte size of an encoded `OrderIntent`, per `EncodedOrderIntent::SIZE` in the Rust source. */
|
|
58
|
+
declare const ENCODED_ORDER_INTENT_SIZE = 213;
|
|
59
|
+
declare function encodeOrderIntent(intent: SolanaOrderIntent): Uint8Array;
|
|
60
|
+
/**
|
|
61
|
+
* SHA-256 of the encoded intent bytes — doubles as the order UID and the middle seed of the order PDA
|
|
62
|
+
* (`OrderIntent::uid()` in the Rust source). Uses the Web Crypto API (available in both Node 20+ and
|
|
63
|
+
* browsers) rather than a new hashing dependency.
|
|
64
|
+
*/
|
|
65
|
+
declare function hashOrderIntent(encoded: Uint8Array): Promise<Uint8Array>;
|
|
66
|
+
declare function toHex(bytes: Uint8Array): string;
|
|
67
|
+
|
|
68
|
+
interface SolanaQuoteParameters {
|
|
69
|
+
ownerAddress: PublicKeyInitData;
|
|
70
|
+
receiverAddress: PublicKeyInitData;
|
|
71
|
+
sellTokenAddress: PublicKeyInitData;
|
|
72
|
+
sellTokenDecimals: number;
|
|
73
|
+
buyTokenAddress: PublicKeyInitData;
|
|
74
|
+
buyTokenDecimals: number;
|
|
75
|
+
/** Sell-side amount for a SELL order, buy-side amount for a BUY order — same convention as Jupiter's `amount`. */
|
|
76
|
+
amount: bigint;
|
|
77
|
+
kind: OrderKind;
|
|
78
|
+
partiallyFillable?: boolean;
|
|
79
|
+
/** Order lifetime from now, in seconds. Defaults to 30 minutes. */
|
|
80
|
+
validForSeconds?: number;
|
|
81
|
+
/** Token program owning `sellMint`'s accounts (classic SPL Token vs Token-2022). Defaults to the
|
|
82
|
+
* classic SPL Token program — pass `TOKEN_2022_PROGRAM_ID` explicitly for Token-2022 mints, since the
|
|
83
|
+
* associated token account address differs by program. */
|
|
84
|
+
sellTokenProgramId?: PublicKeyInitData;
|
|
85
|
+
/** Same as `sellTokenProgramId`, for `buyMint`. */
|
|
86
|
+
buyTokenProgramId?: PublicKeyInitData;
|
|
87
|
+
}
|
|
88
|
+
interface SolanaQuote {
|
|
89
|
+
intent: SolanaOrderIntent;
|
|
90
|
+
intentBytes: Uint8Array;
|
|
91
|
+
/** SHA-256 of `intentBytes`; also the order's uid and the order PDA's seed. */
|
|
92
|
+
uid: Uint8Array;
|
|
93
|
+
orderPda: PublicKey;
|
|
94
|
+
programId: PublicKey;
|
|
95
|
+
/** The raw Jupiter response the quote was built from — real amounts/slippage for the caller to read. */
|
|
96
|
+
jupiterOrder: JupiterOrderResponse;
|
|
97
|
+
/** Token program owning `intent.buyMint`'s accounts, as resolved at quote time — needed to re-derive
|
|
98
|
+
* `buyTokenAccount`'s associated token address if `receiver` is overridden when posting. */
|
|
99
|
+
buyTokenProgramId?: PublicKey;
|
|
100
|
+
}
|
|
101
|
+
/** Signs and submits a `CreateOrder` instruction; supplied by the caller since this SDK has no bound
|
|
102
|
+
* Solana wallet/signer (unlike the EVM adapter). */
|
|
103
|
+
type SolanaSignAndSend = (instruction: TransactionInstruction) => Promise<{
|
|
104
|
+
signature: string;
|
|
105
|
+
}>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Version-embedded seed shared by every settlement-program PDA (`SETTLEMENT_SEED` in
|
|
109
|
+
* cow-settlement-interface). Must be regenerated if `SOLANA_SETTLEMENT_PROGRAM_VERSION` changes.
|
|
110
|
+
*/
|
|
111
|
+
declare const SETTLEMENT_SEED: Uint8Array<ArrayBufferLike>;
|
|
112
|
+
/** Trailing seed identifying order PDAs (`ORDER_SEED` in cow-settlement-interface). */
|
|
113
|
+
declare const ORDER_SEED: Uint8Array<ArrayBufferLike>;
|
|
114
|
+
/**
|
|
115
|
+
* Derives the canonical order PDA and bump for an order's `uid`, matching `find_order_pda` in
|
|
116
|
+
* cow-settlement-interface.
|
|
117
|
+
*/
|
|
118
|
+
declare function findOrderPda(programId: PublicKey, uid: Uint8Array): [PublicKey, number];
|
|
119
|
+
|
|
120
|
+
interface CreateOrderInstructionParams {
|
|
121
|
+
programId: PublicKey;
|
|
122
|
+
/** Authenticates the order; must match `intent.owner` and sign the transaction. */
|
|
123
|
+
owner: PublicKey;
|
|
124
|
+
/** Funds the new order PDA's rent; must sign the transaction. May equal `owner`. */
|
|
125
|
+
createdBy: PublicKey;
|
|
126
|
+
/** The canonical PDA for `intent`'s uid — see `findOrderPda`. */
|
|
127
|
+
orderPda: PublicKey;
|
|
128
|
+
intent: SolanaOrderIntent;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Builds the `CreateOrder` instruction, matching `CreateOrder::into::<Instruction>()` in
|
|
132
|
+
* cow-settlement-interface: `data = [discriminator=2, ...213 intent bytes]`, accounts
|
|
133
|
+
* `[owner (readonly signer), created_by (writable signer), order_pda (writable), system_program]`.
|
|
134
|
+
*/
|
|
135
|
+
declare function buildCreateOrderInstruction(params: CreateOrderInstructionParams): TransactionInstruction;
|
|
136
|
+
|
|
137
|
+
declare function getSolanaQuote(params: SolanaQuoteParameters, options?: {
|
|
138
|
+
env?: CowEnv;
|
|
139
|
+
}): Promise<{
|
|
140
|
+
quoteResults: QuoteResults;
|
|
141
|
+
solanaQuote: SolanaQuote;
|
|
142
|
+
}>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Builds the real `CreateOrder` instruction for `quote` and has the caller sign and submit it. This is
|
|
146
|
+
* the Solana analogue of `postSwapOrderFromQuote` in `postSwapOrder.ts` — but where the EVM version signs
|
|
147
|
+
* order data and POSTs it to the CoW order-book, Solana orders are created entirely on-chain, so this
|
|
148
|
+
* builds a transaction instruction instead of a signed order body. `createdBy` is always `quote.intent.owner`:
|
|
149
|
+
* a single connected wallet both authenticates and funds the order's rent.
|
|
150
|
+
*/
|
|
151
|
+
declare function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }: {
|
|
152
|
+
quoteResults: QuoteResults;
|
|
153
|
+
solanaQuote: SolanaQuote;
|
|
154
|
+
}, signAndSend: SolanaSignAndSend, advancedSettings?: SwapAdvancedSettings, signingStepManager?: SigningStepManager): Promise<OrderPostingResult>;
|
|
155
|
+
|
|
156
|
+
interface SolanaTradingSdkOptions {
|
|
157
|
+
signAndSend: SolanaSignAndSend;
|
|
158
|
+
env?: CowEnv;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Solana counterpart to `TradingSdk`. Unlike the EVM SDK, which gets its signer implicitly from a
|
|
162
|
+
* global adapter set once at app startup, Solana has no such adapter — `signAndSend` is bound at
|
|
163
|
+
* construction instead, so callers get the same `sdk.getQuote(...)` → `.postSwapOrderFromQuote()`
|
|
164
|
+
* shape without threading a signer through every call.
|
|
165
|
+
*/
|
|
166
|
+
declare class SolanaTradingSdk {
|
|
167
|
+
private readonly options;
|
|
168
|
+
constructor(options: SolanaTradingSdkOptions);
|
|
169
|
+
getQuote(params: SolanaQuoteParameters): Promise<QuoteAndPost>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export { type CreateOrderInstructionParams, ENCODED_ORDER_INTENT_SIZE, JupiterAPI, type JupiterOrderRequest, type JupiterOrderResponse, ORDER_SEED, SETTLEMENT_SEED, type SolanaOrderIntent, type SolanaQuote, type SolanaQuoteParameters, type SolanaSignAndSend, SolanaTradingSdk, type SolanaTradingSdkOptions, buildCreateOrderInstruction, encodeOrderIntent, findOrderPda, getSolanaQuote, hashOrderIntent, postSolanaSwapOrderFromQuote, toHex };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { PublicKey, PublicKeyInitData, TransactionInstruction } from '@solana/web3.js';
|
|
2
|
+
import { OrderKind } from '@cowprotocol/sdk-order-book';
|
|
3
|
+
import { CowEnv } from '@cowprotocol/sdk-config';
|
|
4
|
+
import { QuoteResults, SwapAdvancedSettings, SigningStepManager, OrderPostingResult, QuoteAndPost } from '@cowprotocol/sdk-trading';
|
|
5
|
+
|
|
6
|
+
interface JupiterOrderRequest {
|
|
7
|
+
inputMint: string;
|
|
8
|
+
outputMint: string;
|
|
9
|
+
amount: string;
|
|
10
|
+
swapMode: 'ExactIn' | 'ExactOut';
|
|
11
|
+
clientPlatform?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Fields of Jupiter's `/order` response this SDK actually reads. Jupiter's own swap transaction/execute
|
|
15
|
+
* flow (`transaction`, `requestId`) is deliberately not modeled here — quotes are sourced from Jupiter,
|
|
16
|
+
* but orders are posted through the CoW Protocol settlement program, never through Jupiter's `/execute`.
|
|
17
|
+
*/
|
|
18
|
+
interface JupiterOrderResponse {
|
|
19
|
+
inputMint: string;
|
|
20
|
+
outputMint: string;
|
|
21
|
+
inAmount: string;
|
|
22
|
+
outAmount: string;
|
|
23
|
+
swapMode: 'ExactIn' | 'ExactOut';
|
|
24
|
+
slippageBps: number;
|
|
25
|
+
}
|
|
26
|
+
/** Client for Jupiter's public quote API. Quote-only: never used to submit or execute a swap. */
|
|
27
|
+
declare class JupiterAPI {
|
|
28
|
+
getOrder(request: JupiterOrderRequest): Promise<JupiterOrderResponse>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* TS port of `cow-settlement-interface`'s `OrderIntent` (interface/src/data/intent.rs, v0.3.0).
|
|
33
|
+
* Every field here has a Rust counterpart with the same name; keep them in sync if the settlement
|
|
34
|
+
* program's wire format changes.
|
|
35
|
+
*/
|
|
36
|
+
interface SolanaOrderIntent {
|
|
37
|
+
owner: PublicKey;
|
|
38
|
+
buyTokenAccount: PublicKey;
|
|
39
|
+
buyMint: PublicKey;
|
|
40
|
+
sellTokenAccount: PublicKey;
|
|
41
|
+
sellMint: PublicKey;
|
|
42
|
+
sellAmount: bigint;
|
|
43
|
+
buyAmount: bigint;
|
|
44
|
+
/** Unix timestamp seconds. */
|
|
45
|
+
validTo: number;
|
|
46
|
+
kind: OrderKind;
|
|
47
|
+
partiallyFillable: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Must be `true`: this is the flag the `CreateOrder` instruction authenticates against (the owner
|
|
50
|
+
* signs the transaction themselves). The alternative — an off-chain Ed25519-presigned order anyone can
|
|
51
|
+
* submit — is a different, unused authentication path.
|
|
52
|
+
*/
|
|
53
|
+
createdOnChain: boolean;
|
|
54
|
+
/** Exactly 32 bytes, opaque to the settlement program. */
|
|
55
|
+
appData: Uint8Array;
|
|
56
|
+
}
|
|
57
|
+
/** Canonical byte size of an encoded `OrderIntent`, per `EncodedOrderIntent::SIZE` in the Rust source. */
|
|
58
|
+
declare const ENCODED_ORDER_INTENT_SIZE = 213;
|
|
59
|
+
declare function encodeOrderIntent(intent: SolanaOrderIntent): Uint8Array;
|
|
60
|
+
/**
|
|
61
|
+
* SHA-256 of the encoded intent bytes — doubles as the order UID and the middle seed of the order PDA
|
|
62
|
+
* (`OrderIntent::uid()` in the Rust source). Uses the Web Crypto API (available in both Node 20+ and
|
|
63
|
+
* browsers) rather than a new hashing dependency.
|
|
64
|
+
*/
|
|
65
|
+
declare function hashOrderIntent(encoded: Uint8Array): Promise<Uint8Array>;
|
|
66
|
+
declare function toHex(bytes: Uint8Array): string;
|
|
67
|
+
|
|
68
|
+
interface SolanaQuoteParameters {
|
|
69
|
+
ownerAddress: PublicKeyInitData;
|
|
70
|
+
receiverAddress: PublicKeyInitData;
|
|
71
|
+
sellTokenAddress: PublicKeyInitData;
|
|
72
|
+
sellTokenDecimals: number;
|
|
73
|
+
buyTokenAddress: PublicKeyInitData;
|
|
74
|
+
buyTokenDecimals: number;
|
|
75
|
+
/** Sell-side amount for a SELL order, buy-side amount for a BUY order — same convention as Jupiter's `amount`. */
|
|
76
|
+
amount: bigint;
|
|
77
|
+
kind: OrderKind;
|
|
78
|
+
partiallyFillable?: boolean;
|
|
79
|
+
/** Order lifetime from now, in seconds. Defaults to 30 minutes. */
|
|
80
|
+
validForSeconds?: number;
|
|
81
|
+
/** Token program owning `sellMint`'s accounts (classic SPL Token vs Token-2022). Defaults to the
|
|
82
|
+
* classic SPL Token program — pass `TOKEN_2022_PROGRAM_ID` explicitly for Token-2022 mints, since the
|
|
83
|
+
* associated token account address differs by program. */
|
|
84
|
+
sellTokenProgramId?: PublicKeyInitData;
|
|
85
|
+
/** Same as `sellTokenProgramId`, for `buyMint`. */
|
|
86
|
+
buyTokenProgramId?: PublicKeyInitData;
|
|
87
|
+
}
|
|
88
|
+
interface SolanaQuote {
|
|
89
|
+
intent: SolanaOrderIntent;
|
|
90
|
+
intentBytes: Uint8Array;
|
|
91
|
+
/** SHA-256 of `intentBytes`; also the order's uid and the order PDA's seed. */
|
|
92
|
+
uid: Uint8Array;
|
|
93
|
+
orderPda: PublicKey;
|
|
94
|
+
programId: PublicKey;
|
|
95
|
+
/** The raw Jupiter response the quote was built from — real amounts/slippage for the caller to read. */
|
|
96
|
+
jupiterOrder: JupiterOrderResponse;
|
|
97
|
+
/** Token program owning `intent.buyMint`'s accounts, as resolved at quote time — needed to re-derive
|
|
98
|
+
* `buyTokenAccount`'s associated token address if `receiver` is overridden when posting. */
|
|
99
|
+
buyTokenProgramId?: PublicKey;
|
|
100
|
+
}
|
|
101
|
+
/** Signs and submits a `CreateOrder` instruction; supplied by the caller since this SDK has no bound
|
|
102
|
+
* Solana wallet/signer (unlike the EVM adapter). */
|
|
103
|
+
type SolanaSignAndSend = (instruction: TransactionInstruction) => Promise<{
|
|
104
|
+
signature: string;
|
|
105
|
+
}>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Version-embedded seed shared by every settlement-program PDA (`SETTLEMENT_SEED` in
|
|
109
|
+
* cow-settlement-interface). Must be regenerated if `SOLANA_SETTLEMENT_PROGRAM_VERSION` changes.
|
|
110
|
+
*/
|
|
111
|
+
declare const SETTLEMENT_SEED: Uint8Array<ArrayBufferLike>;
|
|
112
|
+
/** Trailing seed identifying order PDAs (`ORDER_SEED` in cow-settlement-interface). */
|
|
113
|
+
declare const ORDER_SEED: Uint8Array<ArrayBufferLike>;
|
|
114
|
+
/**
|
|
115
|
+
* Derives the canonical order PDA and bump for an order's `uid`, matching `find_order_pda` in
|
|
116
|
+
* cow-settlement-interface.
|
|
117
|
+
*/
|
|
118
|
+
declare function findOrderPda(programId: PublicKey, uid: Uint8Array): [PublicKey, number];
|
|
119
|
+
|
|
120
|
+
interface CreateOrderInstructionParams {
|
|
121
|
+
programId: PublicKey;
|
|
122
|
+
/** Authenticates the order; must match `intent.owner` and sign the transaction. */
|
|
123
|
+
owner: PublicKey;
|
|
124
|
+
/** Funds the new order PDA's rent; must sign the transaction. May equal `owner`. */
|
|
125
|
+
createdBy: PublicKey;
|
|
126
|
+
/** The canonical PDA for `intent`'s uid — see `findOrderPda`. */
|
|
127
|
+
orderPda: PublicKey;
|
|
128
|
+
intent: SolanaOrderIntent;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Builds the `CreateOrder` instruction, matching `CreateOrder::into::<Instruction>()` in
|
|
132
|
+
* cow-settlement-interface: `data = [discriminator=2, ...213 intent bytes]`, accounts
|
|
133
|
+
* `[owner (readonly signer), created_by (writable signer), order_pda (writable), system_program]`.
|
|
134
|
+
*/
|
|
135
|
+
declare function buildCreateOrderInstruction(params: CreateOrderInstructionParams): TransactionInstruction;
|
|
136
|
+
|
|
137
|
+
declare function getSolanaQuote(params: SolanaQuoteParameters, options?: {
|
|
138
|
+
env?: CowEnv;
|
|
139
|
+
}): Promise<{
|
|
140
|
+
quoteResults: QuoteResults;
|
|
141
|
+
solanaQuote: SolanaQuote;
|
|
142
|
+
}>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Builds the real `CreateOrder` instruction for `quote` and has the caller sign and submit it. This is
|
|
146
|
+
* the Solana analogue of `postSwapOrderFromQuote` in `postSwapOrder.ts` — but where the EVM version signs
|
|
147
|
+
* order data and POSTs it to the CoW order-book, Solana orders are created entirely on-chain, so this
|
|
148
|
+
* builds a transaction instruction instead of a signed order body. `createdBy` is always `quote.intent.owner`:
|
|
149
|
+
* a single connected wallet both authenticates and funds the order's rent.
|
|
150
|
+
*/
|
|
151
|
+
declare function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }: {
|
|
152
|
+
quoteResults: QuoteResults;
|
|
153
|
+
solanaQuote: SolanaQuote;
|
|
154
|
+
}, signAndSend: SolanaSignAndSend, advancedSettings?: SwapAdvancedSettings, signingStepManager?: SigningStepManager): Promise<OrderPostingResult>;
|
|
155
|
+
|
|
156
|
+
interface SolanaTradingSdkOptions {
|
|
157
|
+
signAndSend: SolanaSignAndSend;
|
|
158
|
+
env?: CowEnv;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Solana counterpart to `TradingSdk`. Unlike the EVM SDK, which gets its signer implicitly from a
|
|
162
|
+
* global adapter set once at app startup, Solana has no such adapter — `signAndSend` is bound at
|
|
163
|
+
* construction instead, so callers get the same `sdk.getQuote(...)` → `.postSwapOrderFromQuote()`
|
|
164
|
+
* shape without threading a signer through every call.
|
|
165
|
+
*/
|
|
166
|
+
declare class SolanaTradingSdk {
|
|
167
|
+
private readonly options;
|
|
168
|
+
constructor(options: SolanaTradingSdkOptions);
|
|
169
|
+
getQuote(params: SolanaQuoteParameters): Promise<QuoteAndPost>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export { type CreateOrderInstructionParams, ENCODED_ORDER_INTENT_SIZE, JupiterAPI, type JupiterOrderRequest, type JupiterOrderResponse, ORDER_SEED, SETTLEMENT_SEED, type SolanaOrderIntent, type SolanaQuote, type SolanaQuoteParameters, type SolanaSignAndSend, SolanaTradingSdk, type SolanaTradingSdkOptions, buildCreateOrderInstruction, encodeOrderIntent, findOrderPda, getSolanaQuote, hashOrderIntent, postSolanaSwapOrderFromQuote, toHex };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
ENCODED_ORDER_INTENT_SIZE: () => ENCODED_ORDER_INTENT_SIZE,
|
|
24
|
+
JupiterAPI: () => JupiterAPI,
|
|
25
|
+
ORDER_SEED: () => ORDER_SEED,
|
|
26
|
+
SETTLEMENT_SEED: () => SETTLEMENT_SEED,
|
|
27
|
+
SolanaTradingSdk: () => SolanaTradingSdk,
|
|
28
|
+
buildCreateOrderInstruction: () => buildCreateOrderInstruction,
|
|
29
|
+
encodeOrderIntent: () => encodeOrderIntent,
|
|
30
|
+
findOrderPda: () => findOrderPda,
|
|
31
|
+
getSolanaQuote: () => getSolanaQuote,
|
|
32
|
+
hashOrderIntent: () => hashOrderIntent,
|
|
33
|
+
postSolanaSwapOrderFromQuote: () => postSolanaSwapOrderFromQuote,
|
|
34
|
+
toHex: () => toHex
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(src_exports);
|
|
37
|
+
|
|
38
|
+
// src/orderIntent.ts
|
|
39
|
+
var import_sdk_order_book = require("@cowprotocol/sdk-order-book");
|
|
40
|
+
var ENCODED_ORDER_INTENT_SIZE = 213;
|
|
41
|
+
var FLAG_CREATED_ON_CHAIN = 1 << 0;
|
|
42
|
+
var FLAG_KIND_BUY = 1 << 1;
|
|
43
|
+
var FLAG_PARTIALLY_FILLABLE = 1 << 2;
|
|
44
|
+
var MAX_VALID_TO = 4294967295;
|
|
45
|
+
function encodeOrderIntent(intent) {
|
|
46
|
+
if (intent.appData.length !== 32) {
|
|
47
|
+
throw new Error("appData must be exactly 32 bytes");
|
|
48
|
+
}
|
|
49
|
+
if (!Number.isInteger(intent.validTo) || intent.validTo < 0 || intent.validTo > MAX_VALID_TO) {
|
|
50
|
+
throw new Error(`validTo must be an integer between 0 and ${MAX_VALID_TO}`);
|
|
51
|
+
}
|
|
52
|
+
const bytes = new Uint8Array(ENCODED_ORDER_INTENT_SIZE);
|
|
53
|
+
const view = new DataView(bytes.buffer);
|
|
54
|
+
let offset = 0;
|
|
55
|
+
const writePubkey = (pubkey) => {
|
|
56
|
+
bytes.set(pubkey.toBytes(), offset);
|
|
57
|
+
offset += 32;
|
|
58
|
+
};
|
|
59
|
+
const writeU64LE = (value) => {
|
|
60
|
+
view.setBigUint64(offset, value, true);
|
|
61
|
+
offset += 8;
|
|
62
|
+
};
|
|
63
|
+
writePubkey(intent.owner);
|
|
64
|
+
writePubkey(intent.buyTokenAccount);
|
|
65
|
+
writePubkey(intent.buyMint);
|
|
66
|
+
writePubkey(intent.sellTokenAccount);
|
|
67
|
+
writePubkey(intent.sellMint);
|
|
68
|
+
writeU64LE(intent.sellAmount);
|
|
69
|
+
writeU64LE(intent.buyAmount);
|
|
70
|
+
view.setUint32(offset, intent.validTo, true);
|
|
71
|
+
offset += 4;
|
|
72
|
+
let flags = 0;
|
|
73
|
+
if (intent.createdOnChain)
|
|
74
|
+
flags |= FLAG_CREATED_ON_CHAIN;
|
|
75
|
+
if (intent.kind === import_sdk_order_book.OrderKind.BUY)
|
|
76
|
+
flags |= FLAG_KIND_BUY;
|
|
77
|
+
if (intent.partiallyFillable)
|
|
78
|
+
flags |= FLAG_PARTIALLY_FILLABLE;
|
|
79
|
+
bytes[offset] = flags;
|
|
80
|
+
offset += 1;
|
|
81
|
+
bytes.set(intent.appData, offset);
|
|
82
|
+
return bytes;
|
|
83
|
+
}
|
|
84
|
+
async function hashOrderIntent(encoded) {
|
|
85
|
+
const digest = await crypto.subtle.digest("SHA-256", encoded);
|
|
86
|
+
return new Uint8Array(digest);
|
|
87
|
+
}
|
|
88
|
+
function toHex(bytes) {
|
|
89
|
+
return Array.from(bytes).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/orderPda.ts
|
|
93
|
+
var import_web3 = require("@solana/web3.js");
|
|
94
|
+
var import_sdk_config = require("@cowprotocol/sdk-config");
|
|
95
|
+
var SETTLEMENT_SEED_PREFIX = "settlement v";
|
|
96
|
+
var SETTLEMENT_SEED_VERSION_LEN = 7;
|
|
97
|
+
var SETTLEMENT_SEED = new TextEncoder().encode(
|
|
98
|
+
SETTLEMENT_SEED_PREFIX + import_sdk_config.SOLANA_SETTLEMENT_PROGRAM_VERSION.padEnd(SETTLEMENT_SEED_VERSION_LEN, " ")
|
|
99
|
+
);
|
|
100
|
+
var ORDER_SEED = new TextEncoder().encode("order");
|
|
101
|
+
function findOrderPda(programId, uid) {
|
|
102
|
+
return import_web3.PublicKey.findProgramAddressSync([SETTLEMENT_SEED, uid, ORDER_SEED], programId);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/createOrderInstruction.ts
|
|
106
|
+
var import_web32 = require("@solana/web3.js");
|
|
107
|
+
var CREATE_ORDER_DISCRIMINATOR = 2;
|
|
108
|
+
function buildCreateOrderInstruction(params) {
|
|
109
|
+
const intentBytes = encodeOrderIntent(params.intent);
|
|
110
|
+
const data = Buffer.alloc(1 + intentBytes.length);
|
|
111
|
+
data[0] = CREATE_ORDER_DISCRIMINATOR;
|
|
112
|
+
data.set(intentBytes, 1);
|
|
113
|
+
return new import_web32.TransactionInstruction({
|
|
114
|
+
programId: params.programId,
|
|
115
|
+
keys: [
|
|
116
|
+
{ pubkey: params.owner, isSigner: true, isWritable: false },
|
|
117
|
+
{ pubkey: params.createdBy, isSigner: true, isWritable: true },
|
|
118
|
+
{ pubkey: params.orderPda, isSigner: false, isWritable: true },
|
|
119
|
+
{ pubkey: import_web32.SystemProgram.programId, isSigner: false, isWritable: false }
|
|
120
|
+
],
|
|
121
|
+
data
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/jupiterApi.ts
|
|
126
|
+
var JUPITER_ORDER_ENDPOINT = "https://ultra-api.jup.ag/order";
|
|
127
|
+
var DEFAULT_CLIENT_PLATFORM = "cowswap";
|
|
128
|
+
var QUOTE_TIMEOUT_MS = 1e4;
|
|
129
|
+
var UNSIGNED_INTEGER_PATTERN = /^\d+$/;
|
|
130
|
+
var JupiterAPI = class {
|
|
131
|
+
async getOrder(request) {
|
|
132
|
+
const params = new URLSearchParams({
|
|
133
|
+
inputMint: request.inputMint,
|
|
134
|
+
outputMint: request.outputMint,
|
|
135
|
+
amount: request.amount,
|
|
136
|
+
swapMode: request.swapMode,
|
|
137
|
+
clientPlatform: request.clientPlatform ?? DEFAULT_CLIENT_PLATFORM
|
|
138
|
+
});
|
|
139
|
+
const controller = new AbortController();
|
|
140
|
+
const timeoutId = setTimeout(() => controller.abort(), QUOTE_TIMEOUT_MS);
|
|
141
|
+
try {
|
|
142
|
+
const response = await fetch(`${JUPITER_ORDER_ENDPOINT}?${params.toString()}`, { signal: controller.signal });
|
|
143
|
+
let body;
|
|
144
|
+
try {
|
|
145
|
+
body = await response.json();
|
|
146
|
+
} catch {
|
|
147
|
+
throw new Error(`Jupiter quote request failed (${response.status})`);
|
|
148
|
+
}
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
const message = isJupiterErrorResponse(body) ? body.error : `Jupiter quote request failed (${response.status})`;
|
|
151
|
+
throw new Error(message);
|
|
152
|
+
}
|
|
153
|
+
if (!isJupiterOrderResponse(body, request)) {
|
|
154
|
+
throw new Error("Jupiter quote response is malformed or does not match the requested pair");
|
|
155
|
+
}
|
|
156
|
+
return body;
|
|
157
|
+
} finally {
|
|
158
|
+
clearTimeout(timeoutId);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
function isJupiterErrorResponse(body) {
|
|
163
|
+
return typeof body === "object" && body !== null && typeof body.error === "string";
|
|
164
|
+
}
|
|
165
|
+
function isJupiterOrderResponse(body, request) {
|
|
166
|
+
if (typeof body !== "object" || body === null)
|
|
167
|
+
return false;
|
|
168
|
+
const candidate = body;
|
|
169
|
+
return candidate.inputMint === request.inputMint && candidate.outputMint === request.outputMint && candidate.swapMode === request.swapMode && typeof candidate.inAmount === "string" && UNSIGNED_INTEGER_PATTERN.test(candidate.inAmount) && typeof candidate.outAmount === "string" && UNSIGNED_INTEGER_PATTERN.test(candidate.outAmount) && typeof candidate.slippageBps === "number" && Number.isFinite(candidate.slippageBps);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/getSolanaQuote.ts
|
|
173
|
+
var import_web33 = require("@solana/web3.js");
|
|
174
|
+
var import_spl_token = require("@solana/spl-token");
|
|
175
|
+
var import_sdk_config2 = require("@cowprotocol/sdk-config");
|
|
176
|
+
var import_sdk_order_book2 = require("@cowprotocol/sdk-order-book");
|
|
177
|
+
var DEFAULT_VALID_FOR_SECONDS = 30 * 60;
|
|
178
|
+
var ZERO_APP_DATA = new Uint8Array(32);
|
|
179
|
+
var jupiterApi = new JupiterAPI();
|
|
180
|
+
async function getSolanaQuote(params, options = {}) {
|
|
181
|
+
const {
|
|
182
|
+
ownerAddress,
|
|
183
|
+
receiverAddress,
|
|
184
|
+
sellTokenDecimals,
|
|
185
|
+
buyTokenDecimals,
|
|
186
|
+
amount,
|
|
187
|
+
kind,
|
|
188
|
+
partiallyFillable = false,
|
|
189
|
+
validForSeconds = DEFAULT_VALID_FOR_SECONDS,
|
|
190
|
+
sellTokenProgramId,
|
|
191
|
+
buyTokenProgramId
|
|
192
|
+
} = params;
|
|
193
|
+
if (!Number.isFinite(validForSeconds) || validForSeconds <= 0) {
|
|
194
|
+
throw new Error("validForSeconds must be a finite number greater than zero");
|
|
195
|
+
}
|
|
196
|
+
const owner = new import_web33.PublicKey(ownerAddress);
|
|
197
|
+
const receiver = new import_web33.PublicKey(receiverAddress);
|
|
198
|
+
const sellMint = new import_web33.PublicKey(params.sellTokenAddress);
|
|
199
|
+
const buyMint = new import_web33.PublicKey(params.buyTokenAddress);
|
|
200
|
+
const sellTokenProgram = sellTokenProgramId ? new import_web33.PublicKey(sellTokenProgramId) : void 0;
|
|
201
|
+
const buyTokenProgram = buyTokenProgramId ? new import_web33.PublicKey(buyTokenProgramId) : void 0;
|
|
202
|
+
const sellTokenAddress = sellMint.toBase58();
|
|
203
|
+
const buyTokenAddress = buyMint.toBase58();
|
|
204
|
+
const jupiterOrder = await jupiterApi.getOrder({
|
|
205
|
+
inputMint: sellTokenAddress,
|
|
206
|
+
outputMint: buyTokenAddress,
|
|
207
|
+
amount: amount.toString(),
|
|
208
|
+
swapMode: kind === import_sdk_order_book2.OrderKind.SELL ? "ExactIn" : "ExactOut"
|
|
209
|
+
});
|
|
210
|
+
const validTo = Math.floor(Date.now() / 1e3) + validForSeconds;
|
|
211
|
+
const orderParams = {
|
|
212
|
+
sellToken: sellTokenAddress,
|
|
213
|
+
buyToken: buyTokenAddress,
|
|
214
|
+
receiver: receiver.toBase58(),
|
|
215
|
+
sellAmount: jupiterOrder.inAmount,
|
|
216
|
+
buyAmount: jupiterOrder.outAmount,
|
|
217
|
+
validTo,
|
|
218
|
+
// TODO: fill appData when we know the format
|
|
219
|
+
appData: "{}",
|
|
220
|
+
// TODO: implement fees
|
|
221
|
+
feeAmount: "0",
|
|
222
|
+
gasAmount: "0",
|
|
223
|
+
gasPrice: "0",
|
|
224
|
+
sellTokenPrice: "0",
|
|
225
|
+
kind,
|
|
226
|
+
partiallyFillable
|
|
227
|
+
};
|
|
228
|
+
const amountsAndCosts = (0, import_sdk_order_book2.getQuoteAmountsAndCosts)({
|
|
229
|
+
orderParams,
|
|
230
|
+
slippagePercentBps: jupiterOrder.slippageBps,
|
|
231
|
+
// TODO: implement fees
|
|
232
|
+
partnerFeeBps: 0,
|
|
233
|
+
protocolFeeBps: 0
|
|
234
|
+
});
|
|
235
|
+
const intent = {
|
|
236
|
+
owner,
|
|
237
|
+
buyTokenAccount: (0, import_spl_token.getAssociatedTokenAddressSync)(buyMint, receiver, false, buyTokenProgram),
|
|
238
|
+
buyMint,
|
|
239
|
+
sellTokenAccount: (0, import_spl_token.getAssociatedTokenAddressSync)(sellMint, owner, false, sellTokenProgram),
|
|
240
|
+
sellMint,
|
|
241
|
+
sellAmount: amountsAndCosts.amountsToSign.sellAmount,
|
|
242
|
+
buyAmount: amountsAndCosts.amountsToSign.buyAmount,
|
|
243
|
+
validTo,
|
|
244
|
+
kind,
|
|
245
|
+
partiallyFillable,
|
|
246
|
+
createdOnChain: true,
|
|
247
|
+
appData: ZERO_APP_DATA
|
|
248
|
+
};
|
|
249
|
+
const intentBytes = encodeOrderIntent(intent);
|
|
250
|
+
const uid = await hashOrderIntent(intentBytes);
|
|
251
|
+
const programId = new import_web33.PublicKey(
|
|
252
|
+
options.env === "staging" ? import_sdk_config2.SOLANA_SETTLEMENT_PROGRAM_ID_STAGING : import_sdk_config2.SOLANA_SETTLEMENT_PROGRAM_ID
|
|
253
|
+
);
|
|
254
|
+
const [orderPda] = findOrderPda(programId, uid);
|
|
255
|
+
const solanaQuote = {
|
|
256
|
+
intent,
|
|
257
|
+
intentBytes,
|
|
258
|
+
uid,
|
|
259
|
+
orderPda,
|
|
260
|
+
programId,
|
|
261
|
+
jupiterOrder,
|
|
262
|
+
buyTokenProgramId: buyTokenProgram
|
|
263
|
+
};
|
|
264
|
+
const quoteResponse = {
|
|
265
|
+
quote: orderParams,
|
|
266
|
+
from: owner.toBase58(),
|
|
267
|
+
expiration: new Date(intent.validTo * 1e3).toISOString(),
|
|
268
|
+
verified: false
|
|
269
|
+
};
|
|
270
|
+
const tradeParameters = {
|
|
271
|
+
kind,
|
|
272
|
+
owner: owner.toBase58(),
|
|
273
|
+
sellToken: sellTokenAddress,
|
|
274
|
+
sellTokenDecimals,
|
|
275
|
+
buyToken: buyTokenAddress,
|
|
276
|
+
buyTokenDecimals,
|
|
277
|
+
amount: amount.toString(),
|
|
278
|
+
receiver: receiver.toBase58(),
|
|
279
|
+
validFor: validForSeconds,
|
|
280
|
+
partiallyFillable: orderParams.partiallyFillable
|
|
281
|
+
};
|
|
282
|
+
const quoteResults = {
|
|
283
|
+
quoteResponse,
|
|
284
|
+
amountsAndCosts,
|
|
285
|
+
suggestedSlippageBps: jupiterOrder.slippageBps,
|
|
286
|
+
tradeParameters,
|
|
287
|
+
orderToSign: {},
|
|
288
|
+
appDataInfo: {},
|
|
289
|
+
orderTypedData: {}
|
|
290
|
+
};
|
|
291
|
+
return { quoteResults, solanaQuote };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/postSwapOrderFromQuote.ts
|
|
295
|
+
var import_spl_token2 = require("@solana/spl-token");
|
|
296
|
+
var import_sdk_order_book3 = require("@cowprotocol/sdk-order-book");
|
|
297
|
+
var import_web34 = require("@solana/web3.js");
|
|
298
|
+
async function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }, signAndSend, advancedSettings, signingStepManager) {
|
|
299
|
+
const intent = { ...solanaQuote.intent };
|
|
300
|
+
let uid = solanaQuote.uid;
|
|
301
|
+
let orderPda = solanaQuote.orderPda;
|
|
302
|
+
if (advancedSettings?.quoteRequest) {
|
|
303
|
+
const { validTo, receiver } = advancedSettings.quoteRequest;
|
|
304
|
+
if (receiver) {
|
|
305
|
+
intent.buyTokenAccount = (0, import_spl_token2.getAssociatedTokenAddressSync)(
|
|
306
|
+
intent.buyMint,
|
|
307
|
+
new import_web34.PublicKey(receiver),
|
|
308
|
+
false,
|
|
309
|
+
solanaQuote.buyTokenProgramId
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
if (validTo)
|
|
313
|
+
intent.validTo = validTo;
|
|
314
|
+
if (receiver || validTo) {
|
|
315
|
+
const intentBytes = encodeOrderIntent(intent);
|
|
316
|
+
uid = await hashOrderIntent(intentBytes);
|
|
317
|
+
[orderPda] = findOrderPda(solanaQuote.programId, uid);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const instruction = buildCreateOrderInstruction({
|
|
321
|
+
programId: solanaQuote.programId,
|
|
322
|
+
owner: intent.owner,
|
|
323
|
+
createdBy: intent.owner,
|
|
324
|
+
orderPda,
|
|
325
|
+
intent
|
|
326
|
+
});
|
|
327
|
+
await signingStepManager?.beforeOrderSign?.();
|
|
328
|
+
const { signature } = await signAndSend(instruction);
|
|
329
|
+
await signingStepManager?.afterOrderSign?.();
|
|
330
|
+
const orderToSign = quoteResults.orderToSign;
|
|
331
|
+
return {
|
|
332
|
+
orderId: toHex(uid),
|
|
333
|
+
txHash: signature,
|
|
334
|
+
signature,
|
|
335
|
+
signingScheme: import_sdk_order_book3.SigningScheme.PRESIGN,
|
|
336
|
+
orderToSign
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// src/solanaTradingSdk.ts
|
|
341
|
+
var SolanaTradingSdk = class {
|
|
342
|
+
constructor(options) {
|
|
343
|
+
this.options = options;
|
|
344
|
+
}
|
|
345
|
+
async getQuote(params) {
|
|
346
|
+
const quote = await getSolanaQuote(params, { env: this.options.env });
|
|
347
|
+
return {
|
|
348
|
+
quoteResults: quote.quoteResults,
|
|
349
|
+
postSwapOrderFromQuote: (advancedSettings, signingStepManager) => postSolanaSwapOrderFromQuote(quote, this.options.signAndSend, advancedSettings, signingStepManager)
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
354
|
+
0 && (module.exports = {
|
|
355
|
+
ENCODED_ORDER_INTENT_SIZE,
|
|
356
|
+
JupiterAPI,
|
|
357
|
+
ORDER_SEED,
|
|
358
|
+
SETTLEMENT_SEED,
|
|
359
|
+
SolanaTradingSdk,
|
|
360
|
+
buildCreateOrderInstruction,
|
|
361
|
+
encodeOrderIntent,
|
|
362
|
+
findOrderPda,
|
|
363
|
+
getSolanaQuote,
|
|
364
|
+
hashOrderIntent,
|
|
365
|
+
postSolanaSwapOrderFromQuote,
|
|
366
|
+
toHex
|
|
367
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
// src/orderIntent.ts
|
|
2
|
+
import { OrderKind } from "@cowprotocol/sdk-order-book";
|
|
3
|
+
var ENCODED_ORDER_INTENT_SIZE = 213;
|
|
4
|
+
var FLAG_CREATED_ON_CHAIN = 1 << 0;
|
|
5
|
+
var FLAG_KIND_BUY = 1 << 1;
|
|
6
|
+
var FLAG_PARTIALLY_FILLABLE = 1 << 2;
|
|
7
|
+
var MAX_VALID_TO = 4294967295;
|
|
8
|
+
function encodeOrderIntent(intent) {
|
|
9
|
+
if (intent.appData.length !== 32) {
|
|
10
|
+
throw new Error("appData must be exactly 32 bytes");
|
|
11
|
+
}
|
|
12
|
+
if (!Number.isInteger(intent.validTo) || intent.validTo < 0 || intent.validTo > MAX_VALID_TO) {
|
|
13
|
+
throw new Error(`validTo must be an integer between 0 and ${MAX_VALID_TO}`);
|
|
14
|
+
}
|
|
15
|
+
const bytes = new Uint8Array(ENCODED_ORDER_INTENT_SIZE);
|
|
16
|
+
const view = new DataView(bytes.buffer);
|
|
17
|
+
let offset = 0;
|
|
18
|
+
const writePubkey = (pubkey) => {
|
|
19
|
+
bytes.set(pubkey.toBytes(), offset);
|
|
20
|
+
offset += 32;
|
|
21
|
+
};
|
|
22
|
+
const writeU64LE = (value) => {
|
|
23
|
+
view.setBigUint64(offset, value, true);
|
|
24
|
+
offset += 8;
|
|
25
|
+
};
|
|
26
|
+
writePubkey(intent.owner);
|
|
27
|
+
writePubkey(intent.buyTokenAccount);
|
|
28
|
+
writePubkey(intent.buyMint);
|
|
29
|
+
writePubkey(intent.sellTokenAccount);
|
|
30
|
+
writePubkey(intent.sellMint);
|
|
31
|
+
writeU64LE(intent.sellAmount);
|
|
32
|
+
writeU64LE(intent.buyAmount);
|
|
33
|
+
view.setUint32(offset, intent.validTo, true);
|
|
34
|
+
offset += 4;
|
|
35
|
+
let flags = 0;
|
|
36
|
+
if (intent.createdOnChain)
|
|
37
|
+
flags |= FLAG_CREATED_ON_CHAIN;
|
|
38
|
+
if (intent.kind === OrderKind.BUY)
|
|
39
|
+
flags |= FLAG_KIND_BUY;
|
|
40
|
+
if (intent.partiallyFillable)
|
|
41
|
+
flags |= FLAG_PARTIALLY_FILLABLE;
|
|
42
|
+
bytes[offset] = flags;
|
|
43
|
+
offset += 1;
|
|
44
|
+
bytes.set(intent.appData, offset);
|
|
45
|
+
return bytes;
|
|
46
|
+
}
|
|
47
|
+
async function hashOrderIntent(encoded) {
|
|
48
|
+
const digest = await crypto.subtle.digest("SHA-256", encoded);
|
|
49
|
+
return new Uint8Array(digest);
|
|
50
|
+
}
|
|
51
|
+
function toHex(bytes) {
|
|
52
|
+
return Array.from(bytes).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/orderPda.ts
|
|
56
|
+
import { PublicKey } from "@solana/web3.js";
|
|
57
|
+
import { SOLANA_SETTLEMENT_PROGRAM_VERSION } from "@cowprotocol/sdk-config";
|
|
58
|
+
var SETTLEMENT_SEED_PREFIX = "settlement v";
|
|
59
|
+
var SETTLEMENT_SEED_VERSION_LEN = 7;
|
|
60
|
+
var SETTLEMENT_SEED = new TextEncoder().encode(
|
|
61
|
+
SETTLEMENT_SEED_PREFIX + SOLANA_SETTLEMENT_PROGRAM_VERSION.padEnd(SETTLEMENT_SEED_VERSION_LEN, " ")
|
|
62
|
+
);
|
|
63
|
+
var ORDER_SEED = new TextEncoder().encode("order");
|
|
64
|
+
function findOrderPda(programId, uid) {
|
|
65
|
+
return PublicKey.findProgramAddressSync([SETTLEMENT_SEED, uid, ORDER_SEED], programId);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/createOrderInstruction.ts
|
|
69
|
+
import { SystemProgram, TransactionInstruction } from "@solana/web3.js";
|
|
70
|
+
var CREATE_ORDER_DISCRIMINATOR = 2;
|
|
71
|
+
function buildCreateOrderInstruction(params) {
|
|
72
|
+
const intentBytes = encodeOrderIntent(params.intent);
|
|
73
|
+
const data = Buffer.alloc(1 + intentBytes.length);
|
|
74
|
+
data[0] = CREATE_ORDER_DISCRIMINATOR;
|
|
75
|
+
data.set(intentBytes, 1);
|
|
76
|
+
return new TransactionInstruction({
|
|
77
|
+
programId: params.programId,
|
|
78
|
+
keys: [
|
|
79
|
+
{ pubkey: params.owner, isSigner: true, isWritable: false },
|
|
80
|
+
{ pubkey: params.createdBy, isSigner: true, isWritable: true },
|
|
81
|
+
{ pubkey: params.orderPda, isSigner: false, isWritable: true },
|
|
82
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
|
|
83
|
+
],
|
|
84
|
+
data
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/jupiterApi.ts
|
|
89
|
+
var JUPITER_ORDER_ENDPOINT = "https://ultra-api.jup.ag/order";
|
|
90
|
+
var DEFAULT_CLIENT_PLATFORM = "cowswap";
|
|
91
|
+
var QUOTE_TIMEOUT_MS = 1e4;
|
|
92
|
+
var UNSIGNED_INTEGER_PATTERN = /^\d+$/;
|
|
93
|
+
var JupiterAPI = class {
|
|
94
|
+
async getOrder(request) {
|
|
95
|
+
const params = new URLSearchParams({
|
|
96
|
+
inputMint: request.inputMint,
|
|
97
|
+
outputMint: request.outputMint,
|
|
98
|
+
amount: request.amount,
|
|
99
|
+
swapMode: request.swapMode,
|
|
100
|
+
clientPlatform: request.clientPlatform ?? DEFAULT_CLIENT_PLATFORM
|
|
101
|
+
});
|
|
102
|
+
const controller = new AbortController();
|
|
103
|
+
const timeoutId = setTimeout(() => controller.abort(), QUOTE_TIMEOUT_MS);
|
|
104
|
+
try {
|
|
105
|
+
const response = await fetch(`${JUPITER_ORDER_ENDPOINT}?${params.toString()}`, { signal: controller.signal });
|
|
106
|
+
let body;
|
|
107
|
+
try {
|
|
108
|
+
body = await response.json();
|
|
109
|
+
} catch {
|
|
110
|
+
throw new Error(`Jupiter quote request failed (${response.status})`);
|
|
111
|
+
}
|
|
112
|
+
if (!response.ok) {
|
|
113
|
+
const message = isJupiterErrorResponse(body) ? body.error : `Jupiter quote request failed (${response.status})`;
|
|
114
|
+
throw new Error(message);
|
|
115
|
+
}
|
|
116
|
+
if (!isJupiterOrderResponse(body, request)) {
|
|
117
|
+
throw new Error("Jupiter quote response is malformed or does not match the requested pair");
|
|
118
|
+
}
|
|
119
|
+
return body;
|
|
120
|
+
} finally {
|
|
121
|
+
clearTimeout(timeoutId);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
function isJupiterErrorResponse(body) {
|
|
126
|
+
return typeof body === "object" && body !== null && typeof body.error === "string";
|
|
127
|
+
}
|
|
128
|
+
function isJupiterOrderResponse(body, request) {
|
|
129
|
+
if (typeof body !== "object" || body === null)
|
|
130
|
+
return false;
|
|
131
|
+
const candidate = body;
|
|
132
|
+
return candidate.inputMint === request.inputMint && candidate.outputMint === request.outputMint && candidate.swapMode === request.swapMode && typeof candidate.inAmount === "string" && UNSIGNED_INTEGER_PATTERN.test(candidate.inAmount) && typeof candidate.outAmount === "string" && UNSIGNED_INTEGER_PATTERN.test(candidate.outAmount) && typeof candidate.slippageBps === "number" && Number.isFinite(candidate.slippageBps);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/getSolanaQuote.ts
|
|
136
|
+
import { PublicKey as PublicKey3 } from "@solana/web3.js";
|
|
137
|
+
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
|
|
138
|
+
import { SOLANA_SETTLEMENT_PROGRAM_ID, SOLANA_SETTLEMENT_PROGRAM_ID_STAGING } from "@cowprotocol/sdk-config";
|
|
139
|
+
import { getQuoteAmountsAndCosts, OrderKind as OrderKind2 } from "@cowprotocol/sdk-order-book";
|
|
140
|
+
var DEFAULT_VALID_FOR_SECONDS = 30 * 60;
|
|
141
|
+
var ZERO_APP_DATA = new Uint8Array(32);
|
|
142
|
+
var jupiterApi = new JupiterAPI();
|
|
143
|
+
async function getSolanaQuote(params, options = {}) {
|
|
144
|
+
const {
|
|
145
|
+
ownerAddress,
|
|
146
|
+
receiverAddress,
|
|
147
|
+
sellTokenDecimals,
|
|
148
|
+
buyTokenDecimals,
|
|
149
|
+
amount,
|
|
150
|
+
kind,
|
|
151
|
+
partiallyFillable = false,
|
|
152
|
+
validForSeconds = DEFAULT_VALID_FOR_SECONDS,
|
|
153
|
+
sellTokenProgramId,
|
|
154
|
+
buyTokenProgramId
|
|
155
|
+
} = params;
|
|
156
|
+
if (!Number.isFinite(validForSeconds) || validForSeconds <= 0) {
|
|
157
|
+
throw new Error("validForSeconds must be a finite number greater than zero");
|
|
158
|
+
}
|
|
159
|
+
const owner = new PublicKey3(ownerAddress);
|
|
160
|
+
const receiver = new PublicKey3(receiverAddress);
|
|
161
|
+
const sellMint = new PublicKey3(params.sellTokenAddress);
|
|
162
|
+
const buyMint = new PublicKey3(params.buyTokenAddress);
|
|
163
|
+
const sellTokenProgram = sellTokenProgramId ? new PublicKey3(sellTokenProgramId) : void 0;
|
|
164
|
+
const buyTokenProgram = buyTokenProgramId ? new PublicKey3(buyTokenProgramId) : void 0;
|
|
165
|
+
const sellTokenAddress = sellMint.toBase58();
|
|
166
|
+
const buyTokenAddress = buyMint.toBase58();
|
|
167
|
+
const jupiterOrder = await jupiterApi.getOrder({
|
|
168
|
+
inputMint: sellTokenAddress,
|
|
169
|
+
outputMint: buyTokenAddress,
|
|
170
|
+
amount: amount.toString(),
|
|
171
|
+
swapMode: kind === OrderKind2.SELL ? "ExactIn" : "ExactOut"
|
|
172
|
+
});
|
|
173
|
+
const validTo = Math.floor(Date.now() / 1e3) + validForSeconds;
|
|
174
|
+
const orderParams = {
|
|
175
|
+
sellToken: sellTokenAddress,
|
|
176
|
+
buyToken: buyTokenAddress,
|
|
177
|
+
receiver: receiver.toBase58(),
|
|
178
|
+
sellAmount: jupiterOrder.inAmount,
|
|
179
|
+
buyAmount: jupiterOrder.outAmount,
|
|
180
|
+
validTo,
|
|
181
|
+
// TODO: fill appData when we know the format
|
|
182
|
+
appData: "{}",
|
|
183
|
+
// TODO: implement fees
|
|
184
|
+
feeAmount: "0",
|
|
185
|
+
gasAmount: "0",
|
|
186
|
+
gasPrice: "0",
|
|
187
|
+
sellTokenPrice: "0",
|
|
188
|
+
kind,
|
|
189
|
+
partiallyFillable
|
|
190
|
+
};
|
|
191
|
+
const amountsAndCosts = getQuoteAmountsAndCosts({
|
|
192
|
+
orderParams,
|
|
193
|
+
slippagePercentBps: jupiterOrder.slippageBps,
|
|
194
|
+
// TODO: implement fees
|
|
195
|
+
partnerFeeBps: 0,
|
|
196
|
+
protocolFeeBps: 0
|
|
197
|
+
});
|
|
198
|
+
const intent = {
|
|
199
|
+
owner,
|
|
200
|
+
buyTokenAccount: getAssociatedTokenAddressSync(buyMint, receiver, false, buyTokenProgram),
|
|
201
|
+
buyMint,
|
|
202
|
+
sellTokenAccount: getAssociatedTokenAddressSync(sellMint, owner, false, sellTokenProgram),
|
|
203
|
+
sellMint,
|
|
204
|
+
sellAmount: amountsAndCosts.amountsToSign.sellAmount,
|
|
205
|
+
buyAmount: amountsAndCosts.amountsToSign.buyAmount,
|
|
206
|
+
validTo,
|
|
207
|
+
kind,
|
|
208
|
+
partiallyFillable,
|
|
209
|
+
createdOnChain: true,
|
|
210
|
+
appData: ZERO_APP_DATA
|
|
211
|
+
};
|
|
212
|
+
const intentBytes = encodeOrderIntent(intent);
|
|
213
|
+
const uid = await hashOrderIntent(intentBytes);
|
|
214
|
+
const programId = new PublicKey3(
|
|
215
|
+
options.env === "staging" ? SOLANA_SETTLEMENT_PROGRAM_ID_STAGING : SOLANA_SETTLEMENT_PROGRAM_ID
|
|
216
|
+
);
|
|
217
|
+
const [orderPda] = findOrderPda(programId, uid);
|
|
218
|
+
const solanaQuote = {
|
|
219
|
+
intent,
|
|
220
|
+
intentBytes,
|
|
221
|
+
uid,
|
|
222
|
+
orderPda,
|
|
223
|
+
programId,
|
|
224
|
+
jupiterOrder,
|
|
225
|
+
buyTokenProgramId: buyTokenProgram
|
|
226
|
+
};
|
|
227
|
+
const quoteResponse = {
|
|
228
|
+
quote: orderParams,
|
|
229
|
+
from: owner.toBase58(),
|
|
230
|
+
expiration: new Date(intent.validTo * 1e3).toISOString(),
|
|
231
|
+
verified: false
|
|
232
|
+
};
|
|
233
|
+
const tradeParameters = {
|
|
234
|
+
kind,
|
|
235
|
+
owner: owner.toBase58(),
|
|
236
|
+
sellToken: sellTokenAddress,
|
|
237
|
+
sellTokenDecimals,
|
|
238
|
+
buyToken: buyTokenAddress,
|
|
239
|
+
buyTokenDecimals,
|
|
240
|
+
amount: amount.toString(),
|
|
241
|
+
receiver: receiver.toBase58(),
|
|
242
|
+
validFor: validForSeconds,
|
|
243
|
+
partiallyFillable: orderParams.partiallyFillable
|
|
244
|
+
};
|
|
245
|
+
const quoteResults = {
|
|
246
|
+
quoteResponse,
|
|
247
|
+
amountsAndCosts,
|
|
248
|
+
suggestedSlippageBps: jupiterOrder.slippageBps,
|
|
249
|
+
tradeParameters,
|
|
250
|
+
orderToSign: {},
|
|
251
|
+
appDataInfo: {},
|
|
252
|
+
orderTypedData: {}
|
|
253
|
+
};
|
|
254
|
+
return { quoteResults, solanaQuote };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// src/postSwapOrderFromQuote.ts
|
|
258
|
+
import { getAssociatedTokenAddressSync as getAssociatedTokenAddressSync2 } from "@solana/spl-token";
|
|
259
|
+
import { SigningScheme } from "@cowprotocol/sdk-order-book";
|
|
260
|
+
import { PublicKey as PublicKey4 } from "@solana/web3.js";
|
|
261
|
+
async function postSolanaSwapOrderFromQuote({ quoteResults, solanaQuote }, signAndSend, advancedSettings, signingStepManager) {
|
|
262
|
+
const intent = { ...solanaQuote.intent };
|
|
263
|
+
let uid = solanaQuote.uid;
|
|
264
|
+
let orderPda = solanaQuote.orderPda;
|
|
265
|
+
if (advancedSettings?.quoteRequest) {
|
|
266
|
+
const { validTo, receiver } = advancedSettings.quoteRequest;
|
|
267
|
+
if (receiver) {
|
|
268
|
+
intent.buyTokenAccount = getAssociatedTokenAddressSync2(
|
|
269
|
+
intent.buyMint,
|
|
270
|
+
new PublicKey4(receiver),
|
|
271
|
+
false,
|
|
272
|
+
solanaQuote.buyTokenProgramId
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
if (validTo)
|
|
276
|
+
intent.validTo = validTo;
|
|
277
|
+
if (receiver || validTo) {
|
|
278
|
+
const intentBytes = encodeOrderIntent(intent);
|
|
279
|
+
uid = await hashOrderIntent(intentBytes);
|
|
280
|
+
[orderPda] = findOrderPda(solanaQuote.programId, uid);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const instruction = buildCreateOrderInstruction({
|
|
284
|
+
programId: solanaQuote.programId,
|
|
285
|
+
owner: intent.owner,
|
|
286
|
+
createdBy: intent.owner,
|
|
287
|
+
orderPda,
|
|
288
|
+
intent
|
|
289
|
+
});
|
|
290
|
+
await signingStepManager?.beforeOrderSign?.();
|
|
291
|
+
const { signature } = await signAndSend(instruction);
|
|
292
|
+
await signingStepManager?.afterOrderSign?.();
|
|
293
|
+
const orderToSign = quoteResults.orderToSign;
|
|
294
|
+
return {
|
|
295
|
+
orderId: toHex(uid),
|
|
296
|
+
txHash: signature,
|
|
297
|
+
signature,
|
|
298
|
+
signingScheme: SigningScheme.PRESIGN,
|
|
299
|
+
orderToSign
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/solanaTradingSdk.ts
|
|
304
|
+
var SolanaTradingSdk = class {
|
|
305
|
+
constructor(options) {
|
|
306
|
+
this.options = options;
|
|
307
|
+
}
|
|
308
|
+
async getQuote(params) {
|
|
309
|
+
const quote = await getSolanaQuote(params, { env: this.options.env });
|
|
310
|
+
return {
|
|
311
|
+
quoteResults: quote.quoteResults,
|
|
312
|
+
postSwapOrderFromQuote: (advancedSettings, signingStepManager) => postSolanaSwapOrderFromQuote(quote, this.options.signAndSend, advancedSettings, signingStepManager)
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
export {
|
|
317
|
+
ENCODED_ORDER_INTENT_SIZE,
|
|
318
|
+
JupiterAPI,
|
|
319
|
+
ORDER_SEED,
|
|
320
|
+
SETTLEMENT_SEED,
|
|
321
|
+
SolanaTradingSdk,
|
|
322
|
+
buildCreateOrderInstruction,
|
|
323
|
+
encodeOrderIntent,
|
|
324
|
+
findOrderPda,
|
|
325
|
+
getSolanaQuote,
|
|
326
|
+
hashOrderIntent,
|
|
327
|
+
postSolanaSwapOrderFromQuote,
|
|
328
|
+
toHex
|
|
329
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cowprotocol/sdk-trading-solana",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CowProtocol Solana trading: Jupiter-sourced quotes and on-chain CreateOrder posting against the Solana settlement program",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/cowprotocol/cow-sdk.git",
|
|
8
|
+
"directory": "packages/sdk-trading-solana"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"require": "./dist/index.js",
|
|
14
|
+
"import": "./dist/index.mjs",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"module": "./dist/index.mjs",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist/**"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/jest": "^29.5.12",
|
|
31
|
+
"@types/node": "^20.17.31",
|
|
32
|
+
"tsup": "^7.2.0",
|
|
33
|
+
"typescript": "^5.2.2",
|
|
34
|
+
"jest": "^29.7.0",
|
|
35
|
+
"jest-fetch-mock": "^3.0.3",
|
|
36
|
+
"ts-jest": "^29.0.0",
|
|
37
|
+
"@cow-sdk/typescript-config": "0.0.0-beta.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@solana/spl-token": "0.4.14",
|
|
41
|
+
"@solana/web3.js": "1.98.4",
|
|
42
|
+
"@cowprotocol/sdk-config": "2.4.0",
|
|
43
|
+
"@cowprotocol/sdk-order-book": "4.0.2",
|
|
44
|
+
"@cowprotocol/sdk-trading": "2.3.0"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
48
|
+
"lint": "eslint src/**/*.ts",
|
|
49
|
+
"test": "jest",
|
|
50
|
+
"test:watch": "jest --watch",
|
|
51
|
+
"test:coverage": "jest --coverage",
|
|
52
|
+
"coverage:badges": "istanbul-badges-readme --exitCode=1",
|
|
53
|
+
"test:coverage:html": "jest --silent=false --coverage --coverageReporters html",
|
|
54
|
+
"typecheck": "tsc --noEmit",
|
|
55
|
+
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
|
56
|
+
}
|
|
57
|
+
}
|