@chipi-stack/backend 14.14.0 → 14.15.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/{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 +35 -6
- package/dist/index.d.ts +35 -6
- package/dist/index.js +321 -127
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +321 -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,175 @@ 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 session must whitelist `transfer` and cap USDC. A payment
|
|
3314
|
+
* that was made but not served is reported in `error.details.xPayment`;
|
|
3315
|
+
* pass it back as `options.xPayment` to retry without paying again.
|
|
3316
|
+
*
|
|
3317
|
+
* @example
|
|
3318
|
+
* ```typescript
|
|
3319
|
+
* const res = await sdk.ai.think(
|
|
3320
|
+
* { portfolio, model: "haiku" },
|
|
3321
|
+
* { payWithSession: { encryptKey, wallet, session, maxAmount: 20_000n } } // $0.02 at most
|
|
3322
|
+
* );
|
|
3323
|
+
* res.x402Payment; // set when the call was paid over x402
|
|
3324
|
+
* ```
|
|
3159
3325
|
*/
|
|
3160
|
-
async think(params) {
|
|
3326
|
+
async think(params, options = {}) {
|
|
3327
|
+
try {
|
|
3328
|
+
return await this.postThink(params, options.xPayment);
|
|
3329
|
+
} catch (err) {
|
|
3330
|
+
const offer = options.payWithSession ? sessionTransferOffer(err) : void 0;
|
|
3331
|
+
if (!offer || !options.payWithSession) throw err;
|
|
3332
|
+
return this.payAndThink(params, offer, options.payWithSession);
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
postThink(params, xPayment) {
|
|
3161
3336
|
return this.client.post({
|
|
3162
3337
|
endpoint: `${AI_BASE}/think`,
|
|
3163
3338
|
bearerToken: this.secret(),
|
|
3164
|
-
body: params
|
|
3339
|
+
body: params,
|
|
3340
|
+
...xPayment ? { headers: { "X-PAYMENT": xPayment } } : {}
|
|
3341
|
+
});
|
|
3342
|
+
}
|
|
3343
|
+
/**
|
|
3344
|
+
* Pay one think call with a session-key USDC transfer, then send it again
|
|
3345
|
+
* with `X-PAYMENT`. Every check that can refuse the call runs before the
|
|
3346
|
+
* transfer; the transfer counts only once its receipt shows the USDC moved.
|
|
3347
|
+
*/
|
|
3348
|
+
async payAndThink(params, offer, pay) {
|
|
3349
|
+
if (!this.sessions) {
|
|
3350
|
+
throw new ChipiApiError(
|
|
3351
|
+
"payWithSession needs the SDK's session client",
|
|
3352
|
+
"X402_NOT_CONFIGURED",
|
|
3353
|
+
402
|
|
3354
|
+
);
|
|
3355
|
+
}
|
|
3356
|
+
let maxAmount;
|
|
3357
|
+
try {
|
|
3358
|
+
maxAmount = BigInt(pay.maxAmount);
|
|
3359
|
+
} catch {
|
|
3360
|
+
throw new ChipiApiError(
|
|
3361
|
+
`maxAmount must be an integer of USDC base units (6 decimals), got ${String(pay.maxAmount)}; nothing was paid`,
|
|
3362
|
+
"X402_INVALID_MAX_AMOUNT",
|
|
3363
|
+
400
|
|
3364
|
+
);
|
|
3365
|
+
}
|
|
3366
|
+
const amount = BigInt(offer.maxAmountRequired);
|
|
3367
|
+
if (amount > maxAmount) {
|
|
3368
|
+
throw new ChipiApiError(
|
|
3369
|
+
`The x402 price (${amount} USDC base units) is above maxAmount (${pay.maxAmount}); nothing was paid`,
|
|
3370
|
+
"X402_PRICE_ABOVE_MAX",
|
|
3371
|
+
402,
|
|
3372
|
+
{ x402: { accepts: [offer] } }
|
|
3373
|
+
);
|
|
3374
|
+
}
|
|
3375
|
+
const payable = offer.extra?.payableModels;
|
|
3376
|
+
if (params.model && Array.isArray(payable) && !payable.includes(params.model)) {
|
|
3377
|
+
throw new ChipiApiError(
|
|
3378
|
+
`Model ${params.model} cannot be paid with x402 (payable: ${payable.join(", ")}); nothing was paid`,
|
|
3379
|
+
"X402_MODEL_NOT_PAYABLE",
|
|
3380
|
+
402,
|
|
3381
|
+
{ x402: { accepts: [offer] } }
|
|
3382
|
+
);
|
|
3383
|
+
}
|
|
3384
|
+
const maxBytes = offer.extra?.maxPromptBytes;
|
|
3385
|
+
if (typeof maxBytes === "number" && thinkInputBytes(params) > maxBytes) {
|
|
3386
|
+
throw new ChipiApiError(
|
|
3387
|
+
`Paid calls accept at most ${maxBytes} bytes of portfolio, history and chain; nothing was paid`,
|
|
3388
|
+
"X402_PAYLOAD_TOO_LARGE",
|
|
3389
|
+
402,
|
|
3390
|
+
{ x402: { accepts: [offer] } }
|
|
3391
|
+
);
|
|
3392
|
+
}
|
|
3393
|
+
const payer = pay.wallet.publicKey;
|
|
3394
|
+
const mask = (1n << 128n) - 1n;
|
|
3395
|
+
const transactionHash = await this.sessions.executeTransactionWithSession(
|
|
3396
|
+
{
|
|
3397
|
+
encryptKey: pay.encryptKey,
|
|
3398
|
+
wallet: pay.wallet,
|
|
3399
|
+
session: pay.session,
|
|
3400
|
+
calls: [
|
|
3401
|
+
{
|
|
3402
|
+
contractAddress: offer.asset,
|
|
3403
|
+
entrypoint: "transfer",
|
|
3404
|
+
calldata: [offer.payTo, (amount & mask).toString(), (amount >> 128n).toString()]
|
|
3405
|
+
}
|
|
3406
|
+
]
|
|
3407
|
+
},
|
|
3408
|
+
this.secret()
|
|
3409
|
+
);
|
|
3410
|
+
const header = {
|
|
3411
|
+
x402Version: 1,
|
|
3412
|
+
scheme: "exact",
|
|
3413
|
+
network: "starknet-mainnet",
|
|
3414
|
+
payload: {
|
|
3415
|
+
signature: { r: padFelt(transactionHash), s: "session" },
|
|
3416
|
+
fromAddress: padFelt(payer),
|
|
3417
|
+
toAddress: padFelt(offer.payTo),
|
|
3418
|
+
amount: amount.toString(),
|
|
3419
|
+
asset: offer.asset,
|
|
3420
|
+
validUntil: Math.floor(Date.now() / 1e3) + offer.maxTimeoutSeconds,
|
|
3421
|
+
nonce: padFelt(transactionHash)
|
|
3422
|
+
}
|
|
3423
|
+
};
|
|
3424
|
+
const xPayment = JSON.stringify(header);
|
|
3425
|
+
const paidError = (message, code, extra = {}) => new ChipiApiError(message, code, 402, {
|
|
3426
|
+
x402: { accepts: [offer] },
|
|
3427
|
+
x402PaymentHash: transactionHash,
|
|
3428
|
+
...extra
|
|
3165
3429
|
});
|
|
3430
|
+
let receipt;
|
|
3431
|
+
try {
|
|
3432
|
+
receipt = await waitForTransaction(transactionHash, { timeoutMs: 12e4 });
|
|
3433
|
+
} catch (err) {
|
|
3434
|
+
throw paidError(
|
|
3435
|
+
`No receipt yet for the x402 payment ${transactionHash}: ${err instanceof Error ? err.message : String(err)}`,
|
|
3436
|
+
"X402_PAYMENT_PENDING",
|
|
3437
|
+
{ xPayment }
|
|
3438
|
+
);
|
|
3439
|
+
}
|
|
3440
|
+
const paid = await new SessionTxVerifier({
|
|
3441
|
+
txHashStore: { consume: () => true },
|
|
3442
|
+
getReceipt: async () => receipt
|
|
3443
|
+
}).verifyTransfer({
|
|
3444
|
+
transactionHash,
|
|
3445
|
+
token: offer.asset,
|
|
3446
|
+
from: payer,
|
|
3447
|
+
to: offer.payTo,
|
|
3448
|
+
minAmount: amount
|
|
3449
|
+
});
|
|
3450
|
+
if (!paid.valid) {
|
|
3451
|
+
throw paidError(
|
|
3452
|
+
`The x402 payment did not move ${amount} USDC base units to payTo (${paid.reason}). Check the session's whitelist (transfer) and its USDC cap.`,
|
|
3453
|
+
"X402_PAYMENT_FAILED",
|
|
3454
|
+
{ reason: paid.reason }
|
|
3455
|
+
);
|
|
3456
|
+
}
|
|
3457
|
+
for (let attempt = 1; ; attempt++) {
|
|
3458
|
+
try {
|
|
3459
|
+
return await this.postThink(params, xPayment);
|
|
3460
|
+
} catch (err) {
|
|
3461
|
+
const retryable = err instanceof ChipiApiError && (err.status === 409 || err.status === 503 && err.details?.retryable === true);
|
|
3462
|
+
if (retryable && attempt < PAID_MAX_ATTEMPTS) {
|
|
3463
|
+
await sleep(PAID_RETRY_DELAY_MS);
|
|
3464
|
+
continue;
|
|
3465
|
+
}
|
|
3466
|
+
if (err instanceof ChipiApiError) {
|
|
3467
|
+
throw new ChipiApiError(err.message, err.code, err.status ?? 402, {
|
|
3468
|
+
...typeof err.details === "object" && err.details !== null ? err.details : {},
|
|
3469
|
+
x402PaymentHash: transactionHash,
|
|
3470
|
+
xPayment
|
|
3471
|
+
});
|
|
3472
|
+
}
|
|
3473
|
+
throw err;
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3166
3476
|
}
|
|
3167
3477
|
/**
|
|
3168
3478
|
* `POST /v1/ai/execute`: unsigned AVNU swap calls (approve + swap) for
|
|
@@ -3524,7 +3834,7 @@ var ChipiSDK = class {
|
|
|
3524
3834
|
this.sessions = new ChipiSessions(this.client);
|
|
3525
3835
|
this.exchanges = new ChipiExchanges(this.client);
|
|
3526
3836
|
this.treasury = new ChipiTreasury(this.client, config.apiSecretKey);
|
|
3527
|
-
this.ai = new ChipiAi(this.client, config.apiSecretKey);
|
|
3837
|
+
this.ai = new ChipiAi(this.client, config.apiSecretKey, this.sessions);
|
|
3528
3838
|
this.executeTransaction = this.executeTransaction.bind(this);
|
|
3529
3839
|
this.executeTransactionWithSession = this.executeTransactionWithSession.bind(this);
|
|
3530
3840
|
this.transfer = this.transfer.bind(this);
|
|
@@ -3837,49 +4147,6 @@ var ChipiBrowserSDK = class extends ChipiSDK {
|
|
|
3837
4147
|
// All methods are inherited from ChipiSDK
|
|
3838
4148
|
// bearerToken parameter is required for each authenticated request
|
|
3839
4149
|
};
|
|
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
4150
|
|
|
3884
4151
|
// src/index.ts
|
|
3885
4152
|
init_encryption2();
|
|
@@ -4204,79 +4471,6 @@ function x402Middleware(config) {
|
|
|
4204
4471
|
next();
|
|
4205
4472
|
};
|
|
4206
4473
|
}
|
|
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
4474
|
|
|
4281
4475
|
// src/shhh/index.ts
|
|
4282
4476
|
init_snip12_hash();
|