@owney/sdk 0.7.21-beta.4 → 0.7.21-beta.5
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/index.cjs +442 -357
- package/dist/index.d.cts +9 -18
- package/dist/index.d.ts +9 -18
- package/dist/index.js +428 -339
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
// src/lib/deposit-batch-callback.ts
|
|
2
|
+
var batchCallbacks = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
var getDepositBatchTransfer = (callback) => callback ? batchCallbacks.get(callback) : void 0;
|
|
4
|
+
function toBatchTransfer(to, amount, verification) {
|
|
5
|
+
return {
|
|
6
|
+
to,
|
|
7
|
+
amount,
|
|
8
|
+
...verification ? {
|
|
9
|
+
yieldseeker: {
|
|
10
|
+
signature: verification.signature,
|
|
11
|
+
userId: verification.userId,
|
|
12
|
+
agentId: verification.yieldseekerAgentId
|
|
13
|
+
}
|
|
14
|
+
} : {}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function registerDepositBatch(callback, transfer) {
|
|
18
|
+
batchCallbacks.set(callback, transfer);
|
|
19
|
+
}
|
|
20
|
+
|
|
1
21
|
// src/errors.ts
|
|
2
22
|
var OwneyError = class extends Error {
|
|
3
23
|
code;
|
|
@@ -2072,39 +2092,6 @@ async function ensureWalletOnChain(pub, wallet, expected) {
|
|
|
2072
2092
|
|
|
2073
2093
|
// src/lib/transfer-auth.ts
|
|
2074
2094
|
import { bytesToHex } from "viem";
|
|
2075
|
-
var ERC20_META_ABI = [
|
|
2076
|
-
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
2077
|
-
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
2078
|
-
];
|
|
2079
|
-
function buildTransferWithAuthorizationTypedData(input) {
|
|
2080
|
-
return {
|
|
2081
|
-
domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
|
|
2082
|
-
types: {
|
|
2083
|
-
TransferWithAuthorization: [
|
|
2084
|
-
{ name: "from", type: "address" },
|
|
2085
|
-
{ name: "to", type: "address" },
|
|
2086
|
-
{ name: "value", type: "uint256" },
|
|
2087
|
-
{ name: "validAfter", type: "uint256" },
|
|
2088
|
-
{ name: "validBefore", type: "uint256" },
|
|
2089
|
-
{ name: "nonce", type: "bytes32" }
|
|
2090
|
-
]
|
|
2091
|
-
},
|
|
2092
|
-
primaryType: "TransferWithAuthorization",
|
|
2093
|
-
message: input.message
|
|
2094
|
-
};
|
|
2095
|
-
}
|
|
2096
|
-
async function readTokenMeta(publicClient, token) {
|
|
2097
|
-
const [tokenName, tokenVersion] = await Promise.all([
|
|
2098
|
-
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
|
|
2099
|
-
publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
2100
|
-
]);
|
|
2101
|
-
return { tokenName, tokenVersion };
|
|
2102
|
-
}
|
|
2103
|
-
function randomAuthNonce() {
|
|
2104
|
-
const bytes = new Uint8Array(32);
|
|
2105
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
2106
|
-
return bytesToHex(bytes);
|
|
2107
|
-
}
|
|
2108
2095
|
|
|
2109
2096
|
// src/lib/sponsor-client.ts
|
|
2110
2097
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
@@ -2147,65 +2134,21 @@ async function postPaymasterIntent(input) {
|
|
|
2147
2134
|
}
|
|
2148
2135
|
return parsed.data;
|
|
2149
2136
|
}
|
|
2150
|
-
async function
|
|
2137
|
+
async function getSponsorRelayerAddress(input) {
|
|
2151
2138
|
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2152
2139
|
let res;
|
|
2153
2140
|
try {
|
|
2154
|
-
res = await fetch(
|
|
2155
|
-
|
|
2156
|
-
headers: {
|
|
2157
|
-
"content-type": "application/json",
|
|
2158
|
-
"x-owney-api-key": input.apiKey,
|
|
2159
|
-
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
2160
|
-
},
|
|
2161
|
-
body: JSON.stringify(input.body)
|
|
2162
|
-
});
|
|
2163
|
-
} catch (networkError) {
|
|
2164
|
-
throw new OwneyError(
|
|
2165
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2166
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2167
|
-
{ cause: String(networkError) }
|
|
2168
|
-
);
|
|
2169
|
-
}
|
|
2170
|
-
const text = await res.text();
|
|
2171
|
-
let parsed = null;
|
|
2172
|
-
try {
|
|
2173
|
-
parsed = JSON.parse(text);
|
|
2174
|
-
} catch {
|
|
2175
|
-
}
|
|
2176
|
-
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2177
|
-
throw new OwneyError(
|
|
2178
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2179
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2141
|
+
res = await fetch(
|
|
2142
|
+
`${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2180
2143
|
{
|
|
2181
|
-
|
|
2182
|
-
responseBody: text.slice(0, 500),
|
|
2183
|
-
// 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
|
|
2184
|
-
// before broadcast, so it is safe to fall back to a user-paid deposit.
|
|
2185
|
-
safeToFallback: res.status === 503
|
|
2144
|
+
headers: { "x-owney-api-key": input.apiKey }
|
|
2186
2145
|
}
|
|
2187
2146
|
);
|
|
2188
|
-
}
|
|
2189
|
-
return parsed.data;
|
|
2190
|
-
}
|
|
2191
|
-
async function postSponsorPermit2Transfer(input) {
|
|
2192
|
-
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2193
|
-
let res;
|
|
2194
|
-
try {
|
|
2195
|
-
res = await fetch(`${base5}/api/v1/sponsor/permit2-transfer`, {
|
|
2196
|
-
method: "POST",
|
|
2197
|
-
headers: {
|
|
2198
|
-
"content-type": "application/json",
|
|
2199
|
-
"x-owney-api-key": input.apiKey,
|
|
2200
|
-
...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
|
|
2201
|
-
},
|
|
2202
|
-
body: JSON.stringify(input.body)
|
|
2203
|
-
});
|
|
2204
2147
|
} catch (networkError) {
|
|
2205
2148
|
throw new OwneyError(
|
|
2206
2149
|
"SPONSOR_REQUEST_FAILED",
|
|
2207
2150
|
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2208
|
-
{ cause: String(networkError), safeToFallback:
|
|
2151
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
2209
2152
|
);
|
|
2210
2153
|
}
|
|
2211
2154
|
const text = await res.text();
|
|
@@ -2214,54 +2157,53 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
2214
2157
|
parsed = JSON.parse(text);
|
|
2215
2158
|
} catch {
|
|
2216
2159
|
}
|
|
2217
|
-
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
2160
|
+
if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
|
|
2218
2161
|
throw new OwneyError(
|
|
2219
2162
|
"SPONSOR_REQUEST_FAILED",
|
|
2220
2163
|
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
2221
2164
|
{
|
|
2222
2165
|
statusCode: res.status,
|
|
2223
2166
|
responseBody: text.slice(0, 500),
|
|
2224
|
-
safeToFallback:
|
|
2167
|
+
safeToFallback: true
|
|
2225
2168
|
}
|
|
2226
2169
|
);
|
|
2227
2170
|
}
|
|
2228
|
-
return parsed.data;
|
|
2171
|
+
return parsed.data.relayer;
|
|
2229
2172
|
}
|
|
2230
|
-
async function
|
|
2231
|
-
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2173
|
+
async function postSponsorBatchTransfer(input) {
|
|
2232
2174
|
let res;
|
|
2233
2175
|
try {
|
|
2234
2176
|
res = await fetch(
|
|
2235
|
-
`${
|
|
2177
|
+
`${input.baseUrl ?? ROUTING_API_BASE_URL}/api/v1/sponsor/permit2-batch`,
|
|
2236
2178
|
{
|
|
2237
|
-
|
|
2179
|
+
method: "POST",
|
|
2180
|
+
headers: {
|
|
2181
|
+
"content-type": "application/json",
|
|
2182
|
+
"x-owney-api-key": input.apiKey
|
|
2183
|
+
},
|
|
2184
|
+
body: JSON.stringify(input.body)
|
|
2238
2185
|
}
|
|
2239
2186
|
);
|
|
2240
|
-
} catch
|
|
2187
|
+
} catch {
|
|
2241
2188
|
throw new OwneyError(
|
|
2242
2189
|
"SPONSOR_REQUEST_FAILED",
|
|
2243
|
-
|
|
2244
|
-
{
|
|
2190
|
+
"Deposit status is unknown. Retry the same amount to check it.",
|
|
2191
|
+
{ safeToFallback: false }
|
|
2245
2192
|
);
|
|
2246
2193
|
}
|
|
2247
|
-
const
|
|
2248
|
-
|
|
2249
|
-
try {
|
|
2250
|
-
parsed = JSON.parse(text);
|
|
2251
|
-
} catch {
|
|
2252
|
-
}
|
|
2253
|
-
if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
|
|
2194
|
+
const parsed = await res.json().catch(() => null);
|
|
2195
|
+
if (!res.ok || !parsed?.success || !/^0x[0-9a-fA-F]{64}$/.test(parsed.data?.txHash ?? "")) {
|
|
2254
2196
|
throw new OwneyError(
|
|
2255
2197
|
"SPONSOR_REQUEST_FAILED",
|
|
2256
|
-
|
|
2198
|
+
"Deposit could not be confirmed. Retry the same amount to check its status.",
|
|
2257
2199
|
{
|
|
2258
2200
|
statusCode: res.status,
|
|
2259
|
-
|
|
2260
|
-
|
|
2201
|
+
safeToFallback: false,
|
|
2202
|
+
notSubmitted: parsed?.notSubmitted === true || parsed?.error?.notSubmitted === true || parsed?.error?.details?.notSubmitted === true
|
|
2261
2203
|
}
|
|
2262
2204
|
);
|
|
2263
2205
|
}
|
|
2264
|
-
return parsed.data
|
|
2206
|
+
return parsed.data;
|
|
2265
2207
|
}
|
|
2266
2208
|
|
|
2267
2209
|
// src/lib/permit2.ts
|
|
@@ -2297,29 +2239,6 @@ var ERC20_ALLOWANCE_ABI = [
|
|
|
2297
2239
|
outputs: [{ name: "", type: "uint256" }]
|
|
2298
2240
|
}
|
|
2299
2241
|
];
|
|
2300
|
-
function buildPermitTransferFromTypedData(input) {
|
|
2301
|
-
return {
|
|
2302
|
-
domain: {
|
|
2303
|
-
name: "Permit2",
|
|
2304
|
-
chainId: input.chainId,
|
|
2305
|
-
verifyingContract: PERMIT2_ADDRESS
|
|
2306
|
-
},
|
|
2307
|
-
types: {
|
|
2308
|
-
PermitTransferFrom: [
|
|
2309
|
-
{ name: "permitted", type: "TokenPermissions" },
|
|
2310
|
-
{ name: "spender", type: "address" },
|
|
2311
|
-
{ name: "nonce", type: "uint256" },
|
|
2312
|
-
{ name: "deadline", type: "uint256" }
|
|
2313
|
-
],
|
|
2314
|
-
TokenPermissions: [
|
|
2315
|
-
{ name: "token", type: "address" },
|
|
2316
|
-
{ name: "amount", type: "uint256" }
|
|
2317
|
-
]
|
|
2318
|
-
},
|
|
2319
|
-
primaryType: "PermitTransferFrom",
|
|
2320
|
-
message: input.message
|
|
2321
|
-
};
|
|
2322
|
-
}
|
|
2323
2242
|
function randomPermit2Nonce() {
|
|
2324
2243
|
const bytes = new Uint8Array(32);
|
|
2325
2244
|
globalThis.crypto.getRandomValues(bytes);
|
|
@@ -2362,88 +2281,6 @@ function makeVerificationAwareDepositCallback(implementation) {
|
|
|
2362
2281
|
});
|
|
2363
2282
|
return callback;
|
|
2364
2283
|
}
|
|
2365
|
-
function makeSponsoredDepositCallback(deps) {
|
|
2366
|
-
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2367
|
-
return makeVerificationAwareDepositCallback(
|
|
2368
|
-
async (smartWallet, chainId, amount, verification) => {
|
|
2369
|
-
const cid = chainId;
|
|
2370
|
-
const token = deps.tokenAddressByChain[cid];
|
|
2371
|
-
if (!token) {
|
|
2372
|
-
throw new OwneyError(
|
|
2373
|
-
"CHAIN_UNSUPPORTED",
|
|
2374
|
-
`No sponsored token configured for chain ${chainId}`
|
|
2375
|
-
);
|
|
2376
|
-
}
|
|
2377
|
-
const pub = deps.getPublicClient(cid);
|
|
2378
|
-
const wallet = deps.getWalletClient(cid);
|
|
2379
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
2380
|
-
try {
|
|
2381
|
-
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2382
|
-
if (balance < BigInt(amount)) {
|
|
2383
|
-
throw new OwneyError(
|
|
2384
|
-
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2385
|
-
"Insufficient balance for this deposit.",
|
|
2386
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
2387
|
-
);
|
|
2388
|
-
}
|
|
2389
|
-
} catch (err) {
|
|
2390
|
-
if (err instanceof OwneyError) throw err;
|
|
2391
|
-
console.warn(
|
|
2392
|
-
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2393
|
-
err instanceof Error ? err.message : String(err)
|
|
2394
|
-
);
|
|
2395
|
-
}
|
|
2396
|
-
const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
|
|
2397
|
-
const validAfter = 0n;
|
|
2398
|
-
const validBefore = BigInt(
|
|
2399
|
-
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2400
|
-
);
|
|
2401
|
-
const nonce = randomAuthNonce();
|
|
2402
|
-
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2403
|
-
token,
|
|
2404
|
-
chainId: cid,
|
|
2405
|
-
tokenName,
|
|
2406
|
-
tokenVersion,
|
|
2407
|
-
message: {
|
|
2408
|
-
from: deps.ownerAddress,
|
|
2409
|
-
to: smartWallet,
|
|
2410
|
-
value: BigInt(amount),
|
|
2411
|
-
validAfter,
|
|
2412
|
-
validBefore,
|
|
2413
|
-
nonce
|
|
2414
|
-
}
|
|
2415
|
-
});
|
|
2416
|
-
const authSignature = await wallet.signTypedData({
|
|
2417
|
-
account: deps.ownerAddress,
|
|
2418
|
-
...typedData
|
|
2419
|
-
});
|
|
2420
|
-
deps.onApproved?.();
|
|
2421
|
-
const result = await post({
|
|
2422
|
-
baseUrl: deps.baseUrl,
|
|
2423
|
-
apiKey: deps.apiKey,
|
|
2424
|
-
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
2425
|
-
body: {
|
|
2426
|
-
chainId: cid,
|
|
2427
|
-
token,
|
|
2428
|
-
from: deps.ownerAddress,
|
|
2429
|
-
to: smartWallet,
|
|
2430
|
-
value: amount,
|
|
2431
|
-
validAfter: validAfter.toString(),
|
|
2432
|
-
validBefore: validBefore.toString(),
|
|
2433
|
-
nonce,
|
|
2434
|
-
authSignature,
|
|
2435
|
-
tokenName,
|
|
2436
|
-
tokenVersion,
|
|
2437
|
-
...verification?.agentId === "yieldseeker" ? {
|
|
2438
|
-
yieldseekerUserId: verification.userId,
|
|
2439
|
-
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
2440
|
-
} : {}
|
|
2441
|
-
}
|
|
2442
|
-
});
|
|
2443
|
-
return result.txHash;
|
|
2444
|
-
}
|
|
2445
|
-
);
|
|
2446
|
-
}
|
|
2447
2284
|
|
|
2448
2285
|
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2449
2286
|
import { SiweMessage, generateNonce } from "siwe";
|
|
@@ -4311,98 +4148,302 @@ import {
|
|
|
4311
4148
|
} from "viem";
|
|
4312
4149
|
import { base as base4, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
4313
4150
|
|
|
4314
|
-
// src/lib/sponsored-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4151
|
+
// src/lib/sponsored-token-batch.ts
|
|
4152
|
+
import {
|
|
4153
|
+
isAddressEqual,
|
|
4154
|
+
keccak256,
|
|
4155
|
+
toBytes
|
|
4156
|
+
} from "viem";
|
|
4157
|
+
|
|
4158
|
+
// src/lib/permit2-batch.ts
|
|
4159
|
+
import { parseAbi as parseAbi2, hashStruct } from "viem";
|
|
4160
|
+
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
4161
|
+
var PERMIT_BATCH_TYPES = {
|
|
4162
|
+
PermitBatchWitnessTransferFrom: [
|
|
4163
|
+
{ name: "permitted", type: "TokenPermissions[]" },
|
|
4164
|
+
{ name: "spender", type: "address" },
|
|
4165
|
+
{ name: "nonce", type: "uint256" },
|
|
4166
|
+
{ name: "deadline", type: "uint256" },
|
|
4167
|
+
{ name: "witness", type: "Deposit" }
|
|
4168
|
+
],
|
|
4169
|
+
Deposit: [{ name: "recipients", type: "address[]" }],
|
|
4170
|
+
TokenPermissions: [
|
|
4171
|
+
{ name: "token", type: "address" },
|
|
4172
|
+
{ name: "amount", type: "uint256" }
|
|
4173
|
+
]
|
|
4174
|
+
};
|
|
4175
|
+
var PERMIT2_BATCH_ABI = parseAbi2([
|
|
4176
|
+
"struct TokenPermissions { address token; uint256 amount; }",
|
|
4177
|
+
"struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
|
|
4178
|
+
"struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
|
|
4179
|
+
"function permitWitnessTransferFrom(PermitBatchTransferFrom permit,SignatureTransferDetails[] transferDetails,address owner,bytes32 witness,string witnessTypeString,bytes signature)",
|
|
4180
|
+
"function nonceBitmap(address owner,uint256 wordPos) view returns (uint256)"
|
|
4181
|
+
]);
|
|
4182
|
+
function batchPermit(b) {
|
|
4183
|
+
return {
|
|
4184
|
+
permitted: b.transfers.map((t) => ({
|
|
4185
|
+
token: b.token,
|
|
4186
|
+
amount: BigInt(t.amount)
|
|
4187
|
+
})),
|
|
4188
|
+
nonce: BigInt(b.nonce),
|
|
4189
|
+
deadline: BigInt(b.deadline)
|
|
4190
|
+
};
|
|
4191
|
+
}
|
|
4192
|
+
function batchTypedData(b, spender) {
|
|
4193
|
+
return {
|
|
4194
|
+
domain: {
|
|
4195
|
+
name: "Permit2",
|
|
4196
|
+
chainId: b.chainId,
|
|
4197
|
+
verifyingContract: BATCH_PERMIT2_ADDRESS
|
|
4198
|
+
},
|
|
4199
|
+
types: PERMIT_BATCH_TYPES,
|
|
4200
|
+
primaryType: "PermitBatchWitnessTransferFrom",
|
|
4201
|
+
message: {
|
|
4202
|
+
...batchPermit(b),
|
|
4203
|
+
spender,
|
|
4204
|
+
witness: { recipients: b.transfers.map((t) => t.to) }
|
|
4205
|
+
}
|
|
4206
|
+
};
|
|
4207
|
+
}
|
|
4208
|
+
|
|
4209
|
+
// src/lib/sponsored-token-batch.ts
|
|
4210
|
+
var memory = /* @__PURE__ */ new Map();
|
|
4211
|
+
var inflight = /* @__PURE__ */ new Map();
|
|
4212
|
+
var planOf = (transfers) => JSON.stringify(
|
|
4213
|
+
transfers.map((t) => ({
|
|
4214
|
+
to: t.to.toLowerCase(),
|
|
4215
|
+
amount: BigInt(t.amount).toString()
|
|
4216
|
+
}))
|
|
4217
|
+
);
|
|
4218
|
+
function read(key2) {
|
|
4219
|
+
return typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
|
|
4220
|
+
}
|
|
4221
|
+
function save(key2, body) {
|
|
4222
|
+
const value = JSON.stringify({
|
|
4223
|
+
...body,
|
|
4224
|
+
transfers: body.transfers.map(({ to, amount }) => ({ to, amount }))
|
|
4225
|
+
});
|
|
4226
|
+
if (typeof window === "undefined") memory.set(key2, value);
|
|
4227
|
+
else window.localStorage.setItem(key2, value);
|
|
4228
|
+
}
|
|
4229
|
+
function clear(key2) {
|
|
4230
|
+
if (typeof window === "undefined") memory.delete(key2);
|
|
4231
|
+
else window.localStorage.removeItem(key2);
|
|
4232
|
+
}
|
|
4233
|
+
function sponsorTokenBatch(i) {
|
|
4234
|
+
const key2 = `owney.token-batch.v1:${keccak256(toBytes(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
|
|
4235
|
+
const plan = planOf(i.transfers);
|
|
4236
|
+
const active = inflight.get(key2);
|
|
4237
|
+
if (active) {
|
|
4238
|
+
if (active.plan !== plan)
|
|
4239
|
+
return Promise.reject(
|
|
4240
|
+
new Error(
|
|
4241
|
+
"A token deposit is already in progress. Wait for its result before depositing again."
|
|
4242
|
+
)
|
|
4353
4243
|
);
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4244
|
+
return active.promise;
|
|
4245
|
+
}
|
|
4246
|
+
const promise = execute(i, key2, plan).finally(() => inflight.delete(key2));
|
|
4247
|
+
inflight.set(key2, { plan, promise });
|
|
4248
|
+
return promise;
|
|
4249
|
+
}
|
|
4250
|
+
async function execute(i, key2, plan) {
|
|
4251
|
+
if (!i.transfers.length || i.transfers.length > 16 || i.transfers.some(
|
|
4252
|
+
(t) => BigInt(t.amount) <= 0n || BigInt(t.amount) >= 1n << 256n
|
|
4253
|
+
) || new Set(i.transfers.map((t) => t.to.toLowerCase())).size !== i.transfers.length)
|
|
4254
|
+
throw new Error("Invalid token deposit shares.");
|
|
4255
|
+
const send = async (initial) => {
|
|
4256
|
+
let body = initial;
|
|
4257
|
+
save(key2, body);
|
|
4258
|
+
try {
|
|
4259
|
+
if (!body.serializedTransaction) {
|
|
4260
|
+
const prepared = await postSponsorBatchTransfer({
|
|
4261
|
+
apiKey: i.apiKey,
|
|
4262
|
+
baseUrl: i.baseUrl,
|
|
4263
|
+
body
|
|
4264
|
+
});
|
|
4265
|
+
if (!prepared.serializedTransaction || keccak256(prepared.serializedTransaction) !== prepared.txHash)
|
|
4266
|
+
throw new Error(
|
|
4267
|
+
"Sponsorship API did not return a valid prepared transaction."
|
|
4268
|
+
);
|
|
4269
|
+
body = {
|
|
4270
|
+
...body,
|
|
4271
|
+
serializedTransaction: prepared.serializedTransaction
|
|
4272
|
+
};
|
|
4273
|
+
save(key2, body);
|
|
4360
4274
|
}
|
|
4361
|
-
const
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
});
|
|
4366
|
-
const nonce = randomPermit2Nonce();
|
|
4367
|
-
const deadline = BigInt(
|
|
4368
|
-
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
4369
|
-
);
|
|
4370
|
-
const typedData = buildPermitTransferFromTypedData({
|
|
4371
|
-
chainId: cid,
|
|
4372
|
-
message: {
|
|
4373
|
-
permitted: { token, amount: amountWei },
|
|
4374
|
-
spender: relayer,
|
|
4375
|
-
nonce,
|
|
4376
|
-
deadline
|
|
4377
|
-
}
|
|
4378
|
-
});
|
|
4379
|
-
const signature = await wallet.signTypedData({
|
|
4380
|
-
account: deps.ownerAddress,
|
|
4381
|
-
...typedData
|
|
4382
|
-
});
|
|
4383
|
-
deps.onApproved?.();
|
|
4384
|
-
const result = await post({
|
|
4385
|
-
baseUrl: deps.baseUrl,
|
|
4386
|
-
apiKey: deps.apiKey,
|
|
4387
|
-
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
4388
|
-
body: {
|
|
4389
|
-
chainId: cid,
|
|
4390
|
-
token,
|
|
4391
|
-
from: deps.ownerAddress,
|
|
4392
|
-
to: smartWallet,
|
|
4393
|
-
amount,
|
|
4394
|
-
nonce: nonce.toString(),
|
|
4395
|
-
deadline: deadline.toString(),
|
|
4396
|
-
signature,
|
|
4397
|
-
...verification?.agentId === "yieldseeker" ? {
|
|
4398
|
-
yieldseekerUserId: verification.userId,
|
|
4399
|
-
yieldseekerAgentId: verification.yieldseekerAgentId
|
|
4400
|
-
} : {}
|
|
4401
|
-
}
|
|
4275
|
+
const result = await postSponsorBatchTransfer({
|
|
4276
|
+
apiKey: i.apiKey,
|
|
4277
|
+
baseUrl: i.baseUrl,
|
|
4278
|
+
body
|
|
4402
4279
|
});
|
|
4280
|
+
if (result.txHash !== keccak256(body.serializedTransaction))
|
|
4281
|
+
throw new Error(
|
|
4282
|
+
"Sponsorship receipt does not match the pending transaction."
|
|
4283
|
+
);
|
|
4284
|
+
clear(key2);
|
|
4403
4285
|
return result.txHash;
|
|
4286
|
+
} catch (error) {
|
|
4287
|
+
if (!body.serializedTransaction || error instanceof OwneyError && error.details?.notSubmitted === true)
|
|
4288
|
+
clear(key2);
|
|
4289
|
+
throw error;
|
|
4404
4290
|
}
|
|
4291
|
+
};
|
|
4292
|
+
const saved = read(key2);
|
|
4293
|
+
if (saved) {
|
|
4294
|
+
const previous = JSON.parse(saved);
|
|
4295
|
+
if (previous.chainId !== i.chainId || !isAddressEqual(previous.from, i.owner) || !isAddressEqual(previous.token, i.token) || planOf(previous.transfers) !== plan)
|
|
4296
|
+
throw new Error(
|
|
4297
|
+
"Retry the previous token deposit and agent split first to reconcile its status."
|
|
4298
|
+
);
|
|
4299
|
+
i.onApproved?.();
|
|
4300
|
+
return send({ ...previous, transfers: i.transfers });
|
|
4301
|
+
}
|
|
4302
|
+
const total = i.transfers.reduce((sum, t) => sum + BigInt(t.amount), 0n);
|
|
4303
|
+
const [balance, allowance] = await Promise.all([
|
|
4304
|
+
readErc20Balance(i.pub, i.token, i.owner),
|
|
4305
|
+
readPermit2Allowance(i.pub, i.token, i.owner)
|
|
4306
|
+
]);
|
|
4307
|
+
if (balance < total)
|
|
4308
|
+
throw new OwneyError(
|
|
4309
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
4310
|
+
"Insufficient token balance for this deposit."
|
|
4311
|
+
);
|
|
4312
|
+
if (allowance < total)
|
|
4313
|
+
throw new OwneyError(
|
|
4314
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
4315
|
+
"token deposits need a one-time Permit2 approval."
|
|
4316
|
+
);
|
|
4317
|
+
const relayer = await getSponsorRelayerAddress({
|
|
4318
|
+
apiKey: i.apiKey,
|
|
4319
|
+
baseUrl: i.baseUrl,
|
|
4320
|
+
chainId: i.chainId
|
|
4321
|
+
});
|
|
4322
|
+
const now = (await i.pub.getBlock()).timestamp;
|
|
4323
|
+
const unsigned = {
|
|
4324
|
+
chainId: i.chainId,
|
|
4325
|
+
token: i.token,
|
|
4326
|
+
from: i.owner,
|
|
4327
|
+
transfers: i.transfers,
|
|
4328
|
+
nonce: randomPermit2Nonce().toString(),
|
|
4329
|
+
deadline: (now + 900n).toString()
|
|
4330
|
+
};
|
|
4331
|
+
const signature = await i.wallet.signTypedData({
|
|
4332
|
+
account: i.owner,
|
|
4333
|
+
...batchTypedData(unsigned, relayer)
|
|
4334
|
+
});
|
|
4335
|
+
i.onApproved?.();
|
|
4336
|
+
return send({ ...unsigned, signature });
|
|
4337
|
+
}
|
|
4338
|
+
|
|
4339
|
+
// src/lib/sponsored-token-deposit.ts
|
|
4340
|
+
function makeSponsoredTokenCallback(deps) {
|
|
4341
|
+
const batch = async (chainId, transfers) => {
|
|
4342
|
+
if (chainId !== 8453 && chainId !== 42161 && chainId !== 1)
|
|
4343
|
+
throw new OwneyError(
|
|
4344
|
+
"CHAIN_UNSUPPORTED",
|
|
4345
|
+
`No sponsored token configured for chain ${chainId}`
|
|
4346
|
+
);
|
|
4347
|
+
const token = deps.tokenAddressByChain[chainId];
|
|
4348
|
+
if (!token)
|
|
4349
|
+
throw new OwneyError(
|
|
4350
|
+
"CHAIN_UNSUPPORTED",
|
|
4351
|
+
`No sponsored token configured for chain ${chainId}`
|
|
4352
|
+
);
|
|
4353
|
+
const pub = deps.getPublicClient(chainId), wallet = deps.getWalletClient(chainId);
|
|
4354
|
+
await ensureWalletOnChain(pub, wallet, chainId);
|
|
4355
|
+
return sponsorTokenBatch({
|
|
4356
|
+
apiKey: deps.apiKey,
|
|
4357
|
+
baseUrl: deps.baseUrl,
|
|
4358
|
+
owner: deps.ownerAddress,
|
|
4359
|
+
token,
|
|
4360
|
+
chainId,
|
|
4361
|
+
transfers,
|
|
4362
|
+
pub,
|
|
4363
|
+
wallet,
|
|
4364
|
+
onApproved: deps.onApproved
|
|
4365
|
+
});
|
|
4366
|
+
};
|
|
4367
|
+
const callback = makeVerificationAwareDepositCallback(
|
|
4368
|
+
(to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
|
|
4405
4369
|
);
|
|
4370
|
+
registerDepositBatch(callback, batch);
|
|
4371
|
+
return callback;
|
|
4372
|
+
}
|
|
4373
|
+
|
|
4374
|
+
// src/lib/agent-deposit-batch.ts
|
|
4375
|
+
function deferred() {
|
|
4376
|
+
let resolve, reject;
|
|
4377
|
+
const promise = new Promise((yes, no) => {
|
|
4378
|
+
resolve = yes;
|
|
4379
|
+
reject = no;
|
|
4380
|
+
});
|
|
4381
|
+
void promise.catch(() => {
|
|
4382
|
+
});
|
|
4383
|
+
return { promise, resolve, reject };
|
|
4384
|
+
}
|
|
4385
|
+
async function runAgentDepositBatch(chainId, legs, transfer) {
|
|
4386
|
+
const funding = deferred();
|
|
4387
|
+
const tasks = [];
|
|
4388
|
+
const transfers = [];
|
|
4389
|
+
try {
|
|
4390
|
+
for (const leg of legs) {
|
|
4391
|
+
const ready = deferred();
|
|
4392
|
+
let entered = false;
|
|
4393
|
+
const callback = makeVerificationAwareDepositCallback(
|
|
4394
|
+
(to, cid, amount, verification) => {
|
|
4395
|
+
if (entered || cid !== chainId || BigInt(amount) !== BigInt(leg.amount)) {
|
|
4396
|
+
const error = new Error(
|
|
4397
|
+
"Agent changed its prepared deposit share."
|
|
4398
|
+
);
|
|
4399
|
+
ready.reject(error);
|
|
4400
|
+
throw error;
|
|
4401
|
+
}
|
|
4402
|
+
entered = true;
|
|
4403
|
+
ready.resolve(toBatchTransfer(to, amount, verification));
|
|
4404
|
+
return funding.promise;
|
|
4405
|
+
}
|
|
4406
|
+
);
|
|
4407
|
+
const task = Promise.resolve().then(() => leg.run(callback));
|
|
4408
|
+
tasks.push(task);
|
|
4409
|
+
void task.then(
|
|
4410
|
+
() => {
|
|
4411
|
+
if (!entered)
|
|
4412
|
+
ready.reject(
|
|
4413
|
+
new Error("Agent did not prepare a deposit transfer.")
|
|
4414
|
+
);
|
|
4415
|
+
},
|
|
4416
|
+
(error) => ready.reject(error)
|
|
4417
|
+
);
|
|
4418
|
+
transfers.push(await ready.promise);
|
|
4419
|
+
}
|
|
4420
|
+
const txHash = await transfer(chainId, transfers);
|
|
4421
|
+
funding.resolve(txHash);
|
|
4422
|
+
const settled = await Promise.allSettled(tasks);
|
|
4423
|
+
const agentResults = {};
|
|
4424
|
+
const failures = [];
|
|
4425
|
+
for (const [index, result] of settled.entries()) {
|
|
4426
|
+
if (result.status === "fulfilled")
|
|
4427
|
+
agentResults[legs[index].id] = result.value;
|
|
4428
|
+
else failures.push(legs[index].id);
|
|
4429
|
+
}
|
|
4430
|
+
if (failures.length)
|
|
4431
|
+
throw new OwneyError(
|
|
4432
|
+
"DEPOSIT_PARTIAL_FAILURE",
|
|
4433
|
+
"The deposit was sent to all agents, but some agent updates could not be confirmed. Check activity before depositing again.",
|
|
4434
|
+
{
|
|
4435
|
+
txHash,
|
|
4436
|
+
fundsSubmitted: true,
|
|
4437
|
+
agentResults,
|
|
4438
|
+
failedAgentIds: failures
|
|
4439
|
+
}
|
|
4440
|
+
);
|
|
4441
|
+
return { agentResults };
|
|
4442
|
+
} catch (error) {
|
|
4443
|
+
funding.reject(error);
|
|
4444
|
+
await Promise.allSettled(tasks);
|
|
4445
|
+
throw error;
|
|
4446
|
+
}
|
|
4406
4447
|
}
|
|
4407
4448
|
|
|
4408
4449
|
// src/lib/sponsored-calls-deposit.ts
|
|
@@ -4432,7 +4473,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4432
4473
|
}
|
|
4433
4474
|
return new URL(configured, origin).toString();
|
|
4434
4475
|
};
|
|
4435
|
-
|
|
4476
|
+
const batch = async (chainId, transfers) => {
|
|
4436
4477
|
const cid = chainId;
|
|
4437
4478
|
const token = deps.tokenAddressByChain[cid];
|
|
4438
4479
|
if (!token) {
|
|
@@ -4448,19 +4489,24 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4448
4489
|
{ chainId }
|
|
4449
4490
|
);
|
|
4450
4491
|
}
|
|
4451
|
-
const
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4492
|
+
const calls = transfers.map((transfer) => ({
|
|
4493
|
+
to: token,
|
|
4494
|
+
value: "0x0",
|
|
4495
|
+
data: encodeFunctionData2({
|
|
4496
|
+
abi: erc20Abi2,
|
|
4497
|
+
functionName: "transfer",
|
|
4498
|
+
args: [transfer.to, BigInt(transfer.amount)]
|
|
4499
|
+
})
|
|
4500
|
+
}));
|
|
4456
4501
|
let paymasterUrl = absolutePaymasterUrl();
|
|
4457
|
-
|
|
4458
|
-
|
|
4502
|
+
for (const transfer of transfers) {
|
|
4503
|
+
const verification = transfer.yieldseeker;
|
|
4504
|
+
if (!verification) continue;
|
|
4505
|
+
if (chainId !== 8453)
|
|
4459
4506
|
throw new OwneyError(
|
|
4460
4507
|
"CHAIN_UNSUPPORTED",
|
|
4461
|
-
`Yieldseeker
|
|
4508
|
+
`Yieldseeker sponsorship is not available on chain ${chainId}.`
|
|
4462
4509
|
);
|
|
4463
|
-
}
|
|
4464
4510
|
const { intent } = await postPaymasterIntent({
|
|
4465
4511
|
baseUrl: deps.routingApiBaseUrl,
|
|
4466
4512
|
apiKey: deps.apiKey,
|
|
@@ -4469,14 +4515,14 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4469
4515
|
chainId,
|
|
4470
4516
|
token,
|
|
4471
4517
|
from: deps.ownerAddress,
|
|
4472
|
-
to:
|
|
4473
|
-
amount,
|
|
4518
|
+
to: transfer.to,
|
|
4519
|
+
amount: transfer.amount,
|
|
4474
4520
|
yieldseekerUserId: verification.userId,
|
|
4475
|
-
yieldseekerAgentId: verification.
|
|
4521
|
+
yieldseekerAgentId: verification.agentId
|
|
4476
4522
|
}
|
|
4477
4523
|
});
|
|
4478
4524
|
const url = new URL(paymasterUrl);
|
|
4479
|
-
url.searchParams.
|
|
4525
|
+
url.searchParams.append("owneyIntent", intent);
|
|
4480
4526
|
paymasterUrl = url.toString();
|
|
4481
4527
|
}
|
|
4482
4528
|
const sendResult = await deps.provider.request({
|
|
@@ -4486,8 +4532,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4486
4532
|
version: "2.0.0",
|
|
4487
4533
|
from: deps.ownerAddress,
|
|
4488
4534
|
chainId: toHex(chainId),
|
|
4489
|
-
atomicRequired:
|
|
4490
|
-
calls
|
|
4535
|
+
atomicRequired: transfers.length > 1,
|
|
4536
|
+
calls,
|
|
4491
4537
|
capabilities: {
|
|
4492
4538
|
paymasterService: { url: paymasterUrl }
|
|
4493
4539
|
}
|
|
@@ -4509,7 +4555,24 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4509
4555
|
params: [callsId]
|
|
4510
4556
|
});
|
|
4511
4557
|
const txHash = status?.receipts?.[0]?.transactionHash;
|
|
4512
|
-
if (
|
|
4558
|
+
if (status?.receipts?.some((receipt) => receipt.status === "0x0") || typeof status?.status === "number" && status.status >= 400) {
|
|
4559
|
+
throw new OwneyError(
|
|
4560
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4561
|
+
"The sponsored deposit did not complete successfully.",
|
|
4562
|
+
{ chainId, callsId, safeToFallback: false }
|
|
4563
|
+
);
|
|
4564
|
+
}
|
|
4565
|
+
if (txHash && (transfers.length === 1 || status?.status === 200 || status?.status === "CONFIRMED")) {
|
|
4566
|
+
if (status?.receipts?.some(
|
|
4567
|
+
(receipt) => receipt.transactionHash !== txHash
|
|
4568
|
+
))
|
|
4569
|
+
throw new OwneyError(
|
|
4570
|
+
"SPONSORED_CALLS_NO_RECEIPT",
|
|
4571
|
+
"The wallet returned multiple transactions for an atomic deposit. Check activity before trying again.",
|
|
4572
|
+
{ chainId, callsId }
|
|
4573
|
+
);
|
|
4574
|
+
return txHash;
|
|
4575
|
+
}
|
|
4513
4576
|
if (pollIntervalMs > 0) {
|
|
4514
4577
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
4515
4578
|
}
|
|
@@ -4519,7 +4582,12 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
4519
4582
|
`No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
|
|
4520
4583
|
{ chainId, callsId }
|
|
4521
4584
|
);
|
|
4522
|
-
}
|
|
4585
|
+
};
|
|
4586
|
+
const callback = makeVerificationAwareDepositCallback(
|
|
4587
|
+
(to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
|
|
4588
|
+
);
|
|
4589
|
+
registerDepositBatch(callback, batch);
|
|
4590
|
+
return callback;
|
|
4523
4591
|
}
|
|
4524
4592
|
|
|
4525
4593
|
// src/client.ts
|
|
@@ -4558,6 +4626,11 @@ var SPONSORED_WETH_BY_CHAIN = {
|
|
|
4558
4626
|
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
4559
4627
|
1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
|
4560
4628
|
};
|
|
4629
|
+
var SPONSORED_TOKENS_BY_ASSET = { USDC: SPONSORED_USDC_BY_CHAIN, WETH: SPONSORED_WETH_BY_CHAIN };
|
|
4630
|
+
function sponsoredTokensFor(asset) {
|
|
4631
|
+
if (!Object.prototype.hasOwnProperty.call(SPONSORED_TOKENS_BY_ASSET, asset)) throw new OwneyError("ASSET_UNSUPPORTED", `No sponsored token configured for ${asset}`);
|
|
4632
|
+
return SPONSORED_TOKENS_BY_ASSET[asset];
|
|
4633
|
+
}
|
|
4561
4634
|
function shouldFallbackToUserPaid(error, asset, appCallback) {
|
|
4562
4635
|
return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
|
|
4563
4636
|
}
|
|
@@ -4692,18 +4765,13 @@ var OwneySDK = class {
|
|
|
4692
4765
|
}
|
|
4693
4766
|
return this.state.provider;
|
|
4694
4767
|
}
|
|
4695
|
-
/**
|
|
4696
|
-
* Lazily builds (and caches) the default EIP-3009 sponsored deposit callback
|
|
4697
|
-
* used when the caller omits `depositCallback`. Wraps the connected EIP-1193
|
|
4698
|
-
* provider with viem `custom(provider)` to read token meta and sign the
|
|
4699
|
-
* `TransferWithAuthorization`, then POSTs to the sponsor API.
|
|
4700
|
-
*/
|
|
4768
|
+
/** Builds the default USDC batch callback for the connected wallet. */
|
|
4701
4769
|
getDefaultSponsoredCallback(onApproved) {
|
|
4702
4770
|
if (!onApproved && this.cachedSponsoredCallback)
|
|
4703
4771
|
return this.cachedSponsoredCallback;
|
|
4704
4772
|
const provider = this.requireConnectedProvider();
|
|
4705
4773
|
const owner = this.state.walletAddress;
|
|
4706
|
-
const callback =
|
|
4774
|
+
const callback = makeSponsoredTokenCallback({
|
|
4707
4775
|
apiKey: this.apiKey,
|
|
4708
4776
|
baseUrl: this.routingApiBaseUrl,
|
|
4709
4777
|
ownerAddress: owner,
|
|
@@ -4725,12 +4793,7 @@ var OwneySDK = class {
|
|
|
4725
4793
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
4726
4794
|
return callback;
|
|
4727
4795
|
}
|
|
4728
|
-
/**
|
|
4729
|
-
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
4730
|
-
* callback used when the caller omits `depositCallback` for a WETH
|
|
4731
|
-
* deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
4732
|
-
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
4733
|
-
*/
|
|
4796
|
+
/** Builds the wallet-native sponsored calls callback for compatible paymasters. */
|
|
4734
4797
|
getDefaultSponsoredCallsCallback(asset, onApproved) {
|
|
4735
4798
|
const cached = this.cachedSponsoredCallsCallbacks.get(asset);
|
|
4736
4799
|
if (!onApproved && cached) return cached;
|
|
@@ -4742,7 +4805,7 @@ var OwneySDK = class {
|
|
|
4742
4805
|
ownerAddress: this.state.walletAddress,
|
|
4743
4806
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
4744
4807
|
onApproved,
|
|
4745
|
-
tokenAddressByChain: asset
|
|
4808
|
+
tokenAddressByChain: sponsoredTokensFor(asset)
|
|
4746
4809
|
});
|
|
4747
4810
|
if (!onApproved) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
4748
4811
|
return callback;
|
|
@@ -4751,14 +4814,14 @@ var OwneySDK = class {
|
|
|
4751
4814
|
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
4752
4815
|
* callback used when the caller omits `depositCallback` for a WETH deposit.
|
|
4753
4816
|
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
4754
|
-
*
|
|
4817
|
+
* single-use batch authorization instead of an EIP-3009 authorization.
|
|
4755
4818
|
*/
|
|
4756
4819
|
getDefaultWethSponsoredCallback(onApproved) {
|
|
4757
4820
|
if (!onApproved && this.cachedWethSponsoredCallback)
|
|
4758
4821
|
return this.cachedWethSponsoredCallback;
|
|
4759
4822
|
const provider = this.requireConnectedProvider();
|
|
4760
4823
|
const owner = this.state.walletAddress;
|
|
4761
|
-
const callback =
|
|
4824
|
+
const callback = makeSponsoredTokenCallback({
|
|
4762
4825
|
apiKey: this.apiKey,
|
|
4763
4826
|
baseUrl: this.routingApiBaseUrl,
|
|
4764
4827
|
ownerAddress: owner,
|
|
@@ -5064,7 +5127,8 @@ var OwneySDK = class {
|
|
|
5064
5127
|
* @param options.asset - Asset symbol to deposit (e.g. "USDC")
|
|
5065
5128
|
* @param options.depositCallback - Callback that performs the token transfer and returns a tx hash.
|
|
5066
5129
|
* When agentId is omitted, this callback is invoked once per eligible agent with that agent's
|
|
5067
|
-
* split amount and smart wallet address
|
|
5130
|
+
* split amount and smart wallet address. Default sponsored deposits batch
|
|
5131
|
+
* all shares into one signature; custom callbacks still run once per agent.
|
|
5068
5132
|
* @param options.agentId - Optional explicit target. Otherwise split equally,
|
|
5069
5133
|
* or fund remaining agents when a recovery deposit cannot meet every minimum.
|
|
5070
5134
|
* @returns {OwneyDepositResult} for a single agent, or {OwneyMultiDepositResult} with per-agent results
|
|
@@ -5149,6 +5213,32 @@ var OwneySDK = class {
|
|
|
5149
5213
|
}
|
|
5150
5214
|
);
|
|
5151
5215
|
}
|
|
5216
|
+
const batchTransfer = getDepositBatchTransfer(effectiveCallback);
|
|
5217
|
+
if (!depositCallback && batchTransfer) {
|
|
5218
|
+
return runAgentDepositBatch(
|
|
5219
|
+
chainId,
|
|
5220
|
+
agentAmounts.map(({ agent, amount: amount2 }) => ({
|
|
5221
|
+
id: agent.id,
|
|
5222
|
+
amount: amount2,
|
|
5223
|
+
run: (callback) => withFailureReporting(
|
|
5224
|
+
this.apiKey,
|
|
5225
|
+
agent.id,
|
|
5226
|
+
() => agent.deposit(state, chainId, amount2, asset, callback),
|
|
5227
|
+
this.routingApiBaseUrl
|
|
5228
|
+
)
|
|
5229
|
+
})),
|
|
5230
|
+
async (cid, transfers) => {
|
|
5231
|
+
try {
|
|
5232
|
+
return await batchTransfer(cid, transfers);
|
|
5233
|
+
} catch (error) {
|
|
5234
|
+
if (!(error instanceof OwneyError) || error.code !== "PERMIT2_APPROVAL_REQUIRED")
|
|
5235
|
+
throw error;
|
|
5236
|
+
await this.approvePermit2(asset);
|
|
5237
|
+
return batchTransfer(cid, transfers);
|
|
5238
|
+
}
|
|
5239
|
+
}
|
|
5240
|
+
);
|
|
5241
|
+
}
|
|
5152
5242
|
const agentResults = {};
|
|
5153
5243
|
for (const [
|
|
5154
5244
|
index,
|
|
@@ -5188,7 +5278,7 @@ var OwneySDK = class {
|
|
|
5188
5278
|
*
|
|
5189
5279
|
* 1. Missing Permit2 allowance: when the app did not supply its own
|
|
5190
5280
|
* callback and the attempt fails with `PERMIT2_APPROVAL_REQUIRED` on a
|
|
5191
|
-
*
|
|
5281
|
+
* token deposit, this is the wallet's first Permit2 deposit for that token. We send
|
|
5192
5282
|
* the one-time (user-paid) Permit2 approval via `approvePermit2()` and
|
|
5193
5283
|
* retry the SAME sponsored attempt once. Bounded to one approval attempt
|
|
5194
5284
|
* per call so a wallet/agent that keeps reporting the allowance as
|
|
@@ -5225,12 +5315,12 @@ var OwneySDK = class {
|
|
|
5225
5315
|
try {
|
|
5226
5316
|
return await attempt(effectiveCallback);
|
|
5227
5317
|
} catch (error) {
|
|
5228
|
-
if (!approvalAttempted && appCallback === void 0 && asset === "WETH" && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
|
|
5318
|
+
if (!approvalAttempted && appCallback === void 0 && (asset === "WETH" || asset === "USDC") && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
|
|
5229
5319
|
approvalAttempted = true;
|
|
5230
5320
|
console.warn(
|
|
5231
|
-
"[owney-sdk] First
|
|
5321
|
+
"[owney-sdk] First token deposit: sending one-time Permit2 approval..."
|
|
5232
5322
|
);
|
|
5233
|
-
await this.approvePermit2();
|
|
5323
|
+
await this.approvePermit2(asset);
|
|
5234
5324
|
continue;
|
|
5235
5325
|
}
|
|
5236
5326
|
if (!shouldFallbackToUserPaid(error, asset, appCallback)) throw error;
|
|
@@ -5883,23 +5973,22 @@ var OwneySDK = class {
|
|
|
5883
5973
|
return pending;
|
|
5884
5974
|
}
|
|
5885
5975
|
/**
|
|
5886
|
-
* One-time, user-paid approval of Permit2 on the
|
|
5887
|
-
* the active chain. Required once per wallet
|
|
5976
|
+
* One-time, user-paid approval of Permit2 on the selected token for
|
|
5977
|
+
* the active chain. Required once per wallet, token and chain before sponsored
|
|
5888
5978
|
* deposits; afterwards deposit() is signature-only. Resolves only after the
|
|
5889
5979
|
* approval transaction is mined (1 confirmation), so a subsequent deposit()
|
|
5890
5980
|
* will see the new allowance; throws if the transaction reverted.
|
|
5891
5981
|
* @returns the approval transaction hash.
|
|
5892
5982
|
*/
|
|
5893
5983
|
async approvePermit2(asset = "WETH") {
|
|
5894
|
-
void asset;
|
|
5895
5984
|
const state = this.requireState();
|
|
5896
5985
|
const chainId = this.requireChainId();
|
|
5897
|
-
this.
|
|
5898
|
-
const token =
|
|
5986
|
+
this.getEligibleAgents(chainId, asset, { excludeDisabled: true });
|
|
5987
|
+
const token = sponsoredTokensFor(asset)[chainId];
|
|
5899
5988
|
if (!token) {
|
|
5900
5989
|
throw new OwneyError(
|
|
5901
5990
|
"CHAIN_UNSUPPORTED",
|
|
5902
|
-
`No sponsored
|
|
5991
|
+
`No sponsored token on chain ${chainId}`
|
|
5903
5992
|
);
|
|
5904
5993
|
}
|
|
5905
5994
|
const provider = this.requireConnectedProvider();
|