@chipi-stack/backend 14.14.0 → 14.15.1
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/{client-CqD_yGH0.d.mts → client-D6SDBu5p.d.mts} +3 -1
- package/dist/{client-CqD_yGH0.d.ts → client-D6SDBu5p.d.ts} +3 -1
- package/dist/index.d.mts +39 -6
- package/dist/index.d.ts +39 -6
- package/dist/index.js +346 -127
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +346 -127
- package/dist/index.mjs.map +1 -1
- package/dist/skuPurchases.d.mts +1 -1
- package/dist/skuPurchases.d.ts +1 -1
- package/dist/skus.d.mts +1 -1
- package/dist/skus.d.ts +1 -1
- package/dist/transactions.d.mts +1 -1
- package/dist/transactions.d.ts +1 -1
- package/dist/{wallets-DxKnh21Q.d.ts → wallets-C541u9mn.d.ts} +1 -1
- package/dist/{wallets-CYCLyyXY.d.mts → wallets-CX42j3eN.d.mts} +1 -1
- package/dist/wallets.d.mts +2 -2
- package/dist/wallets.d.ts +2 -2
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -732,7 +732,8 @@ var ChipiClient = class {
|
|
|
732
732
|
throw new shared.ChipiApiError(
|
|
733
733
|
errorData.message,
|
|
734
734
|
errorData.code || `HTTP_${response.status}`,
|
|
735
|
-
response.status
|
|
735
|
+
response.status,
|
|
736
|
+
data
|
|
736
737
|
);
|
|
737
738
|
}
|
|
738
739
|
return data;
|
|
@@ -743,14 +744,15 @@ var ChipiClient = class {
|
|
|
743
744
|
async post({
|
|
744
745
|
endpoint,
|
|
745
746
|
bearerToken,
|
|
746
|
-
body
|
|
747
|
+
body,
|
|
748
|
+
headers
|
|
747
749
|
}) {
|
|
748
750
|
try {
|
|
749
751
|
const url = new URL(`${this.baseUrl}${endpoint}`);
|
|
750
752
|
this.addVersionParams(url);
|
|
751
753
|
const response = await fetch(url.toString(), {
|
|
752
754
|
method: "POST",
|
|
753
|
-
headers: this.getHeaders(bearerToken),
|
|
755
|
+
headers: { ...headers, ...this.getHeaders(bearerToken) },
|
|
754
756
|
body: body ? JSON.stringify(body) : void 0
|
|
755
757
|
});
|
|
756
758
|
const data = await response.json();
|
|
@@ -759,7 +761,8 @@ var ChipiClient = class {
|
|
|
759
761
|
throw new shared.ChipiApiError(
|
|
760
762
|
errorData.message,
|
|
761
763
|
errorData.code || `HTTP_${response.status}`,
|
|
762
|
-
response.status
|
|
764
|
+
response.status,
|
|
765
|
+
data
|
|
763
766
|
);
|
|
764
767
|
}
|
|
765
768
|
return data;
|
|
@@ -786,7 +789,8 @@ var ChipiClient = class {
|
|
|
786
789
|
throw new shared.ChipiApiError(
|
|
787
790
|
errorData.message,
|
|
788
791
|
errorData.code || `HTTP_${response.status}`,
|
|
789
|
-
response.status
|
|
792
|
+
response.status,
|
|
793
|
+
data
|
|
790
794
|
);
|
|
791
795
|
}
|
|
792
796
|
return data;
|
|
@@ -813,7 +817,8 @@ var ChipiClient = class {
|
|
|
813
817
|
throw new shared.ChipiApiError(
|
|
814
818
|
errorData.message,
|
|
815
819
|
errorData.code || `HTTP_${response.status}`,
|
|
816
|
-
response.status
|
|
820
|
+
response.status,
|
|
821
|
+
data
|
|
817
822
|
);
|
|
818
823
|
}
|
|
819
824
|
return data;
|
|
@@ -838,7 +843,8 @@ var ChipiClient = class {
|
|
|
838
843
|
throw new shared.ChipiApiError(
|
|
839
844
|
errorData.message,
|
|
840
845
|
errorData.code || `HTTP_${response.status}`,
|
|
841
|
-
response.status
|
|
846
|
+
response.status,
|
|
847
|
+
data
|
|
842
848
|
);
|
|
843
849
|
}
|
|
844
850
|
return data;
|
|
@@ -3113,8 +3119,149 @@ var ChipiTreasury = class {
|
|
|
3113
3119
|
};
|
|
3114
3120
|
}
|
|
3115
3121
|
};
|
|
3122
|
+
var DEFAULT_RETRY_INTERVAL_MS = 3e3;
|
|
3123
|
+
var DEFAULT_TIMEOUT_MS = 3e5;
|
|
3124
|
+
function isPending(finalityStatus) {
|
|
3125
|
+
return finalityStatus === void 0 || finalityStatus === "RECEIVED" || finalityStatus === "PENDING" || finalityStatus === "PRE_CONFIRMED";
|
|
3126
|
+
}
|
|
3127
|
+
async function waitForTransaction(transactionHash, options = {}) {
|
|
3128
|
+
const {
|
|
3129
|
+
nodeUrl = shared.WALLET_RPC_ENDPOINTS.CHIPI,
|
|
3130
|
+
retryIntervalMs = DEFAULT_RETRY_INTERVAL_MS,
|
|
3131
|
+
timeoutMs = DEFAULT_TIMEOUT_MS
|
|
3132
|
+
} = options;
|
|
3133
|
+
const deadline = Date.now() + timeoutMs;
|
|
3134
|
+
let lastError;
|
|
3135
|
+
for (; ; ) {
|
|
3136
|
+
try {
|
|
3137
|
+
const receipt = await shared.withRpcFallback(
|
|
3138
|
+
(provider) => provider.getTransactionReceipt(transactionHash),
|
|
3139
|
+
{ fallbackUrl: nodeUrl }
|
|
3140
|
+
);
|
|
3141
|
+
if (!isPending(receipt?.finality_status)) {
|
|
3142
|
+
const reverted = receipt.execution_status === "REVERTED";
|
|
3143
|
+
return {
|
|
3144
|
+
transactionHash,
|
|
3145
|
+
success: !reverted,
|
|
3146
|
+
executionStatus: receipt.execution_status,
|
|
3147
|
+
finalityStatus: receipt.finality_status,
|
|
3148
|
+
...reverted ? { revertReason: receipt.revert_reason ?? "reverted on chain" } : {},
|
|
3149
|
+
events: receipt.events ?? [],
|
|
3150
|
+
...receipt.block_number !== void 0 ? { blockNumber: receipt.block_number } : {}
|
|
3151
|
+
};
|
|
3152
|
+
}
|
|
3153
|
+
} catch (err) {
|
|
3154
|
+
lastError = err;
|
|
3155
|
+
}
|
|
3156
|
+
if (Date.now() + retryIntervalMs >= deadline) {
|
|
3157
|
+
const detail = lastError instanceof Error ? `: ${lastError.message}` : "";
|
|
3158
|
+
throw new Error(
|
|
3159
|
+
`Timed out after ${timeoutMs}ms waiting for ${transactionHash}${detail}. The transaction may still land; re-check before treating it as failed.`
|
|
3160
|
+
);
|
|
3161
|
+
}
|
|
3162
|
+
await new Promise((resolve) => setTimeout(resolve, retryIntervalMs));
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
var InMemoryTxHashStore = class {
|
|
3166
|
+
constructor() {
|
|
3167
|
+
this.used = /* @__PURE__ */ new Set();
|
|
3168
|
+
}
|
|
3169
|
+
consume(transactionHash) {
|
|
3170
|
+
if (this.used.has(transactionHash)) return false;
|
|
3171
|
+
this.used.add(transactionHash);
|
|
3172
|
+
return true;
|
|
3173
|
+
}
|
|
3174
|
+
};
|
|
3175
|
+
var TRANSFER_SELECTOR = BigInt(starknet.hash.getSelectorFromName("Transfer"));
|
|
3176
|
+
function toBigInt2(value) {
|
|
3177
|
+
if (value === void 0) return void 0;
|
|
3178
|
+
try {
|
|
3179
|
+
return BigInt(value);
|
|
3180
|
+
} catch {
|
|
3181
|
+
return void 0;
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
function decodeTransfer(event) {
|
|
3185
|
+
if (toBigInt2(event.keys[0]) !== TRANSFER_SELECTOR) return void 0;
|
|
3186
|
+
const [from, to, low, high] = event.keys.length >= 3 ? [event.keys[1], event.keys[2], event.data[0], event.data[1]] : [event.data[0], event.data[1], event.data[2], event.data[3]];
|
|
3187
|
+
const values = [from, to, low, high].map(toBigInt2);
|
|
3188
|
+
if (values.some((v) => v === void 0)) return void 0;
|
|
3189
|
+
const [f, t, l, h] = values;
|
|
3190
|
+
return { from: f, to: t, amount: l + (h << 128n) };
|
|
3191
|
+
}
|
|
3192
|
+
var SessionTxVerifier = class {
|
|
3193
|
+
constructor(config = {}) {
|
|
3194
|
+
const { txHashStore, getReceipt, ...receiptOptions } = config;
|
|
3195
|
+
this.store = txHashStore ?? new InMemoryTxHashStore();
|
|
3196
|
+
this.getReceipt = getReceipt ?? waitForTransaction;
|
|
3197
|
+
this.receiptOptions = receiptOptions;
|
|
3198
|
+
}
|
|
3199
|
+
async verifyTransfer(params) {
|
|
3200
|
+
const { transactionHash, token, from, to, minAmount } = params;
|
|
3201
|
+
const receipt = await this.getReceipt(transactionHash, this.receiptOptions);
|
|
3202
|
+
if (!receipt.success) {
|
|
3203
|
+
return { valid: false, transactionHash, reason: "REVERTED", detail: receipt.revertReason };
|
|
3204
|
+
}
|
|
3205
|
+
const tokenAddress = BigInt(token);
|
|
3206
|
+
const fromAddress = BigInt(from);
|
|
3207
|
+
const toAddress = BigInt(to);
|
|
3208
|
+
let best;
|
|
3209
|
+
for (const event of receipt.events) {
|
|
3210
|
+
if (toBigInt2(event.from_address) !== tokenAddress) continue;
|
|
3211
|
+
const transfer = decodeTransfer(event);
|
|
3212
|
+
if (!transfer || transfer.from !== fromAddress || transfer.to !== toAddress) continue;
|
|
3213
|
+
if (best === void 0 || transfer.amount > best) best = transfer.amount;
|
|
3214
|
+
}
|
|
3215
|
+
if (best === void 0) {
|
|
3216
|
+
return { valid: false, transactionHash, reason: "TRANSFER_NOT_FOUND" };
|
|
3217
|
+
}
|
|
3218
|
+
if (best < minAmount) {
|
|
3219
|
+
return {
|
|
3220
|
+
valid: false,
|
|
3221
|
+
transactionHash,
|
|
3222
|
+
reason: "AMOUNT_TOO_LOW",
|
|
3223
|
+
detail: `paid ${best}, expected at least ${minAmount}`
|
|
3224
|
+
};
|
|
3225
|
+
}
|
|
3226
|
+
const key = "0x" + BigInt(transactionHash).toString(16);
|
|
3227
|
+
if (!await this.store.consume(key)) {
|
|
3228
|
+
return { valid: false, transactionHash, reason: "ALREADY_USED" };
|
|
3229
|
+
}
|
|
3230
|
+
return {
|
|
3231
|
+
valid: true,
|
|
3232
|
+
transactionHash,
|
|
3233
|
+
amount: best,
|
|
3234
|
+
...receipt.blockNumber !== void 0 ? { blockNumber: receipt.blockNumber } : {}
|
|
3235
|
+
};
|
|
3236
|
+
}
|
|
3237
|
+
};
|
|
3238
|
+
|
|
3239
|
+
// src/ai.ts
|
|
3116
3240
|
var AI_BASE = "/ai";
|
|
3117
3241
|
var THINK_ACTIONS = /* @__PURE__ */ new Set(["swap", "supply", "withdraw", "hold"]);
|
|
3242
|
+
var PAID_MAX_ATTEMPTS = 3;
|
|
3243
|
+
var PAID_RETRY_DELAY_MS = 5e3;
|
|
3244
|
+
var padFelt = (value) => "0x" + BigInt(value).toString(16).padStart(64, "0");
|
|
3245
|
+
function sameFelt(a, b) {
|
|
3246
|
+
try {
|
|
3247
|
+
return BigInt(a) === BigInt(b);
|
|
3248
|
+
} catch {
|
|
3249
|
+
return false;
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
function sessionTransferOffer(err) {
|
|
3253
|
+
if (!(err instanceof shared.ChipiApiError) || err.status !== 402) return void 0;
|
|
3254
|
+
const details = err.details;
|
|
3255
|
+
return details?.x402?.accepts?.find(
|
|
3256
|
+
(r) => r.scheme === "exact" && r.network === "starknet-mainnet" && r.extra?.paymentMethod === "session-transfer" && sameFelt(r.asset, shared.CONTRACT_ADDRESSES.USDC_MAINNET)
|
|
3257
|
+
);
|
|
3258
|
+
}
|
|
3259
|
+
function thinkInputBytes(params) {
|
|
3260
|
+
const bytes = (text) => new TextEncoder().encode(text).length;
|
|
3261
|
+
const chain = params.chain ?? "starknet";
|
|
3262
|
+
return bytes(JSON.stringify(params.portfolio, null, 2)) + (params.history?.recentDecisions ? bytes(String(params.history.recentDecisions)) : 0) + 2 * bytes(chain);
|
|
3263
|
+
}
|
|
3264
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3118
3265
|
function isThinkDecision(decision) {
|
|
3119
3266
|
if (!decision || typeof decision !== "object") return false;
|
|
3120
3267
|
const d = decision;
|
|
@@ -3128,9 +3275,10 @@ function isThinkDecision(decision) {
|
|
|
3128
3275
|
return true;
|
|
3129
3276
|
}
|
|
3130
3277
|
var ChipiAi = class {
|
|
3131
|
-
constructor(client, apiSecretKey) {
|
|
3278
|
+
constructor(client, apiSecretKey, sessions) {
|
|
3132
3279
|
this.client = client;
|
|
3133
3280
|
this.apiSecretKey = apiSecretKey;
|
|
3281
|
+
this.sessions = sessions;
|
|
3134
3282
|
}
|
|
3135
3283
|
secret() {
|
|
3136
3284
|
const key = this.apiSecretKey;
|
|
@@ -3161,13 +3309,200 @@ var ChipiAi = class {
|
|
|
3161
3309
|
*
|
|
3162
3310
|
* The decision is advice, not an order: check it with `isThinkDecision` and
|
|
3163
3311
|
* enforce your own limits before executing anything.
|
|
3312
|
+
*
|
|
3313
|
+
* **Paying per call (x402).** With `payWithSession`, a 402 that carries a
|
|
3314
|
+
* session-transfer offer (credits short, x402 on) is paid with the agent's
|
|
3315
|
+
* session key: a USDC `transfer` to the offer's `payTo`, then the same call
|
|
3316
|
+
* again with `X-PAYMENT`. The price must be at most `maxAmount`, `model`
|
|
3317
|
+
* must be payable and the input within `maxPromptBytes`, all checked before
|
|
3318
|
+
* paying. The wallet must be a wallet of the organization that owns the
|
|
3319
|
+
* `sk_`: the first request names it in `X-PAYER`, and when the API answers
|
|
3320
|
+
* that a payment from it would be refused, nothing is paid
|
|
3321
|
+
* (`X402_PAYER_NOT_IN_ORG`). The session must whitelist `transfer` and cap
|
|
3322
|
+
* USDC. A payment that was made but not served is reported in
|
|
3323
|
+
* `error.details.xPayment`; pass it back as `options.xPayment` to retry
|
|
3324
|
+
* without paying again.
|
|
3325
|
+
*
|
|
3326
|
+
* @example
|
|
3327
|
+
* ```typescript
|
|
3328
|
+
* const res = await sdk.ai.think(
|
|
3329
|
+
* { portfolio, model: "haiku" },
|
|
3330
|
+
* { payWithSession: { encryptKey, wallet, session, maxAmount: 20_000n } } // $0.02 at most
|
|
3331
|
+
* );
|
|
3332
|
+
* res.x402Payment; // set when the call was paid over x402
|
|
3333
|
+
* ```
|
|
3164
3334
|
*/
|
|
3165
|
-
async think(params) {
|
|
3335
|
+
async think(params, options = {}) {
|
|
3336
|
+
let payer;
|
|
3337
|
+
if (options.payWithSession && !options.xPayment) {
|
|
3338
|
+
try {
|
|
3339
|
+
payer = padFelt(options.payWithSession.wallet.publicKey);
|
|
3340
|
+
} catch {
|
|
3341
|
+
payer = void 0;
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
try {
|
|
3345
|
+
return await this.postThink(params, options.xPayment, payer);
|
|
3346
|
+
} catch (err) {
|
|
3347
|
+
const offer = options.payWithSession ? sessionTransferOffer(err) : void 0;
|
|
3348
|
+
if (!offer || !options.payWithSession) throw err;
|
|
3349
|
+
return this.payAndThink(params, offer, options.payWithSession);
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3352
|
+
postThink(params, xPayment, payer) {
|
|
3353
|
+
const headers = {
|
|
3354
|
+
...xPayment ? { "X-PAYMENT": xPayment } : {},
|
|
3355
|
+
...payer ? { "X-PAYER": payer } : {}
|
|
3356
|
+
};
|
|
3166
3357
|
return this.client.post({
|
|
3167
3358
|
endpoint: `${AI_BASE}/think`,
|
|
3168
3359
|
bearerToken: this.secret(),
|
|
3169
|
-
body: params
|
|
3360
|
+
body: params,
|
|
3361
|
+
...Object.keys(headers).length > 0 ? { headers } : {}
|
|
3362
|
+
});
|
|
3363
|
+
}
|
|
3364
|
+
/**
|
|
3365
|
+
* Pay one think call with a session-key USDC transfer, then send it again
|
|
3366
|
+
* with `X-PAYMENT`. Every check that can refuse the call runs before the
|
|
3367
|
+
* transfer; the transfer counts only once its receipt shows the USDC moved.
|
|
3368
|
+
*/
|
|
3369
|
+
async payAndThink(params, offer, pay) {
|
|
3370
|
+
if (!this.sessions) {
|
|
3371
|
+
throw new shared.ChipiApiError(
|
|
3372
|
+
"payWithSession needs the SDK's session client",
|
|
3373
|
+
"X402_NOT_CONFIGURED",
|
|
3374
|
+
402
|
|
3375
|
+
);
|
|
3376
|
+
}
|
|
3377
|
+
let maxAmount;
|
|
3378
|
+
try {
|
|
3379
|
+
maxAmount = BigInt(pay.maxAmount);
|
|
3380
|
+
} catch {
|
|
3381
|
+
throw new shared.ChipiApiError(
|
|
3382
|
+
`maxAmount must be an integer of USDC base units (6 decimals), got ${String(pay.maxAmount)}; nothing was paid`,
|
|
3383
|
+
"X402_INVALID_MAX_AMOUNT",
|
|
3384
|
+
400
|
|
3385
|
+
);
|
|
3386
|
+
}
|
|
3387
|
+
const amount = BigInt(offer.maxAmountRequired);
|
|
3388
|
+
if (amount > maxAmount) {
|
|
3389
|
+
throw new shared.ChipiApiError(
|
|
3390
|
+
`The x402 price (${amount} USDC base units) is above maxAmount (${pay.maxAmount}); nothing was paid`,
|
|
3391
|
+
"X402_PRICE_ABOVE_MAX",
|
|
3392
|
+
402,
|
|
3393
|
+
{ x402: { accepts: [offer] } }
|
|
3394
|
+
);
|
|
3395
|
+
}
|
|
3396
|
+
const payable = offer.extra?.payableModels;
|
|
3397
|
+
if (params.model && Array.isArray(payable) && !payable.includes(params.model)) {
|
|
3398
|
+
throw new shared.ChipiApiError(
|
|
3399
|
+
`Model ${params.model} cannot be paid with x402 (payable: ${payable.join(", ")}); nothing was paid`,
|
|
3400
|
+
"X402_MODEL_NOT_PAYABLE",
|
|
3401
|
+
402,
|
|
3402
|
+
{ x402: { accepts: [offer] } }
|
|
3403
|
+
);
|
|
3404
|
+
}
|
|
3405
|
+
const maxBytes = offer.extra?.maxPromptBytes;
|
|
3406
|
+
if (typeof maxBytes === "number" && thinkInputBytes(params) > maxBytes) {
|
|
3407
|
+
throw new shared.ChipiApiError(
|
|
3408
|
+
`Paid calls accept at most ${maxBytes} bytes of portfolio, history and chain; nothing was paid`,
|
|
3409
|
+
"X402_PAYLOAD_TOO_LARGE",
|
|
3410
|
+
402,
|
|
3411
|
+
{ x402: { accepts: [offer] } }
|
|
3412
|
+
);
|
|
3413
|
+
}
|
|
3414
|
+
const payerCheck = offer.extra?.payer;
|
|
3415
|
+
if (payerCheck?.accepted === false && typeof payerCheck.address === "string" && sameFelt(payerCheck.address, pay.wallet.publicKey)) {
|
|
3416
|
+
throw new shared.ChipiApiError(
|
|
3417
|
+
`Wallet ${pay.wallet.publicKey} is not a wallet of the organization that owns this API key, so the API would refuse its payment; nothing was paid. Pay from a wallet of this organization.`,
|
|
3418
|
+
"X402_PAYER_NOT_IN_ORG",
|
|
3419
|
+
402,
|
|
3420
|
+
{ x402: { accepts: [offer] } }
|
|
3421
|
+
);
|
|
3422
|
+
}
|
|
3423
|
+
const payer = pay.wallet.publicKey;
|
|
3424
|
+
const mask = (1n << 128n) - 1n;
|
|
3425
|
+
const transactionHash = await this.sessions.executeTransactionWithSession(
|
|
3426
|
+
{
|
|
3427
|
+
encryptKey: pay.encryptKey,
|
|
3428
|
+
wallet: pay.wallet,
|
|
3429
|
+
session: pay.session,
|
|
3430
|
+
calls: [
|
|
3431
|
+
{
|
|
3432
|
+
contractAddress: offer.asset,
|
|
3433
|
+
entrypoint: "transfer",
|
|
3434
|
+
calldata: [offer.payTo, (amount & mask).toString(), (amount >> 128n).toString()]
|
|
3435
|
+
}
|
|
3436
|
+
]
|
|
3437
|
+
},
|
|
3438
|
+
this.secret()
|
|
3439
|
+
);
|
|
3440
|
+
const header = {
|
|
3441
|
+
x402Version: 1,
|
|
3442
|
+
scheme: "exact",
|
|
3443
|
+
network: "starknet-mainnet",
|
|
3444
|
+
payload: {
|
|
3445
|
+
signature: { r: padFelt(transactionHash), s: "session" },
|
|
3446
|
+
fromAddress: padFelt(payer),
|
|
3447
|
+
toAddress: padFelt(offer.payTo),
|
|
3448
|
+
amount: amount.toString(),
|
|
3449
|
+
asset: offer.asset,
|
|
3450
|
+
validUntil: Math.floor(Date.now() / 1e3) + offer.maxTimeoutSeconds,
|
|
3451
|
+
nonce: padFelt(transactionHash)
|
|
3452
|
+
}
|
|
3453
|
+
};
|
|
3454
|
+
const xPayment = JSON.stringify(header);
|
|
3455
|
+
const paidError = (message, code, extra = {}) => new shared.ChipiApiError(message, code, 402, {
|
|
3456
|
+
x402: { accepts: [offer] },
|
|
3457
|
+
x402PaymentHash: transactionHash,
|
|
3458
|
+
...extra
|
|
3459
|
+
});
|
|
3460
|
+
let receipt;
|
|
3461
|
+
try {
|
|
3462
|
+
receipt = await waitForTransaction(transactionHash, { timeoutMs: 12e4 });
|
|
3463
|
+
} catch (err) {
|
|
3464
|
+
throw paidError(
|
|
3465
|
+
`No receipt yet for the x402 payment ${transactionHash}: ${err instanceof Error ? err.message : String(err)}`,
|
|
3466
|
+
"X402_PAYMENT_PENDING",
|
|
3467
|
+
{ xPayment }
|
|
3468
|
+
);
|
|
3469
|
+
}
|
|
3470
|
+
const paid = await new SessionTxVerifier({
|
|
3471
|
+
txHashStore: { consume: () => true },
|
|
3472
|
+
getReceipt: async () => receipt
|
|
3473
|
+
}).verifyTransfer({
|
|
3474
|
+
transactionHash,
|
|
3475
|
+
token: offer.asset,
|
|
3476
|
+
from: payer,
|
|
3477
|
+
to: offer.payTo,
|
|
3478
|
+
minAmount: amount
|
|
3170
3479
|
});
|
|
3480
|
+
if (!paid.valid) {
|
|
3481
|
+
throw paidError(
|
|
3482
|
+
`The x402 payment did not move ${amount} USDC base units to payTo (${paid.reason}). Check the session's whitelist (transfer) and its USDC cap.`,
|
|
3483
|
+
"X402_PAYMENT_FAILED",
|
|
3484
|
+
{ reason: paid.reason }
|
|
3485
|
+
);
|
|
3486
|
+
}
|
|
3487
|
+
for (let attempt = 1; ; attempt++) {
|
|
3488
|
+
try {
|
|
3489
|
+
return await this.postThink(params, xPayment);
|
|
3490
|
+
} catch (err) {
|
|
3491
|
+
const retryable = err instanceof shared.ChipiApiError && (err.status === 409 || err.status === 503 && err.details?.retryable === true);
|
|
3492
|
+
if (retryable && attempt < PAID_MAX_ATTEMPTS) {
|
|
3493
|
+
await sleep(PAID_RETRY_DELAY_MS);
|
|
3494
|
+
continue;
|
|
3495
|
+
}
|
|
3496
|
+
if (err instanceof shared.ChipiApiError) {
|
|
3497
|
+
throw new shared.ChipiApiError(err.message, err.code, err.status ?? 402, {
|
|
3498
|
+
...typeof err.details === "object" && err.details !== null ? err.details : {},
|
|
3499
|
+
x402PaymentHash: transactionHash,
|
|
3500
|
+
xPayment
|
|
3501
|
+
});
|
|
3502
|
+
}
|
|
3503
|
+
throw err;
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3171
3506
|
}
|
|
3172
3507
|
/**
|
|
3173
3508
|
* `POST /v1/ai/execute`: unsigned AVNU swap calls (approve + swap) for
|
|
@@ -3529,7 +3864,7 @@ var ChipiSDK = class {
|
|
|
3529
3864
|
this.sessions = new ChipiSessions(this.client);
|
|
3530
3865
|
this.exchanges = new ChipiExchanges(this.client);
|
|
3531
3866
|
this.treasury = new ChipiTreasury(this.client, config.apiSecretKey);
|
|
3532
|
-
this.ai = new ChipiAi(this.client, config.apiSecretKey);
|
|
3867
|
+
this.ai = new ChipiAi(this.client, config.apiSecretKey, this.sessions);
|
|
3533
3868
|
this.executeTransaction = this.executeTransaction.bind(this);
|
|
3534
3869
|
this.executeTransactionWithSession = this.executeTransactionWithSession.bind(this);
|
|
3535
3870
|
this.transfer = this.transfer.bind(this);
|
|
@@ -3842,49 +4177,6 @@ var ChipiBrowserSDK = class extends ChipiSDK {
|
|
|
3842
4177
|
// All methods are inherited from ChipiSDK
|
|
3843
4178
|
// bearerToken parameter is required for each authenticated request
|
|
3844
4179
|
};
|
|
3845
|
-
var DEFAULT_RETRY_INTERVAL_MS = 3e3;
|
|
3846
|
-
var DEFAULT_TIMEOUT_MS = 3e5;
|
|
3847
|
-
function isPending(finalityStatus) {
|
|
3848
|
-
return finalityStatus === void 0 || finalityStatus === "RECEIVED" || finalityStatus === "PENDING" || finalityStatus === "PRE_CONFIRMED";
|
|
3849
|
-
}
|
|
3850
|
-
async function waitForTransaction(transactionHash, options = {}) {
|
|
3851
|
-
const {
|
|
3852
|
-
nodeUrl = shared.WALLET_RPC_ENDPOINTS.CHIPI,
|
|
3853
|
-
retryIntervalMs = DEFAULT_RETRY_INTERVAL_MS,
|
|
3854
|
-
timeoutMs = DEFAULT_TIMEOUT_MS
|
|
3855
|
-
} = options;
|
|
3856
|
-
const deadline = Date.now() + timeoutMs;
|
|
3857
|
-
let lastError;
|
|
3858
|
-
for (; ; ) {
|
|
3859
|
-
try {
|
|
3860
|
-
const receipt = await shared.withRpcFallback(
|
|
3861
|
-
(provider) => provider.getTransactionReceipt(transactionHash),
|
|
3862
|
-
{ fallbackUrl: nodeUrl }
|
|
3863
|
-
);
|
|
3864
|
-
if (!isPending(receipt?.finality_status)) {
|
|
3865
|
-
const reverted = receipt.execution_status === "REVERTED";
|
|
3866
|
-
return {
|
|
3867
|
-
transactionHash,
|
|
3868
|
-
success: !reverted,
|
|
3869
|
-
executionStatus: receipt.execution_status,
|
|
3870
|
-
finalityStatus: receipt.finality_status,
|
|
3871
|
-
...reverted ? { revertReason: receipt.revert_reason ?? "reverted on chain" } : {},
|
|
3872
|
-
events: receipt.events ?? [],
|
|
3873
|
-
...receipt.block_number !== void 0 ? { blockNumber: receipt.block_number } : {}
|
|
3874
|
-
};
|
|
3875
|
-
}
|
|
3876
|
-
} catch (err) {
|
|
3877
|
-
lastError = err;
|
|
3878
|
-
}
|
|
3879
|
-
if (Date.now() + retryIntervalMs >= deadline) {
|
|
3880
|
-
const detail = lastError instanceof Error ? `: ${lastError.message}` : "";
|
|
3881
|
-
throw new Error(
|
|
3882
|
-
`Timed out after ${timeoutMs}ms waiting for ${transactionHash}${detail}. The transaction may still land; re-check before treating it as failed.`
|
|
3883
|
-
);
|
|
3884
|
-
}
|
|
3885
|
-
await new Promise((resolve) => setTimeout(resolve, retryIntervalMs));
|
|
3886
|
-
}
|
|
3887
|
-
}
|
|
3888
4180
|
|
|
3889
4181
|
// src/index.ts
|
|
3890
4182
|
init_encryption2();
|
|
@@ -4209,79 +4501,6 @@ function x402Middleware(config) {
|
|
|
4209
4501
|
next();
|
|
4210
4502
|
};
|
|
4211
4503
|
}
|
|
4212
|
-
var InMemoryTxHashStore = class {
|
|
4213
|
-
constructor() {
|
|
4214
|
-
this.used = /* @__PURE__ */ new Set();
|
|
4215
|
-
}
|
|
4216
|
-
consume(transactionHash) {
|
|
4217
|
-
if (this.used.has(transactionHash)) return false;
|
|
4218
|
-
this.used.add(transactionHash);
|
|
4219
|
-
return true;
|
|
4220
|
-
}
|
|
4221
|
-
};
|
|
4222
|
-
var TRANSFER_SELECTOR = BigInt(starknet.hash.getSelectorFromName("Transfer"));
|
|
4223
|
-
function toBigInt2(value) {
|
|
4224
|
-
if (value === void 0) return void 0;
|
|
4225
|
-
try {
|
|
4226
|
-
return BigInt(value);
|
|
4227
|
-
} catch {
|
|
4228
|
-
return void 0;
|
|
4229
|
-
}
|
|
4230
|
-
}
|
|
4231
|
-
function decodeTransfer(event) {
|
|
4232
|
-
if (toBigInt2(event.keys[0]) !== TRANSFER_SELECTOR) return void 0;
|
|
4233
|
-
const [from, to, low, high] = event.keys.length >= 3 ? [event.keys[1], event.keys[2], event.data[0], event.data[1]] : [event.data[0], event.data[1], event.data[2], event.data[3]];
|
|
4234
|
-
const values = [from, to, low, high].map(toBigInt2);
|
|
4235
|
-
if (values.some((v) => v === void 0)) return void 0;
|
|
4236
|
-
const [f, t, l, h] = values;
|
|
4237
|
-
return { from: f, to: t, amount: l + (h << 128n) };
|
|
4238
|
-
}
|
|
4239
|
-
var SessionTxVerifier = class {
|
|
4240
|
-
constructor(config = {}) {
|
|
4241
|
-
const { txHashStore, getReceipt, ...receiptOptions } = config;
|
|
4242
|
-
this.store = txHashStore ?? new InMemoryTxHashStore();
|
|
4243
|
-
this.getReceipt = getReceipt ?? waitForTransaction;
|
|
4244
|
-
this.receiptOptions = receiptOptions;
|
|
4245
|
-
}
|
|
4246
|
-
async verifyTransfer(params) {
|
|
4247
|
-
const { transactionHash, token, from, to, minAmount } = params;
|
|
4248
|
-
const receipt = await this.getReceipt(transactionHash, this.receiptOptions);
|
|
4249
|
-
if (!receipt.success) {
|
|
4250
|
-
return { valid: false, transactionHash, reason: "REVERTED", detail: receipt.revertReason };
|
|
4251
|
-
}
|
|
4252
|
-
const tokenAddress = BigInt(token);
|
|
4253
|
-
const fromAddress = BigInt(from);
|
|
4254
|
-
const toAddress = BigInt(to);
|
|
4255
|
-
let best;
|
|
4256
|
-
for (const event of receipt.events) {
|
|
4257
|
-
if (toBigInt2(event.from_address) !== tokenAddress) continue;
|
|
4258
|
-
const transfer = decodeTransfer(event);
|
|
4259
|
-
if (!transfer || transfer.from !== fromAddress || transfer.to !== toAddress) continue;
|
|
4260
|
-
if (best === void 0 || transfer.amount > best) best = transfer.amount;
|
|
4261
|
-
}
|
|
4262
|
-
if (best === void 0) {
|
|
4263
|
-
return { valid: false, transactionHash, reason: "TRANSFER_NOT_FOUND" };
|
|
4264
|
-
}
|
|
4265
|
-
if (best < minAmount) {
|
|
4266
|
-
return {
|
|
4267
|
-
valid: false,
|
|
4268
|
-
transactionHash,
|
|
4269
|
-
reason: "AMOUNT_TOO_LOW",
|
|
4270
|
-
detail: `paid ${best}, expected at least ${minAmount}`
|
|
4271
|
-
};
|
|
4272
|
-
}
|
|
4273
|
-
const key = "0x" + BigInt(transactionHash).toString(16);
|
|
4274
|
-
if (!await this.store.consume(key)) {
|
|
4275
|
-
return { valid: false, transactionHash, reason: "ALREADY_USED" };
|
|
4276
|
-
}
|
|
4277
|
-
return {
|
|
4278
|
-
valid: true,
|
|
4279
|
-
transactionHash,
|
|
4280
|
-
amount: best,
|
|
4281
|
-
...receipt.blockNumber !== void 0 ? { blockNumber: receipt.blockNumber } : {}
|
|
4282
|
-
};
|
|
4283
|
-
}
|
|
4284
|
-
};
|
|
4285
4504
|
|
|
4286
4505
|
// src/shhh/index.ts
|
|
4287
4506
|
init_snip12_hash();
|