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