@keelcodes/sdk 2.3.2 → 2.5.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/dist/aa/index.d.ts +1 -0
- package/dist/aa/index.js +1 -0
- package/dist/aa/x402-solana.d.ts +99 -0
- package/dist/aa/x402-solana.js +171 -0
- package/dist/http.d.ts +1 -1
- package/dist/http.js +2 -2
- package/dist/wallet/index.d.ts +2 -0
- package/dist/wallet/index.js +2 -0
- package/dist/wallet/solana-dex-provider.d.ts +56 -0
- package/dist/wallet/solana-dex-provider.js +94 -0
- package/dist/wallet/solana-dex-types.d.ts +160 -0
- package/dist/wallet/solana-dex-types.js +2 -0
- package/dist/wallet/solana-execution-types.d.ts +5 -0
- package/dist/wallet/wallet-api.d.ts +13 -0
- package/dist/wallet/wallet-api.js +60 -0
- package/package.json +1 -1
package/dist/aa/index.d.ts
CHANGED
package/dist/aa/index.js
CHANGED
|
@@ -26,5 +26,6 @@ __exportStar(require("./userop"), exports);
|
|
|
26
26
|
__exportStar(require("./kernel-account"), exports);
|
|
27
27
|
__exportStar(require("./keel-session"), exports);
|
|
28
28
|
__exportStar(require("./x402"), exports);
|
|
29
|
+
__exportStar(require("./x402-solana"), exports);
|
|
29
30
|
__exportStar(require("./x402-session"), exports);
|
|
30
31
|
__exportStar(require("./bundler"), exports);
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { X402Accept, X402PaymentRequired, X402ResourceInfo } from './x402';
|
|
2
|
+
/** CAIP-2 Solana network 前缀(如 `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1`)。 */
|
|
3
|
+
export declare const SOLANA_NETWORK_PREFIX = "solana:";
|
|
4
|
+
/** Solana x402 签名域分隔标签。 */
|
|
5
|
+
export declare const SOLANA_X402_DOMAIN = "x402-solana-v2";
|
|
6
|
+
/** Solana 原生资产标签(与账本 `asset` 列一致,金额单位为 lamports)。 */
|
|
7
|
+
export declare const SOLANA_X402_ASSET = "sol";
|
|
8
|
+
export interface X402SolanaPaymentPayload {
|
|
9
|
+
x402Version: 2;
|
|
10
|
+
resource?: X402ResourceInfo;
|
|
11
|
+
accepted: X402Accept;
|
|
12
|
+
payload: {
|
|
13
|
+
method: string;
|
|
14
|
+
url: string;
|
|
15
|
+
/** 重放保护随机串(字符串即可;缺省由 txHash + 时间派生) */
|
|
16
|
+
salt: string;
|
|
17
|
+
/** 链上付款交易签名(base58) */
|
|
18
|
+
txHash: string;
|
|
19
|
+
/** 付款人公钥(base58) */
|
|
20
|
+
payer: string;
|
|
21
|
+
};
|
|
22
|
+
extensions?: Record<string, unknown>;
|
|
23
|
+
/** base58(ed25519 signature, 64B) */
|
|
24
|
+
signature: string;
|
|
25
|
+
}
|
|
26
|
+
/** 链上付款参数:向 `to` 转 `lamports`,原生 SOL。 */
|
|
27
|
+
export interface X402SolanaPayment {
|
|
28
|
+
to: string;
|
|
29
|
+
lamports: bigint;
|
|
30
|
+
asset: typeof SOLANA_X402_ASSET;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Solana 会话签名器:只需公钥 + 对原始消息签名。
|
|
34
|
+
* 私钥始终留在客户端(session key / 智能账户),SDK 不接触。
|
|
35
|
+
*/
|
|
36
|
+
export interface X402SolanaSigner {
|
|
37
|
+
/** base58 公钥(= payload.payer) */
|
|
38
|
+
publicKey: string;
|
|
39
|
+
/** 返回 64B 签名:Uint8Array 或 base58 字符串 */
|
|
40
|
+
signMessage(message: Uint8Array): Promise<Uint8Array | string>;
|
|
41
|
+
}
|
|
42
|
+
export interface PayX402WithSolanaParams {
|
|
43
|
+
challenge: X402PaymentRequired;
|
|
44
|
+
/** 不传则自动选第一个 Solana accept */
|
|
45
|
+
accept?: X402Accept;
|
|
46
|
+
/** 发起链上转账并返回交易签名(base58) */
|
|
47
|
+
sendPayment: (payment: X402SolanaPayment) => Promise<{
|
|
48
|
+
signature: string;
|
|
49
|
+
payer?: string;
|
|
50
|
+
}>;
|
|
51
|
+
signer: X402SolanaSigner;
|
|
52
|
+
/** 等交易确认(建议传入,避免服务端校验时还查不到) */
|
|
53
|
+
waitForConfirmation?: (signature: string) => Promise<unknown>;
|
|
54
|
+
method?: string;
|
|
55
|
+
url?: string;
|
|
56
|
+
salt?: string;
|
|
57
|
+
}
|
|
58
|
+
export interface PayX402WithSolanaResult {
|
|
59
|
+
/** 可直接写入 `PAYMENT-SIGNATURE` 头 */
|
|
60
|
+
header: string;
|
|
61
|
+
payload: X402SolanaPaymentPayload;
|
|
62
|
+
/** 链上付款交易签名(= payload.payload.txHash) */
|
|
63
|
+
txHash: string;
|
|
64
|
+
payment: X402SolanaPayment;
|
|
65
|
+
accept: X402Accept;
|
|
66
|
+
}
|
|
67
|
+
/** base58 编码(仅用于把 64B ed25519 签名转成 wire 形态)。 */
|
|
68
|
+
export declare function encodeBase58(bytes: Uint8Array): string;
|
|
69
|
+
export declare function isSolanaAccept(accept: X402Accept): boolean;
|
|
70
|
+
/** 选 Solana accept:优先 exact;可按 network 精确过滤。 */
|
|
71
|
+
export declare function selectSolanaAccept(challenge: X402PaymentRequired, opts?: {
|
|
72
|
+
network?: string;
|
|
73
|
+
}): X402Accept;
|
|
74
|
+
/** 按 accept 组装转账参数(原生 SOL)。 */
|
|
75
|
+
export declare function buildSolanaX402Payment(accept: X402Accept): X402SolanaPayment;
|
|
76
|
+
/** 构造待签消息(必须与 payments `buildSolanaPaymentMessage` 逐字节一致)。 */
|
|
77
|
+
export declare function buildSolanaX402Message(payload: X402SolanaPaymentPayload): Uint8Array;
|
|
78
|
+
/** 由链上付款结果构造未签名的 payload。 */
|
|
79
|
+
export declare function buildSolanaX402Payload(args: {
|
|
80
|
+
challenge: X402PaymentRequired;
|
|
81
|
+
accepted: X402Accept;
|
|
82
|
+
txHash: string;
|
|
83
|
+
payer: string;
|
|
84
|
+
method?: string;
|
|
85
|
+
url?: string;
|
|
86
|
+
salt?: string;
|
|
87
|
+
}): X402SolanaPaymentPayload;
|
|
88
|
+
/**
|
|
89
|
+
* 用 Solana 会话密钥签 x402 payload,返回 base58 签名。
|
|
90
|
+
* 写入 `payload.signature` 后即可 base64 成 `PAYMENT-SIGNATURE`。
|
|
91
|
+
*/
|
|
92
|
+
export declare function signX402WithSolanaSession(payload: X402SolanaPaymentPayload, signer: X402SolanaSigner): Promise<string>;
|
|
93
|
+
/** 把已签名的 payload 编码成 `PAYMENT-SIGNATURE` 头。 */
|
|
94
|
+
export declare function encodeSolanaPaymentSignature(payload: X402SolanaPaymentPayload): string;
|
|
95
|
+
/**
|
|
96
|
+
* 一次完成「链上转账 → 等确认 → x402 凭证签名」。
|
|
97
|
+
* 转账由调用方注入(可用 aa-relay `/solana/submit` 广播,gas 可垫付)。
|
|
98
|
+
*/
|
|
99
|
+
export declare function payX402WithSolana(params: PayX402WithSolanaParams): Promise<PayX402WithSolanaResult>;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// x402 v2 · Solana(verifyOnly)客户端
|
|
4
|
+
//
|
|
5
|
+
// 结算模型与 EVM 一致:客户端自己广播 SOL 转账,服务端只做「验签 + 链上校验 +
|
|
6
|
+
// 入账」,没有 facilitator `/settle`。
|
|
7
|
+
//
|
|
8
|
+
// 与 payments 服务端(`protocol/x402-v2.ts`)逐字节对齐:
|
|
9
|
+
// message = ["x402-solana-v2", scheme, network, amount, asset, payTo,
|
|
10
|
+
// method, url, salt, txHash].join("\n")
|
|
11
|
+
// header = base64(JSON({ x402Version, resource, accepted,
|
|
12
|
+
// payload:{method,url,salt,txHash,payer}, signature }))
|
|
13
|
+
//
|
|
14
|
+
// EVM 用 EIP-712 把「accepted + payload」绑定到付款人;SVM 没有等价的结构化
|
|
15
|
+
// 签名,改用 ed25519 对上面这条确定性消息签名,payer 由 `payload.payer` 显式给出
|
|
16
|
+
// (ed25519 无法从签名恢复公钥),服务端再用该公钥验签 + 校验链上同址。
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.SOLANA_X402_ASSET = exports.SOLANA_X402_DOMAIN = exports.SOLANA_NETWORK_PREFIX = void 0;
|
|
20
|
+
exports.encodeBase58 = encodeBase58;
|
|
21
|
+
exports.isSolanaAccept = isSolanaAccept;
|
|
22
|
+
exports.selectSolanaAccept = selectSolanaAccept;
|
|
23
|
+
exports.buildSolanaX402Payment = buildSolanaX402Payment;
|
|
24
|
+
exports.buildSolanaX402Message = buildSolanaX402Message;
|
|
25
|
+
exports.buildSolanaX402Payload = buildSolanaX402Payload;
|
|
26
|
+
exports.signX402WithSolanaSession = signX402WithSolanaSession;
|
|
27
|
+
exports.encodeSolanaPaymentSignature = encodeSolanaPaymentSignature;
|
|
28
|
+
exports.payX402WithSolana = payX402WithSolana;
|
|
29
|
+
const x402_1 = require("./x402");
|
|
30
|
+
/** CAIP-2 Solana network 前缀(如 `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1`)。 */
|
|
31
|
+
exports.SOLANA_NETWORK_PREFIX = 'solana:';
|
|
32
|
+
/** Solana x402 签名域分隔标签。 */
|
|
33
|
+
exports.SOLANA_X402_DOMAIN = 'x402-solana-v2';
|
|
34
|
+
/** Solana 原生资产标签(与账本 `asset` 列一致,金额单位为 lamports)。 */
|
|
35
|
+
exports.SOLANA_X402_ASSET = 'sol';
|
|
36
|
+
const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
37
|
+
/** base58 编码(仅用于把 64B ed25519 签名转成 wire 形态)。 */
|
|
38
|
+
function encodeBase58(bytes) {
|
|
39
|
+
let zeros = 0;
|
|
40
|
+
while (zeros < bytes.length && bytes[zeros] === 0)
|
|
41
|
+
zeros++;
|
|
42
|
+
const digits = [];
|
|
43
|
+
for (let i = zeros; i < bytes.length; i++) {
|
|
44
|
+
let carry = bytes[i];
|
|
45
|
+
for (let j = 0; j < digits.length; j++) {
|
|
46
|
+
carry += digits[j] << 8;
|
|
47
|
+
digits[j] = carry % 58;
|
|
48
|
+
carry = (carry / 58) | 0;
|
|
49
|
+
}
|
|
50
|
+
while (carry > 0) {
|
|
51
|
+
digits.push(carry % 58);
|
|
52
|
+
carry = (carry / 58) | 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
let out = '1'.repeat(zeros);
|
|
56
|
+
for (let i = digits.length - 1; i >= 0; i--)
|
|
57
|
+
out += BASE58_ALPHABET[digits[i]];
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
function isSolanaAccept(accept) {
|
|
61
|
+
return (String(accept.network ?? '').startsWith(exports.SOLANA_NETWORK_PREFIX) &&
|
|
62
|
+
String(accept.asset ?? '').toLowerCase() === exports.SOLANA_X402_ASSET);
|
|
63
|
+
}
|
|
64
|
+
/** 选 Solana accept:优先 exact;可按 network 精确过滤。 */
|
|
65
|
+
function selectSolanaAccept(challenge, opts = {}) {
|
|
66
|
+
const matches = challenge.accepts.filter((a) => isSolanaAccept(a) && (opts.network === undefined || a.network === opts.network));
|
|
67
|
+
const pick = matches.find((a) => a.scheme === 'exact') ?? matches[0];
|
|
68
|
+
if (!pick)
|
|
69
|
+
throw new Error('[keel-sdk] no Solana accept in this x402 challenge');
|
|
70
|
+
return pick;
|
|
71
|
+
}
|
|
72
|
+
/** 按 accept 组装转账参数(原生 SOL)。 */
|
|
73
|
+
function buildSolanaX402Payment(accept) {
|
|
74
|
+
if (!isSolanaAccept(accept)) {
|
|
75
|
+
throw new Error(`[keel-sdk] accept is not Solana native: ${accept.network}/${accept.asset}`);
|
|
76
|
+
}
|
|
77
|
+
const lamports = BigInt(accept.amount);
|
|
78
|
+
if (lamports <= 0n)
|
|
79
|
+
throw new Error('[keel-sdk] x402 amount must be > 0');
|
|
80
|
+
if (!/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(accept.payTo)) {
|
|
81
|
+
throw new Error(`[keel-sdk] invalid Solana payTo: ${accept.payTo}`);
|
|
82
|
+
}
|
|
83
|
+
return { to: accept.payTo, lamports, asset: exports.SOLANA_X402_ASSET };
|
|
84
|
+
}
|
|
85
|
+
/** 构造待签消息(必须与 payments `buildSolanaPaymentMessage` 逐字节一致)。 */
|
|
86
|
+
function buildSolanaX402Message(payload) {
|
|
87
|
+
const { accepted } = payload;
|
|
88
|
+
return new TextEncoder().encode([
|
|
89
|
+
exports.SOLANA_X402_DOMAIN,
|
|
90
|
+
accepted.scheme,
|
|
91
|
+
accepted.network,
|
|
92
|
+
accepted.amount,
|
|
93
|
+
accepted.asset,
|
|
94
|
+
accepted.payTo,
|
|
95
|
+
payload.payload.method,
|
|
96
|
+
payload.payload.url,
|
|
97
|
+
payload.payload.salt,
|
|
98
|
+
payload.payload.txHash,
|
|
99
|
+
].join('\n'));
|
|
100
|
+
}
|
|
101
|
+
/** 由链上付款结果构造未签名的 payload。 */
|
|
102
|
+
function buildSolanaX402Payload(args) {
|
|
103
|
+
return {
|
|
104
|
+
x402Version: 2,
|
|
105
|
+
resource: args.challenge.resource,
|
|
106
|
+
accepted: args.accepted,
|
|
107
|
+
payload: {
|
|
108
|
+
method: args.method ?? 'GET',
|
|
109
|
+
url: args.url ?? args.challenge.resource?.url ?? '',
|
|
110
|
+
salt: args.salt ?? `${args.txHash}:${Date.now()}:${Math.random().toString(36).slice(2)}`,
|
|
111
|
+
txHash: args.txHash,
|
|
112
|
+
payer: args.payer,
|
|
113
|
+
},
|
|
114
|
+
signature: '',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 用 Solana 会话密钥签 x402 payload,返回 base58 签名。
|
|
119
|
+
* 写入 `payload.signature` 后即可 base64 成 `PAYMENT-SIGNATURE`。
|
|
120
|
+
*/
|
|
121
|
+
async function signX402WithSolanaSession(payload, signer) {
|
|
122
|
+
if (payload.payload.payer !== signer.publicKey) {
|
|
123
|
+
throw new Error('[keel-sdk] payload.payer does not match the signing publicKey');
|
|
124
|
+
}
|
|
125
|
+
const raw = await signer.signMessage(buildSolanaX402Message(payload));
|
|
126
|
+
const sig = typeof raw === 'string' ? raw : encodeBase58(raw);
|
|
127
|
+
if (!/^[1-9A-HJ-NP-Za-km-z]{64,88}$/.test(sig)) {
|
|
128
|
+
throw new Error('[keel-sdk] signMessage must return a 64-byte ed25519 signature');
|
|
129
|
+
}
|
|
130
|
+
return sig;
|
|
131
|
+
}
|
|
132
|
+
/** 把已签名的 payload 编码成 `PAYMENT-SIGNATURE` 头。 */
|
|
133
|
+
function encodeSolanaPaymentSignature(payload) {
|
|
134
|
+
return (0, x402_1.toBase64)(JSON.stringify(payload));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 一次完成「链上转账 → 等确认 → x402 凭证签名」。
|
|
138
|
+
* 转账由调用方注入(可用 aa-relay `/solana/submit` 广播,gas 可垫付)。
|
|
139
|
+
*/
|
|
140
|
+
async function payX402WithSolana(params) {
|
|
141
|
+
const accept = params.accept ?? selectSolanaAccept(params.challenge);
|
|
142
|
+
const payment = buildSolanaX402Payment(accept);
|
|
143
|
+
const sent = await params.sendPayment(payment);
|
|
144
|
+
const txHash = String(sent?.signature ?? '').trim();
|
|
145
|
+
if (!/^[1-9A-HJ-NP-Za-km-z]{64,88}$/.test(txHash)) {
|
|
146
|
+
throw new Error(`[keel-sdk] sendPayment must return a base58 tx signature, got ${txHash}`);
|
|
147
|
+
}
|
|
148
|
+
const payer = String(sent.payer ?? params.signer.publicKey);
|
|
149
|
+
if (payer !== params.signer.publicKey) {
|
|
150
|
+
throw new Error('[keel-sdk] sendPayment payer differs from the x402 signing key');
|
|
151
|
+
}
|
|
152
|
+
if (params.waitForConfirmation)
|
|
153
|
+
await params.waitForConfirmation(txHash);
|
|
154
|
+
const unsigned = buildSolanaX402Payload({
|
|
155
|
+
challenge: params.challenge,
|
|
156
|
+
accepted: accept,
|
|
157
|
+
txHash,
|
|
158
|
+
payer,
|
|
159
|
+
method: params.method,
|
|
160
|
+
url: params.url,
|
|
161
|
+
salt: params.salt,
|
|
162
|
+
});
|
|
163
|
+
unsigned.signature = await signX402WithSolanaSession(unsigned, params.signer);
|
|
164
|
+
return {
|
|
165
|
+
header: encodeSolanaPaymentSignature(unsigned),
|
|
166
|
+
payload: unsigned,
|
|
167
|
+
txHash,
|
|
168
|
+
payment,
|
|
169
|
+
accept,
|
|
170
|
+
};
|
|
171
|
+
}
|
package/dist/http.d.ts
CHANGED
package/dist/http.js
CHANGED
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.HttpClient = exports.SDK_VERSION = void 0;
|
|
7
7
|
const errors_1 = require("./errors");
|
|
8
8
|
/** SDK 版本(契约 §5,随包版本同步)。 */
|
|
9
|
-
exports.SDK_VERSION = '2.
|
|
9
|
+
exports.SDK_VERSION = '2.5.0';
|
|
10
10
|
/**
|
|
11
11
|
* 钱包签名的复用时长(ms):首次签名后在该窗口内复用,避免每个请求都触发钱包弹窗。
|
|
12
12
|
* 服务端会话 TTL 为 24h,此处取 1h 留足余量。
|
|
@@ -88,7 +88,7 @@ class HttpClient {
|
|
|
88
88
|
return { ...this.headers, ...this.walletToken.headers };
|
|
89
89
|
}
|
|
90
90
|
const timestamp = String(now);
|
|
91
|
-
const signature = await this.wallet.sign(`
|
|
91
|
+
const signature = await this.wallet.sign(`Keel auth: ${timestamp}`);
|
|
92
92
|
const headers = {
|
|
93
93
|
'x-wallet-address': this.wallet.address.toLowerCase(),
|
|
94
94
|
'x-wallet-timestamp': timestamp,
|
package/dist/wallet/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export * from './solana-account-types';
|
|
3
3
|
export * from './solana-execution-types';
|
|
4
|
+
export * from './solana-dex-types';
|
|
5
|
+
export * from './solana-dex-provider';
|
|
4
6
|
export * from './mpc-types';
|
|
5
7
|
export * from './wallet-api';
|
|
6
8
|
export * from '../aa';
|
package/dist/wallet/index.js
CHANGED
|
@@ -20,6 +20,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
20
20
|
__exportStar(require("./types"), exports);
|
|
21
21
|
__exportStar(require("./solana-account-types"), exports);
|
|
22
22
|
__exportStar(require("./solana-execution-types"), exports);
|
|
23
|
+
__exportStar(require("./solana-dex-types"), exports);
|
|
24
|
+
__exportStar(require("./solana-dex-provider"), exports);
|
|
23
25
|
__exportStar(require("./mpc-types"), exports);
|
|
24
26
|
__exportStar(require("./wallet-api"), exports);
|
|
25
27
|
__exportStar(require("../aa"), exports);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { SolanaDexQuoteParams } from './solana-dex-types';
|
|
2
|
+
export interface DexInstructionAccount {
|
|
3
|
+
pubkey: string;
|
|
4
|
+
isSigner?: boolean;
|
|
5
|
+
isWritable?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** 归一化后的单条内层指令(data 为 base64) */
|
|
8
|
+
export interface DexInstruction {
|
|
9
|
+
programId: string;
|
|
10
|
+
accounts: DexInstructionAccount[];
|
|
11
|
+
data: string;
|
|
12
|
+
}
|
|
13
|
+
/** 统一报价(字段口径与 relay `/dex/swap` 的响应一致) */
|
|
14
|
+
export interface DexProviderQuote {
|
|
15
|
+
inAmount: string;
|
|
16
|
+
outAmount: string;
|
|
17
|
+
otherAmountThreshold: string;
|
|
18
|
+
priceImpactPct: string;
|
|
19
|
+
slippageBps: number;
|
|
20
|
+
/** provider 原始响应,供 `swapInstructions` 复用(不会发给 relay) */
|
|
21
|
+
raw?: unknown;
|
|
22
|
+
}
|
|
23
|
+
export interface DexProviderSwapRequest {
|
|
24
|
+
inputMint: string;
|
|
25
|
+
outputMint: string;
|
|
26
|
+
amount: string | number;
|
|
27
|
+
slippageBps?: number;
|
|
28
|
+
/** swap 的 user(= Squads vault PDA) */
|
|
29
|
+
userPublicKey: string;
|
|
30
|
+
/** `quote()` 返回的 `raw` */
|
|
31
|
+
quote?: unknown;
|
|
32
|
+
}
|
|
33
|
+
export interface DexProviderSwap {
|
|
34
|
+
instructions: DexInstruction[];
|
|
35
|
+
addressLookupTableAddresses?: string[];
|
|
36
|
+
}
|
|
37
|
+
export interface DexProvider {
|
|
38
|
+
readonly name: string;
|
|
39
|
+
quote(req: SolanaDexQuoteParams): Promise<DexProviderQuote>;
|
|
40
|
+
swapInstructions(req: DexProviderSwapRequest): Promise<DexProviderSwap>;
|
|
41
|
+
}
|
|
42
|
+
export interface JupiterProviderOptions {
|
|
43
|
+
/** 默认 https://lite-api.jup.ag */
|
|
44
|
+
baseUrl?: string;
|
|
45
|
+
/** Jupiter Pro(或自建网关)的 key,非空时作为 `x-api-key` 头 */
|
|
46
|
+
apiKey?: string;
|
|
47
|
+
/** 便于测试注入;默认全局 fetch */
|
|
48
|
+
fetchImpl?: typeof fetch;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Jupiter(含任意 Jupiter 兼容端点)provider。
|
|
52
|
+
* 只需要 base URL 与可选 key,B 端即可用自己的额度/网关取价取指令。
|
|
53
|
+
*/
|
|
54
|
+
export declare function jupiterProvider(opts?: JupiterProviderOptions): DexProvider;
|
|
55
|
+
/** B 端自定义 provider 的便捷校验:确保 relay 需要的字段齐全。 */
|
|
56
|
+
export declare function assertDexProvider(provider: DexProvider): void;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jupiterProvider = jupiterProvider;
|
|
4
|
+
exports.assertDexProvider = assertDexProvider;
|
|
5
|
+
/** Jupiter `/swap/v1/swap-instructions` → 有顺序的指令列表(与 relay 内部口径一致)。 */
|
|
6
|
+
function collectJupiterInstructions(body) {
|
|
7
|
+
const out = [];
|
|
8
|
+
const push = (ix) => {
|
|
9
|
+
if (ix && ix.programId)
|
|
10
|
+
out.push({ programId: ix.programId, accounts: ix.accounts ?? [], data: ix.data });
|
|
11
|
+
};
|
|
12
|
+
push(body?.tokenLedgerInstruction);
|
|
13
|
+
for (const ix of body?.computeBudgetInstructions ?? [])
|
|
14
|
+
push(ix);
|
|
15
|
+
for (const ix of body?.setupInstructions ?? [])
|
|
16
|
+
push(ix);
|
|
17
|
+
push(body?.swapInstruction);
|
|
18
|
+
push(body?.cleanupInstruction);
|
|
19
|
+
for (const ix of body?.otherInstructions ?? [])
|
|
20
|
+
push(ix);
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Jupiter(含任意 Jupiter 兼容端点)provider。
|
|
25
|
+
* 只需要 base URL 与可选 key,B 端即可用自己的额度/网关取价取指令。
|
|
26
|
+
*/
|
|
27
|
+
function jupiterProvider(opts = {}) {
|
|
28
|
+
const baseUrl = (opts.baseUrl ?? 'https://lite-api.jup.ag').replace(/\/+$/, '');
|
|
29
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
30
|
+
const headers = () => ({
|
|
31
|
+
'content-type': 'application/json',
|
|
32
|
+
...(opts.apiKey ? { 'x-api-key': opts.apiKey } : {}),
|
|
33
|
+
});
|
|
34
|
+
async function readJson(res, path) {
|
|
35
|
+
const text = await res.text();
|
|
36
|
+
if (!res.ok)
|
|
37
|
+
throw new Error(`jupiter ${path} failed: HTTP ${res.status} ${text.slice(0, 200)}`);
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(text);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
throw new Error(`jupiter ${path} returned invalid JSON`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
name: 'jupiter',
|
|
47
|
+
async quote(req) {
|
|
48
|
+
const qs = new URLSearchParams({
|
|
49
|
+
inputMint: req.inputMint,
|
|
50
|
+
outputMint: req.outputMint,
|
|
51
|
+
amount: String(req.amount),
|
|
52
|
+
...(req.slippageBps === undefined ? {} : { slippageBps: String(req.slippageBps) }),
|
|
53
|
+
});
|
|
54
|
+
const path = `/swap/v1/quote?${qs.toString()}`;
|
|
55
|
+
const raw = await readJson(await doFetch(`${baseUrl}${path}`, { headers: headers() }), path);
|
|
56
|
+
return {
|
|
57
|
+
inAmount: String(raw.inAmount),
|
|
58
|
+
outAmount: String(raw.outAmount),
|
|
59
|
+
otherAmountThreshold: String(raw.otherAmountThreshold ?? ''),
|
|
60
|
+
priceImpactPct: String(raw.priceImpactPct ?? '0'),
|
|
61
|
+
slippageBps: Number(raw.slippageBps ?? req.slippageBps ?? 50),
|
|
62
|
+
raw,
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
async swapInstructions(req) {
|
|
66
|
+
const path = '/swap/v1/swap-instructions';
|
|
67
|
+
const body = {
|
|
68
|
+
quoteResponse: req.quote,
|
|
69
|
+
userPublicKey: req.userPublicKey,
|
|
70
|
+
wrapAndUnwrapSol: true,
|
|
71
|
+
dynamicComputeUnitLimit: true,
|
|
72
|
+
};
|
|
73
|
+
const raw = await readJson(await doFetch(`${baseUrl}${path}`, {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
headers: headers(),
|
|
76
|
+
body: JSON.stringify(body),
|
|
77
|
+
}), path);
|
|
78
|
+
if (raw?.error)
|
|
79
|
+
throw new Error(`jupiter swap-instructions error: ${String(raw.error)}`);
|
|
80
|
+
return {
|
|
81
|
+
instructions: collectJupiterInstructions(raw),
|
|
82
|
+
addressLookupTableAddresses: Array.isArray(raw?.addressLookupTableAddresses)
|
|
83
|
+
? raw.addressLookupTableAddresses
|
|
84
|
+
: [],
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** B 端自定义 provider 的便捷校验:确保 relay 需要的字段齐全。 */
|
|
90
|
+
function assertDexProvider(provider) {
|
|
91
|
+
if (!provider || typeof provider.quote !== 'function' || typeof provider.swapInstructions !== 'function') {
|
|
92
|
+
throw new Error('dexProvider must implement quote() and swapInstructions()');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import type { SolanaSubmitResult } from './solana-execution-types';
|
|
2
|
+
import type { DexInstruction, DexProvider } from './solana-dex-provider';
|
|
3
|
+
/** DEX 报价(只读,不构建交易) */
|
|
4
|
+
export interface SolanaDexQuoteParams {
|
|
5
|
+
chain: string;
|
|
6
|
+
inputMint: string;
|
|
7
|
+
outputMint: string;
|
|
8
|
+
/** base units(十进制整数) */
|
|
9
|
+
amount: string | number;
|
|
10
|
+
slippageBps?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface SolanaDexQuoteResult {
|
|
13
|
+
chain: string;
|
|
14
|
+
/** Jupiter 原始报价(字段随聚合器版本变化,按需读取) */
|
|
15
|
+
quote: Record<string, unknown>;
|
|
16
|
+
inputMint: string;
|
|
17
|
+
inAmount: string;
|
|
18
|
+
outputMint: string;
|
|
19
|
+
outAmount: string;
|
|
20
|
+
otherAmountThreshold: string;
|
|
21
|
+
priceImpactPct: string;
|
|
22
|
+
slippageBps: number;
|
|
23
|
+
}
|
|
24
|
+
/** `ProgramInteraction` policy 安装入参(`/dex/policy` 与 `/dex/install` 通用) */
|
|
25
|
+
export interface SolanaDexPolicyParams {
|
|
26
|
+
chain: string;
|
|
27
|
+
blockhash: string;
|
|
28
|
+
feePayer: string;
|
|
29
|
+
creator: string;
|
|
30
|
+
settingsPda: string;
|
|
31
|
+
transactionIndex: string | number;
|
|
32
|
+
/** policy PDA 的 u64 seed(= 调用前链上 `settings.policySeed + 1`) */
|
|
33
|
+
seed: string | number;
|
|
34
|
+
/** 作为 policy 唯一签名者的 session key 公钥 */
|
|
35
|
+
sessionKey: string;
|
|
36
|
+
rentPayer?: string;
|
|
37
|
+
accountIndex?: number;
|
|
38
|
+
/** 缺省 `[1,2,4]`(Initiate | Vote | Execute) */
|
|
39
|
+
permissions?: number[];
|
|
40
|
+
spendingLimits?: Array<{
|
|
41
|
+
mint: string;
|
|
42
|
+
/** base units(十进制整数) */
|
|
43
|
+
maxPerPeriod: string | number;
|
|
44
|
+
period?: number;
|
|
45
|
+
start?: string | number;
|
|
46
|
+
expiration?: string | number;
|
|
47
|
+
}>;
|
|
48
|
+
/** 缺省 = relay 配置里的 program 白名单(Jupiter + 直连 DEX + 辅助 program) */
|
|
49
|
+
constraints?: Array<{
|
|
50
|
+
programId: string;
|
|
51
|
+
accountConstraints?: Array<{
|
|
52
|
+
accountIndex: number;
|
|
53
|
+
pubkeys: string[];
|
|
54
|
+
owner?: string;
|
|
55
|
+
}>;
|
|
56
|
+
dataConstraints?: Array<{
|
|
57
|
+
offset: string | number;
|
|
58
|
+
value?: string;
|
|
59
|
+
operator?: number;
|
|
60
|
+
}>;
|
|
61
|
+
}>;
|
|
62
|
+
memo?: string;
|
|
63
|
+
}
|
|
64
|
+
/** 安装计划中的一步(未签名交易,按 `steps` 顺序签名广播) */
|
|
65
|
+
export interface SolanaDexInstallStep {
|
|
66
|
+
step: 'createSettingsTransaction' | 'createProposal' | 'approveProposal' | 'executeSettingsTransaction';
|
|
67
|
+
/** 需要本地签名的公钥(base58) */
|
|
68
|
+
signers: string[];
|
|
69
|
+
instructionCount: number;
|
|
70
|
+
/** base64(v0) */
|
|
71
|
+
transaction: string;
|
|
72
|
+
}
|
|
73
|
+
export interface SolanaDexInstallResult {
|
|
74
|
+
chain: string;
|
|
75
|
+
policyPda: string;
|
|
76
|
+
/** 四步共用同一个 blockhash;过期未走完需重新调用 */
|
|
77
|
+
blockhash: string;
|
|
78
|
+
steps: SolanaDexInstallStep[];
|
|
79
|
+
}
|
|
80
|
+
export interface SolanaDexSwapParams {
|
|
81
|
+
chain: string;
|
|
82
|
+
blockhash: string;
|
|
83
|
+
feePayer: string;
|
|
84
|
+
/** policy PDA */
|
|
85
|
+
policy: string;
|
|
86
|
+
/** smart account(vault)PDA */
|
|
87
|
+
vaultPda: string;
|
|
88
|
+
sessionKey: string;
|
|
89
|
+
inputMint: string;
|
|
90
|
+
outputMint: string;
|
|
91
|
+
amount: string | number;
|
|
92
|
+
accountIndex?: number;
|
|
93
|
+
numSigners?: number;
|
|
94
|
+
slippageBps?: number;
|
|
95
|
+
wrapAndUnwrapSol?: boolean;
|
|
96
|
+
dynamicComputeUnitLimit?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* policy 创建时 `instructionsConstraints` 的顺序(programId 数组)。
|
|
99
|
+
* 缺省取 relay 配置白名单顺序;链上要求逐条指令都能映射到一条约束。
|
|
100
|
+
* 使用 `dexProvider`(external 模式)时**必须**给出,relay 以它作为 program 白名单。
|
|
101
|
+
*/
|
|
102
|
+
policyPrograms?: string[];
|
|
103
|
+
/**
|
|
104
|
+
* 自定义 DEX/聚合器 provider(2026-10-24)。给出时 SDK 先本地取价取指令,
|
|
105
|
+
* 再以 `provider: 'external'` 交给 relay 组装交易;URL/key 不经过 relay。
|
|
106
|
+
*/
|
|
107
|
+
dexProvider?: DexProvider;
|
|
108
|
+
/** external 模式的报价回显(不参与风控/计费);`dexProvider` 会自动填充 */
|
|
109
|
+
quote?: {
|
|
110
|
+
inAmount?: string;
|
|
111
|
+
outAmount?: string;
|
|
112
|
+
otherAmountThreshold?: string;
|
|
113
|
+
priceImpactPct?: string;
|
|
114
|
+
slippageBps?: number;
|
|
115
|
+
};
|
|
116
|
+
/** external 模式的归一化内层指令(`dexProvider` 会自动填充) */
|
|
117
|
+
swapInstructions?: DexInstruction[];
|
|
118
|
+
/** external 模式的地址查找表(`dexProvider` 会自动填充) */
|
|
119
|
+
addressLookupTableAddresses?: string[];
|
|
120
|
+
}
|
|
121
|
+
export interface SolanaDexSwapResult {
|
|
122
|
+
chain: string;
|
|
123
|
+
inAmount: string;
|
|
124
|
+
outAmount: string;
|
|
125
|
+
otherAmountThreshold: string;
|
|
126
|
+
priceImpactPct: string;
|
|
127
|
+
slippageBps: number;
|
|
128
|
+
innerInstructionCount: number;
|
|
129
|
+
instructionConstraintIndices: number[];
|
|
130
|
+
addressLookupTables: number;
|
|
131
|
+
transactionBytes: number;
|
|
132
|
+
/** 未签名交易(base64 v0);签名后经 `submit` 广播才会计费 */
|
|
133
|
+
transaction: string;
|
|
134
|
+
instructionCount?: number;
|
|
135
|
+
/** 计费提示:签名后发到该端点,relay 按 fee payer 收固定服务费 */
|
|
136
|
+
billing?: {
|
|
137
|
+
endpoint: string;
|
|
138
|
+
feePayer: string;
|
|
139
|
+
feeLamports: string;
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* 本地签名回调:入参为未签名交易(base64 v0)与需要签名的公钥,
|
|
144
|
+
* 返回已签名的 base64 交易。SDK 不内置 Solana 依赖,由调用方用
|
|
145
|
+
* `@solana/web3.js`(`VersionedTransaction.deserialize` → `sign`)实现。
|
|
146
|
+
*/
|
|
147
|
+
export type SolanaTransactionSigner = (transaction: string, signers: string[]) => Promise<string> | string;
|
|
148
|
+
/** `dexSwapAndSubmit`:一次完成「取未签名交易 → 本地签名 → 经 relay 计费广播」。 */
|
|
149
|
+
export interface SolanaDexSwapAndSubmitParams extends SolanaDexSwapParams {
|
|
150
|
+
sign: SolanaTransactionSigner;
|
|
151
|
+
/**
|
|
152
|
+
* 垫付模式下的扣费主体(客户地址)。fee payer 是平台热钱包时必须给,
|
|
153
|
+
* relay 会把「固定服务费 + 实际垫付 lamports」扣在该地址账本上。
|
|
154
|
+
*/
|
|
155
|
+
payer?: string;
|
|
156
|
+
}
|
|
157
|
+
export interface SolanaDexSwapAndSubmitResult extends SolanaDexSwapResult {
|
|
158
|
+
/** relay `/submit` 的广播回执(计费发生在这里) */
|
|
159
|
+
submit: SolanaSubmitResult;
|
|
160
|
+
}
|
|
@@ -75,6 +75,11 @@ export interface SolanaRemoveSignerParams {
|
|
|
75
75
|
export interface SolanaSubmitParams {
|
|
76
76
|
chain: string;
|
|
77
77
|
transaction: string;
|
|
78
|
+
/**
|
|
79
|
+
* 垫付模式的扣费主体(客户 base58 地址)。仅当交易 fee payer 是平台热钱包时需要;
|
|
80
|
+
* relay 会把固定服务费 + 实际垫付 lamports 扣在该地址。
|
|
81
|
+
*/
|
|
82
|
+
payer?: string;
|
|
78
83
|
}
|
|
79
84
|
/** 构建端点返回的未签名交易(base64)。 */
|
|
80
85
|
export interface SolanaUnsignedTransaction {
|
|
@@ -3,6 +3,7 @@ import type { KeelEnvelope } from '../types';
|
|
|
3
3
|
import type { CreateWalletParams, WalletCreated, ImportWalletParams, SignParams, SignResult, SendParams, SendResult, ChainParams, ChainResult, WalletBalanceParams, WalletBalanceResult, SessionCreateParams, SessionCreated, PaymasterInfo, PaymasterVoucherParams, PaymasterVoucher } from './types';
|
|
4
4
|
import type { SolanaDeriveAccountParams, SolanaDerivedAccount, SolanaCreateAccountParams, SolanaInstallPolicyParams, SolanaCreatePolicyTransactionParams, SolanaCreateProposalParams, SolanaApproveProposalParams, SolanaExecutePolicyTransactionParams, SolanaRemovePolicyParams } from './solana-account-types';
|
|
5
5
|
import type { SolanaExecutePolicyPayloadParams, SolanaCloseSettingsParams, SolanaCloseTransactionParams, SolanaExecutePolicyParams, SolanaExecuteParams, SolanaRemoveSignerParams, SolanaSubmitParams, SolanaUnsignedTransaction, SolanaSubmitResult } from './solana-execution-types';
|
|
6
|
+
import type { SolanaDexQuoteParams, SolanaDexQuoteResult, SolanaDexPolicyParams, SolanaDexInstallResult, SolanaDexSwapParams, SolanaDexSwapResult, SolanaDexSwapAndSubmitParams, SolanaDexSwapAndSubmitResult } from './solana-dex-types';
|
|
6
7
|
import type { MpcSendCodeParams, MpcVerifyParams, MpcBalanceParams, MpcSignMessageParams, MpcSignTypedDataParams, MpcSendTransactionParams, MpcContractCallParams, MpcContractWriteParams, MpcGasEstimateParams, MpcRegisterParams, MpcRegisterResult, MpcSolanaSignParams, MpcSolanaSignResult, MpcSolanaSendParams, MpcSolanaSendResult } from './mpc-types';
|
|
7
8
|
export declare class WalletAPI {
|
|
8
9
|
private http;
|
|
@@ -171,6 +172,18 @@ export declare class WalletAPI {
|
|
|
171
172
|
execute: (p: SolanaExecuteParams) => Promise<SolanaUnsignedTransaction>;
|
|
172
173
|
removeSigner: (p: SolanaRemoveSignerParams) => Promise<SolanaUnsignedTransaction>;
|
|
173
174
|
submit: (p: SolanaSubmitParams) => Promise<SolanaSubmitResult>;
|
|
175
|
+
/** DEX:Jupiter 报价(只读,不构建交易)。 */
|
|
176
|
+
dexQuote: (p: SolanaDexQuoteParams) => Promise<SolanaDexQuoteResult>;
|
|
177
|
+
/** DEX:一站式 policy 安装——返回四笔未签名交易,按 `steps` 顺序本地签名广播。 */
|
|
178
|
+
dexInstallPolicy: (p: SolanaDexPolicyParams) => Promise<SolanaDexInstallResult>;
|
|
179
|
+
/** DEX:组装 ProgramInteraction swap 交易(未签名;签名后经 `submit` 广播并计费)。 */
|
|
180
|
+
dexSwap: (p: SolanaDexSwapParams) => Promise<SolanaDexSwapResult>;
|
|
181
|
+
/**
|
|
182
|
+
* DEX:一步完成 swap —— 取未签名交易 → `sign` 回调本地签名 → 经 `/submit` 广播。
|
|
183
|
+
* `sign` 由调用方提供(SDK 不内置 Solana 依赖),例如:
|
|
184
|
+
* `(b64) => { const tx = VersionedTransaction.deserialize(Buffer.from(b64, 'base64')); tx.sign([kp]); return Buffer.from(tx.serialize()).toString('base64') }`
|
|
185
|
+
*/
|
|
186
|
+
dexSwapAndSubmit: (p: SolanaDexSwapAndSubmitParams) => Promise<SolanaDexSwapAndSubmitResult>;
|
|
174
187
|
};
|
|
175
188
|
};
|
|
176
189
|
/** MPC(email 分片钱包)子域:canonical `/v1/wallet/mpc/*`。 */
|
|
@@ -153,6 +153,24 @@ class WalletAPI {
|
|
|
153
153
|
execute: (p) => this.http.post('/v1/wallet/session/solana/session/execute', p),
|
|
154
154
|
removeSigner: (p) => this.http.post('/v1/wallet/session/solana/session/remove-signer', p),
|
|
155
155
|
submit: (p) => this.http.post('/v1/wallet/session/solana/submit', p),
|
|
156
|
+
/** DEX:Jupiter 报价(只读,不构建交易)。 */
|
|
157
|
+
dexQuote: (p) => this.http.post('/v1/wallet/session/solana/dex/quote', p),
|
|
158
|
+
/** DEX:一站式 policy 安装——返回四笔未签名交易,按 `steps` 顺序本地签名广播。 */
|
|
159
|
+
dexInstallPolicy: (p) => this.http.post('/v1/wallet/session/solana/dex/install', p),
|
|
160
|
+
/** DEX:组装 ProgramInteraction swap 交易(未签名;签名后经 `submit` 广播并计费)。 */
|
|
161
|
+
dexSwap: (p) => buildDexSwapRequest(this.http, p),
|
|
162
|
+
/**
|
|
163
|
+
* DEX:一步完成 swap —— 取未签名交易 → `sign` 回调本地签名 → 经 `/submit` 广播。
|
|
164
|
+
* `sign` 由调用方提供(SDK 不内置 Solana 依赖),例如:
|
|
165
|
+
* `(b64) => { const tx = VersionedTransaction.deserialize(Buffer.from(b64, 'base64')); tx.sign([kp]); return Buffer.from(tx.serialize()).toString('base64') }`
|
|
166
|
+
*/
|
|
167
|
+
dexSwapAndSubmit: async (p) => {
|
|
168
|
+
const { sign, payer, ...swap } = p;
|
|
169
|
+
const built = await buildDexSwapRequest(this.http, swap);
|
|
170
|
+
const signed = await sign(built.transaction, [...new Set([swap.feePayer, swap.sessionKey])]);
|
|
171
|
+
const submit = await this.http.post('/v1/wallet/session/solana/submit', { chain: swap.chain, transaction: signed, ...(payer ? { payer } : {}) });
|
|
172
|
+
return { ...built, submit };
|
|
173
|
+
},
|
|
156
174
|
},
|
|
157
175
|
};
|
|
158
176
|
/** MPC(email 分片钱包)子域:canonical `/v1/wallet/mpc/*`。 */
|
|
@@ -184,3 +202,45 @@ class WalletAPI {
|
|
|
184
202
|
};
|
|
185
203
|
}
|
|
186
204
|
exports.WalletAPI = WalletAPI;
|
|
205
|
+
/**
|
|
206
|
+
* `/dex/swap` 的请求构造(2026-10-24):
|
|
207
|
+
* - 给了 `dexProvider`:本地取价取指令 → 以 `provider: 'external'` 交给 relay;
|
|
208
|
+
* - 只给了 `swapInstructions`(B 端自己取):直接透传为 external;
|
|
209
|
+
* - 都没给:原样发给 relay,由 relay 调内置 Jupiter(默认行为)。
|
|
210
|
+
*/
|
|
211
|
+
async function buildDexSwapRequest(http, p) {
|
|
212
|
+
const { dexProvider, quote, swapInstructions, addressLookupTableAddresses, ...plain } = p;
|
|
213
|
+
if (!dexProvider) {
|
|
214
|
+
const payload = {
|
|
215
|
+
...plain,
|
|
216
|
+
...(swapInstructions ? { provider: 'external', swapInstructions } : {}),
|
|
217
|
+
...(quote ? { quote } : {}),
|
|
218
|
+
...(addressLookupTableAddresses?.length ? { addressLookupTableAddresses } : {}),
|
|
219
|
+
};
|
|
220
|
+
return http.post('/v1/wallet/session/solana/dex/swap', payload);
|
|
221
|
+
}
|
|
222
|
+
const q = await dexProvider.quote(plain);
|
|
223
|
+
const swap = await dexProvider.swapInstructions({
|
|
224
|
+
inputMint: String(plain.inputMint),
|
|
225
|
+
outputMint: String(plain.outputMint),
|
|
226
|
+
amount: plain.amount,
|
|
227
|
+
slippageBps: plain.slippageBps,
|
|
228
|
+
userPublicKey: plain.vaultPda,
|
|
229
|
+
quote: q.raw,
|
|
230
|
+
});
|
|
231
|
+
return http.post('/v1/wallet/session/solana/dex/swap', {
|
|
232
|
+
...plain,
|
|
233
|
+
provider: 'external',
|
|
234
|
+
quote: {
|
|
235
|
+
inAmount: q.inAmount,
|
|
236
|
+
outAmount: q.outAmount,
|
|
237
|
+
otherAmountThreshold: q.otherAmountThreshold,
|
|
238
|
+
priceImpactPct: q.priceImpactPct,
|
|
239
|
+
slippageBps: q.slippageBps,
|
|
240
|
+
},
|
|
241
|
+
swapInstructions: swap.instructions,
|
|
242
|
+
...(swap.addressLookupTableAddresses?.length
|
|
243
|
+
? { addressLookupTableAddresses: swap.addressLookupTableAddresses }
|
|
244
|
+
: {}),
|
|
245
|
+
});
|
|
246
|
+
}
|
package/package.json
CHANGED