@rhinestone/sdk 2.0.0-beta.4 → 2.0.0-beta.41
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/src/accounts/error.d.ts +2 -9
- package/dist/src/accounts/error.d.ts.map +1 -1
- package/dist/src/accounts/error.js +5 -11
- package/dist/src/accounts/hca.d.ts +25 -0
- package/dist/src/accounts/hca.d.ts.map +1 -0
- package/dist/src/accounts/hca.js +148 -0
- package/dist/src/accounts/index.d.ts +23 -10
- package/dist/src/accounts/index.d.ts.map +1 -1
- package/dist/src/accounts/index.js +170 -79
- package/dist/src/accounts/kernel.d.ts +1 -2
- package/dist/src/accounts/kernel.d.ts.map +1 -1
- package/dist/src/accounts/kernel.js +1 -8
- package/dist/src/accounts/nexus.d.ts +3 -2
- package/dist/src/accounts/nexus.d.ts.map +1 -1
- package/dist/src/accounts/nexus.js +33 -8
- package/dist/src/accounts/safe.d.ts +1 -2
- package/dist/src/accounts/safe.d.ts.map +1 -1
- package/dist/src/accounts/safe.js +3 -9
- package/dist/src/accounts/signing/common.d.ts +10 -4
- package/dist/src/accounts/signing/common.d.ts.map +1 -1
- package/dist/src/accounts/signing/common.js +48 -46
- package/dist/src/accounts/signing/message.d.ts.map +1 -1
- package/dist/src/accounts/signing/message.js +1 -4
- package/dist/src/accounts/signing/typedData.d.ts.map +1 -1
- package/dist/src/accounts/signing/typedData.js +1 -4
- package/dist/src/accounts/startale.d.ts +1 -2
- package/dist/src/accounts/startale.d.ts.map +1 -1
- package/dist/src/accounts/startale.js +6 -9
- package/dist/src/actions/ecdsa.js +5 -5
- package/dist/src/actions/index.js +2 -2
- package/dist/src/actions/mfa.js +2 -2
- package/dist/src/actions/passkeys.js +2 -2
- package/dist/src/actions/smart-sessions.d.ts +29 -5
- package/dist/src/actions/smart-sessions.d.ts.map +1 -1
- package/dist/src/actions/smart-sessions.js +38 -10
- package/dist/src/errors/index.d.ts +6 -4
- package/dist/src/errors/index.d.ts.map +1 -1
- package/dist/src/errors/index.js +6 -6
- package/dist/src/execution/error.d.ts +42 -11
- package/dist/src/execution/error.d.ts.map +1 -1
- package/dist/src/execution/error.js +53 -15
- package/dist/src/execution/index.d.ts +18 -18
- package/dist/src/execution/index.d.ts.map +1 -1
- package/dist/src/execution/index.js +27 -42
- package/dist/src/execution/utils.d.ts +55 -14
- package/dist/src/execution/utils.d.ts.map +1 -1
- package/dist/src/execution/utils.js +629 -82
- package/dist/src/index.d.ts +270 -38
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +89 -116
- package/dist/src/jwt-server/jcs.d.ts +1 -1
- package/dist/src/jwt-server/jcs.js +4 -2
- package/dist/src/modules/index.d.ts.map +1 -1
- package/dist/src/modules/index.js +0 -5
- package/dist/src/modules/read.d.ts +3 -2
- package/dist/src/modules/read.d.ts.map +1 -1
- package/dist/src/modules/read.js +52 -4
- package/dist/src/modules/validators/core.d.ts +6 -6
- package/dist/src/modules/validators/core.d.ts.map +1 -1
- package/dist/src/modules/validators/core.js +42 -39
- package/dist/src/modules/validators/cross-chain-permits.d.ts +21 -0
- package/dist/src/modules/validators/cross-chain-permits.d.ts.map +1 -0
- package/dist/src/modules/validators/cross-chain-permits.js +111 -0
- package/dist/src/modules/validators/permissions.d.ts +1 -0
- package/dist/src/modules/validators/permissions.d.ts.map +1 -1
- package/dist/src/modules/validators/permissions.js +192 -18
- package/dist/src/modules/validators/policies/claim/arbiters.d.ts +22 -0
- package/dist/src/modules/validators/policies/claim/arbiters.d.ts.map +1 -0
- package/dist/src/modules/validators/policies/claim/arbiters.js +57 -0
- package/dist/src/modules/validators/smart-sessions.d.ts +38 -9
- package/dist/src/modules/validators/smart-sessions.d.ts.map +1 -1
- package/dist/src/modules/validators/smart-sessions.js +509 -92
- package/dist/src/orchestrator/caip2.d.ts +16 -3
- package/dist/src/orchestrator/caip2.d.ts.map +1 -1
- package/dist/src/orchestrator/caip2.js +38 -6
- package/dist/src/orchestrator/client.d.ts +2 -1
- package/dist/src/orchestrator/client.d.ts.map +1 -1
- package/dist/src/orchestrator/client.js +50 -24
- package/dist/src/orchestrator/consts.d.ts +1 -1
- package/dist/src/orchestrator/consts.d.ts.map +1 -1
- package/dist/src/orchestrator/consts.js +1 -1
- package/dist/src/orchestrator/destinations.d.ts +24 -0
- package/dist/src/orchestrator/destinations.d.ts.map +1 -0
- package/dist/src/orchestrator/destinations.js +59 -0
- package/dist/src/orchestrator/error.d.ts +95 -5
- package/dist/src/orchestrator/error.d.ts.map +1 -1
- package/dist/src/orchestrator/error.js +260 -4
- package/dist/src/orchestrator/index.d.ts +7 -5
- package/dist/src/orchestrator/index.d.ts.map +1 -1
- package/dist/src/orchestrator/index.js +4 -3
- package/dist/src/orchestrator/registry.d.ts +2 -1
- package/dist/src/orchestrator/registry.d.ts.map +1 -1
- package/dist/src/orchestrator/registry.js +13 -0
- package/dist/src/orchestrator/types.d.ts +92 -29
- package/dist/src/orchestrator/types.d.ts.map +1 -1
- package/dist/src/orchestrator/types.js +1 -5
- package/dist/src/orchestrator/wire.d.ts +30 -0
- package/dist/src/orchestrator/wire.d.ts.map +1 -0
- package/dist/src/orchestrator/wire.gen.d.ts +4290 -0
- package/dist/src/orchestrator/wire.gen.d.ts.map +1 -0
- package/dist/src/orchestrator/wire.gen.js +1 -0
- package/dist/src/orchestrator/wire.js +1 -0
- package/dist/src/smart-sessions/index.d.ts +6 -0
- package/dist/src/smart-sessions/index.d.ts.map +1 -0
- package/dist/src/smart-sessions/index.js +7 -0
- package/dist/src/types.d.ts +299 -31
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/index.d.ts +59 -0
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +59 -0
- package/package.json +5 -9
- package/dist/src/actions/recovery.d.ts +0 -33
- package/dist/src/actions/recovery.d.ts.map +0 -1
- package/dist/src/actions/recovery.js +0 -189
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import { concat, createPublicClient, createWalletClient, encodeAbiParameters, encodePacked, hashDomain, hashMessage, hashStruct, hashTypedData, isAddress, keccak256, publicActions, toHex, } from 'viem';
|
|
1
|
+
import { concat, createPublicClient, createWalletClient, encodeAbiParameters, encodePacked, hashDomain, hashMessage, hashStruct, hashTypedData, isAddress, keccak256, pad, publicActions, toHex, } from 'viem';
|
|
2
2
|
import { entryPoint07Address, getUserOperationHash, } from 'viem/account-abstraction';
|
|
3
3
|
import { wrapTypedDataSignature } from 'viem/experimental/erc7739';
|
|
4
|
-
import { EoaAccountMustHaveAccountError, EoaSigningMethodNotConfiguredError, FactoryArgsNotAvailableError, getAccountProvider, getAddress, getEip712Domain, getEip1271Signature, getEip7702InitCall, getEmissarySignature,
|
|
5
|
-
import { convertOwnerSetToSignerSet } from '../accounts/signing/common.js';
|
|
4
|
+
import { EoaAccountMustHaveAccountError, EoaSigningMethodNotConfiguredError, FactoryArgsNotAvailableError, getAccountProvider, getAddress, getEip712Domain, getEip1271Signature, getEip7702InitCall, getEmissarySignature, getInitCode, getOwnerEcdsaSignature, getOwnerPasskeySignature, getSmartAccount, getTypedDataPackedSignature, is7702, packAccountSignature, toErc6492Signature, } from '../accounts/index.js';
|
|
5
|
+
import { convertOwnerSetToSignerSet, packMultiFactorOwnerSignatures, packPasskeyOwnerSignatures, } from '../accounts/signing/common.js';
|
|
6
6
|
import { K1_DEFAULT_VALIDATOR_ADDRESS } from '../accounts/startale.js';
|
|
7
7
|
import { createTransport, getBundlerClient, } from '../accounts/utils.js';
|
|
8
8
|
import { createAuthProvider } from '../auth/provider.js';
|
|
9
9
|
import { buildMockSignature, DUMMY_PRECLAIMOP_SELECTOR, DUMMY_PRECLAIMOP_TARGET, getOwnerValidator, getPermissionId, getSmartSessionValidator, isSessionEnabled, } from '../modules/validators/index.js';
|
|
10
|
-
import { getMultiFactorValidator,
|
|
10
|
+
import { getMultiFactorValidator, getValidator as getValidatorModule, getWebAuthnValidator, supportsEip712, } from '../modules/validators/core.js';
|
|
11
11
|
import { buildPermit2ClaimPolicyCalldata, } from '../modules/validators/policies/claim/permit2.js';
|
|
12
|
-
import { resolvePermit2ClaimPolicy, } from '../modules/validators/smart-sessions.js';
|
|
12
|
+
import { resolvePermit2ClaimPolicy, selectPermit2ClaimPolicyForMessage, } from '../modules/validators/smart-sessions.js';
|
|
13
13
|
import { getOrchestrator, } from '../orchestrator/index.js';
|
|
14
|
+
import { isNonEvmChainId, toCaip2 } from '../orchestrator/caip2.js';
|
|
15
|
+
import { getChainId, isNonEvmChain, } from '../orchestrator/destinations.js';
|
|
14
16
|
import { getChainById, getTokenAddress, resolveTokenAddress, } from '../orchestrator/registry.js';
|
|
15
|
-
import { SIG_MODE_EMISSARY_EXECUTION_ERC1271,
|
|
17
|
+
import { SIG_MODE_EMISSARY_EXECUTION_ERC1271, SIG_MODE_ERC1271, } from '../orchestrator/types.js';
|
|
16
18
|
import { convertBigIntFields } from '../orchestrator/utils.js';
|
|
17
|
-
import { Eip7702InitSignatureRequiredError, QuoteNotInPreparedTransactionError,
|
|
19
|
+
import { Eip7702InitSignatureRequiredError, IndependentSigningNotSupportedError, InsufficientOwnerSignaturesError, InvalidOwnerSigningOptionsError, InvalidSourceCallsError, MismatchedOwnerSignaturesError, QuoteNotInPreparedTransactionError, UnknownOwnerError, } from './error.js';
|
|
18
20
|
function isResolvedSessionSignerSet(signers) {
|
|
19
21
|
return (signers?.type === 'experimental_session' && 'verifyExecutions' in signers);
|
|
20
22
|
}
|
|
@@ -25,16 +27,61 @@ async function resolveSignersForChain(config, signers, chainId) {
|
|
|
25
27
|
const resolved = resolveSessionForChain(signers, chainId);
|
|
26
28
|
const enabled = await isSessionEnabled(getAddress(config), config.provider, resolved.session, config.useDevContracts);
|
|
27
29
|
const enableData = enabled ? undefined : resolved.enableData;
|
|
28
|
-
const verifyExecutions = resolved.verifyExecutions ??
|
|
29
|
-
signers.verifyExecutions ??
|
|
30
|
-
resolved.session.hasExplicitPermissions;
|
|
31
30
|
return {
|
|
32
31
|
type: 'experimental_session',
|
|
33
32
|
session: resolved.session,
|
|
34
33
|
enableData,
|
|
35
|
-
|
|
34
|
+
// First use of any session must go through the emissary's verifyExecution
|
|
35
|
+
// path: that's the only mechanism that consumes enableData + calls setConfig
|
|
36
|
+
// to install the session on-chain. Once installed, steady-state signing can
|
|
37
|
+
// drop to mode 1 unless the session has explicit permissions to enforce.
|
|
38
|
+
verifyExecutions: !enabled || resolved.session.hasExplicitPermissions,
|
|
36
39
|
};
|
|
37
40
|
}
|
|
41
|
+
// Picks the single signature mode that matches the bytes shape the SDK will
|
|
42
|
+
// actually sign, so the on-chain dispatcher hits the right validator on the
|
|
43
|
+
// first try instead of falling through a hybrid.
|
|
44
|
+
async function resolveSignatureMode(config, signers, sourceChains, targetChainId,
|
|
45
|
+
// Optional pre-resolved per-chain signer sets. When supplied, reuse them
|
|
46
|
+
// instead of resolving again — each resolveSignersForChain does an
|
|
47
|
+
// isSessionEnabled RPC, so sharing one map across mockSignatures, sigMode, and
|
|
48
|
+
// preClaim avoids duplicate calls per intent. The map also defines *which*
|
|
49
|
+
// chains carry a session: the caller omits a non-EVM target (it has no session
|
|
50
|
+
// validator), so deriving the chain set from the map's keys keeps us from
|
|
51
|
+
// resolving — and throwing on — a target with no configured session.
|
|
52
|
+
preResolved) {
|
|
53
|
+
if (config.account?.type === 'eoa') {
|
|
54
|
+
return SIG_MODE_ERC1271;
|
|
55
|
+
}
|
|
56
|
+
if (signers?.type !== 'experimental_session') {
|
|
57
|
+
return SIG_MODE_ERC1271;
|
|
58
|
+
}
|
|
59
|
+
const chainIds = preResolved
|
|
60
|
+
? [...preResolved.keys()]
|
|
61
|
+
: [...new Set([...(sourceChains ?? []).map((c) => c.id), targetChainId])];
|
|
62
|
+
const resolvedSet = await Promise.all(chainIds.map((chainId) =>
|
|
63
|
+
// Use has() not `?? resolve` so a cached `undefined` entry is still reused
|
|
64
|
+
// (get() can't distinguish "missing key" from "present but undefined").
|
|
65
|
+
preResolved?.has(chainId)
|
|
66
|
+
? preResolved.get(chainId)
|
|
67
|
+
: resolveSignersForChain(config, signers, chainId)));
|
|
68
|
+
let anyVerifyExecutions = false;
|
|
69
|
+
for (const resolved of resolvedSet) {
|
|
70
|
+
if (!isResolvedSessionSignerSet(resolved)) {
|
|
71
|
+
return SIG_MODE_ERC1271;
|
|
72
|
+
}
|
|
73
|
+
// Conservative: one chain with verifyExecutions=true forces mode 5 across
|
|
74
|
+
// all origin signatures — a single mode field can't describe divergence.
|
|
75
|
+
if (resolved.verifyExecutions)
|
|
76
|
+
anyVerifyExecutions = true;
|
|
77
|
+
}
|
|
78
|
+
// Mode 4 (pure emissary-execution) is unused: signIntentTypedData always
|
|
79
|
+
// emits a dual sig for the intent's origin/destination signatures when
|
|
80
|
+
// verifyExecutions=true, so mode 5 is the right hybrid.
|
|
81
|
+
return anyVerifyExecutions
|
|
82
|
+
? SIG_MODE_EMISSARY_EXECUTION_ERC1271
|
|
83
|
+
: SIG_MODE_ERC1271;
|
|
84
|
+
}
|
|
38
85
|
function resolveSessionForChain(signers, chainId) {
|
|
39
86
|
if ('sessions' in signers) {
|
|
40
87
|
const config = signers.sessions[chainId];
|
|
@@ -46,13 +93,23 @@ function resolveSessionForChain(signers, chainId) {
|
|
|
46
93
|
return { session: signers.session, enableData: signers.enableData };
|
|
47
94
|
}
|
|
48
95
|
async function prepareTransaction(config, transaction) {
|
|
49
|
-
const { sourceChains, targetChain, tokenRequests, signers, sponsored, eip7702InitSignature, settlementLayers, sourceAssets,
|
|
96
|
+
const { sourceChains, targetChain, tokenRequests, signers, sponsored, eip7702InitSignature, settlementLayers, sourceAssets, auxiliaryFunds, appFees, account, recipient, sourceCalls, } = getTransactionParams(transaction);
|
|
50
97
|
const accountAddress = getAddress(config);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
98
|
+
// Destination calls (transaction.calls on a cross-chain transaction) are
|
|
99
|
+
// executed on the destination chain by the solver/account. For non-EVM
|
|
100
|
+
// destinations we can't resolve arbitrary EVM calls; assert there are
|
|
101
|
+
// none and route an empty list through.
|
|
102
|
+
let resolvedCalls;
|
|
103
|
+
if (isNonEvmChain(targetChain)) {
|
|
104
|
+
if (transaction.calls && transaction.calls.length > 0) {
|
|
105
|
+
throw new Error(`Destination calls are not supported for non-EVM target chain ${targetChain.name} (${targetChain.caip2})`);
|
|
106
|
+
}
|
|
107
|
+
resolvedCalls = [];
|
|
54
108
|
}
|
|
55
|
-
|
|
109
|
+
else {
|
|
110
|
+
resolvedCalls = await resolveCallInputs(transaction.calls, config, targetChain, accountAddress);
|
|
111
|
+
}
|
|
112
|
+
const prepared = await prepareTransactionAsIntent(config, sourceChains, targetChain, resolvedCalls, transaction.gasLimit, tokenRequests, recipient, sponsored, eip7702InitSignature, settlementLayers, sourceAssets, auxiliaryFunds, account, signers, sourceCalls, appFees);
|
|
56
113
|
return {
|
|
57
114
|
quotes: prepared.quotes,
|
|
58
115
|
intentInput: prepared.intentInput,
|
|
@@ -96,6 +153,9 @@ function getTransactionMessages(_config, preparedTransaction, options) {
|
|
|
96
153
|
return getIntentMessages(quote.signData);
|
|
97
154
|
}
|
|
98
155
|
async function signTransaction(config, preparedTransaction, options) {
|
|
156
|
+
if (options && 'owner' in options) {
|
|
157
|
+
return signTransactionAsOwner(config, preparedTransaction, options);
|
|
158
|
+
}
|
|
99
159
|
const { signers } = getTransactionParams(preparedTransaction.transaction);
|
|
100
160
|
const quote = resolveQuote(preparedTransaction.quotes, options);
|
|
101
161
|
const targetChain = 'targetChain' in preparedTransaction.transaction
|
|
@@ -113,6 +173,349 @@ async function signTransaction(config, preparedTransaction, options) {
|
|
|
113
173
|
targetExecutionSignature,
|
|
114
174
|
};
|
|
115
175
|
}
|
|
176
|
+
function assertIndependentSigningSupported(config, preparedTransaction) {
|
|
177
|
+
if (config.account?.type === 'eoa') {
|
|
178
|
+
throw new IndependentSigningNotSupportedError({
|
|
179
|
+
context: { reason: 'eoa-account' },
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
const { signers } = getTransactionParams(preparedTransaction.transaction);
|
|
183
|
+
if (signers?.type === 'experimental_session') {
|
|
184
|
+
throw new IndependentSigningNotSupportedError({
|
|
185
|
+
context: { reason: 'smart-session' },
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
const validator = getOwnerValidator(config);
|
|
189
|
+
const selectedValidator = getValidator(config, signers);
|
|
190
|
+
if (!selectedValidator ||
|
|
191
|
+
selectedValidator.address.toLowerCase() !== validator.address.toLowerCase()) {
|
|
192
|
+
throw new IndependentSigningNotSupportedError({
|
|
193
|
+
context: { reason: 'non-owner-validator' },
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (validator.address.toLowerCase() ===
|
|
197
|
+
K1_DEFAULT_VALIDATOR_ADDRESS.toLowerCase()) {
|
|
198
|
+
throw new IndependentSigningNotSupportedError({
|
|
199
|
+
context: { reason: 'erc7739-validator' },
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (!config.owners) {
|
|
203
|
+
throw new IndependentSigningNotSupportedError({
|
|
204
|
+
context: { reason: 'missing-owner-set' },
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return { owners: config.owners, signers, validator };
|
|
208
|
+
}
|
|
209
|
+
function getValidatorIdKey(id) {
|
|
210
|
+
if (typeof id === 'number' && (!Number.isSafeInteger(id) || id < 0)) {
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
return pad(toHex(id), { size: 12 }).toLowerCase();
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function getMultiFactorOwnerFactors(owners, signers) {
|
|
221
|
+
if (signers?.type === 'owner' && signers.kind === 'multi-factor') {
|
|
222
|
+
return signers.validators.map((factor) => {
|
|
223
|
+
switch (factor.type) {
|
|
224
|
+
case 'ecdsa':
|
|
225
|
+
return {
|
|
226
|
+
id: factor.id,
|
|
227
|
+
owners: {
|
|
228
|
+
type: 'ecdsa',
|
|
229
|
+
accounts: factor.accounts,
|
|
230
|
+
threshold: factor.accounts.length,
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
case 'passkey':
|
|
234
|
+
return {
|
|
235
|
+
id: factor.id,
|
|
236
|
+
owners: {
|
|
237
|
+
type: 'passkey',
|
|
238
|
+
accounts: factor.accounts,
|
|
239
|
+
threshold: factor.accounts.length,
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
return owners.validators.map((factor, id) => ({ id, owners: factor }));
|
|
246
|
+
}
|
|
247
|
+
function resolveAtomicOwnerSet(owners, validatorId, signers) {
|
|
248
|
+
if (owners.type !== 'multi-factor') {
|
|
249
|
+
return validatorId === undefined ? { id: 0, owners } : undefined;
|
|
250
|
+
}
|
|
251
|
+
if (validatorId === undefined)
|
|
252
|
+
return undefined;
|
|
253
|
+
const key = getValidatorIdKey(validatorId);
|
|
254
|
+
if (!key)
|
|
255
|
+
return undefined;
|
|
256
|
+
return getMultiFactorOwnerFactors(owners, signers).find((factor) => getValidatorIdKey(factor.id) === key);
|
|
257
|
+
}
|
|
258
|
+
function getAtomicOwnerAccounts(owners) {
|
|
259
|
+
switch (owners.type) {
|
|
260
|
+
case 'ecdsa':
|
|
261
|
+
case 'passkey':
|
|
262
|
+
return owners.accounts;
|
|
263
|
+
case 'ens':
|
|
264
|
+
return owners.owners.map(({ account }) => account);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function getAtomicOwnerThreshold(owners) {
|
|
268
|
+
return owners.threshold ?? 1;
|
|
269
|
+
}
|
|
270
|
+
function isWebAuthnAccount(account) {
|
|
271
|
+
return account.type === 'webAuthn';
|
|
272
|
+
}
|
|
273
|
+
function getAccountIdentity(account) {
|
|
274
|
+
return isWebAuthnAccount(account)
|
|
275
|
+
? account.publicKey.toLowerCase()
|
|
276
|
+
: account.address.toLowerCase();
|
|
277
|
+
}
|
|
278
|
+
function getAtomicSignatureIdentity(signature) {
|
|
279
|
+
return signature.kind === 'passkey'
|
|
280
|
+
? signature.publicKey.toLowerCase()
|
|
281
|
+
: signature.signer.toLowerCase();
|
|
282
|
+
}
|
|
283
|
+
function isOwnerMember(owners, account) {
|
|
284
|
+
if ((owners.type === 'passkey') !== isWebAuthnAccount(account))
|
|
285
|
+
return false;
|
|
286
|
+
const identity = getAccountIdentity(account);
|
|
287
|
+
return getAtomicOwnerAccounts(owners).some((configured) => getAccountIdentity(configured) === identity);
|
|
288
|
+
}
|
|
289
|
+
async function signTransactionAsOwner(config, preparedTransaction, options) {
|
|
290
|
+
const { owners, signers } = assertIndependentSigningSupported(config, preparedTransaction);
|
|
291
|
+
const factor = resolveAtomicOwnerSet(owners, options.validatorId, signers);
|
|
292
|
+
if (!factor) {
|
|
293
|
+
throw new InvalidOwnerSigningOptionsError({
|
|
294
|
+
context: { validatorId: options.validatorId },
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
const atomicOwners = factor.owners;
|
|
298
|
+
if (!isOwnerMember(atomicOwners, options.owner)) {
|
|
299
|
+
throw new UnknownOwnerError({
|
|
300
|
+
context: {
|
|
301
|
+
...(isWebAuthnAccount(options.owner)
|
|
302
|
+
? { publicKey: options.owner.publicKey }
|
|
303
|
+
: { signer: options.owner.address }),
|
|
304
|
+
validatorId: options.validatorId,
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
const quote = resolveQuote(preparedTransaction.quotes, options.intentId === undefined ? undefined : { intentId: options.intentId });
|
|
309
|
+
const { origin } = getIntentMessages(quote.signData);
|
|
310
|
+
const atomicSigners = convertOwnerSetToSignerSet(atomicOwners);
|
|
311
|
+
if (atomicSigners.type !== 'owner' || atomicSigners.kind === 'multi-factor') {
|
|
312
|
+
throw new IndependentSigningNotSupportedError();
|
|
313
|
+
}
|
|
314
|
+
const validatorSupportsEip712 = supportsEip712(getValidatorModule(atomicOwners));
|
|
315
|
+
let signature;
|
|
316
|
+
if (atomicSigners.kind === 'ecdsa' && !isWebAuthnAccount(options.owner)) {
|
|
317
|
+
const signatures = [];
|
|
318
|
+
for (const typedData of origin) {
|
|
319
|
+
signatures.push(await getOwnerEcdsaSignature(config, getChainById(typedData.domain?.chainId), typedData, options.owner, atomicSigners.module, validatorSupportsEip712));
|
|
320
|
+
}
|
|
321
|
+
signature = {
|
|
322
|
+
kind: 'ecdsa',
|
|
323
|
+
signer: options.owner.address,
|
|
324
|
+
origin: signatures,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
else if (atomicSigners.kind === 'passkey' &&
|
|
328
|
+
isWebAuthnAccount(options.owner)) {
|
|
329
|
+
const signatures = [];
|
|
330
|
+
for (const typedData of origin) {
|
|
331
|
+
signatures.push(await getOwnerPasskeySignature(config, typedData, options.owner, validatorSupportsEip712));
|
|
332
|
+
}
|
|
333
|
+
signature = {
|
|
334
|
+
kind: 'passkey',
|
|
335
|
+
publicKey: options.owner.publicKey,
|
|
336
|
+
origin: signatures,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
throw new UnknownOwnerError({
|
|
341
|
+
context: { validatorId: options.validatorId },
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
return owners.type === 'multi-factor'
|
|
345
|
+
? {
|
|
346
|
+
intentId: quote.intentId,
|
|
347
|
+
kind: 'multi-factor',
|
|
348
|
+
validatorId: factor.id,
|
|
349
|
+
signature,
|
|
350
|
+
}
|
|
351
|
+
: { intentId: quote.intentId, ...signature };
|
|
352
|
+
}
|
|
353
|
+
function orderAtomicOwnerSignatures(owners, signatures, originCount, validatorId) {
|
|
354
|
+
const expectedKind = owners.type === 'passkey' ? 'passkey' : 'ecdsa';
|
|
355
|
+
const byIdentity = new Map();
|
|
356
|
+
const configuredIdentities = new Set(getAtomicOwnerAccounts(owners).map(getAccountIdentity));
|
|
357
|
+
for (const signature of signatures) {
|
|
358
|
+
if (signature.kind !== expectedKind ||
|
|
359
|
+
signature.origin.length !== originCount) {
|
|
360
|
+
throw new MismatchedOwnerSignaturesError({
|
|
361
|
+
context: {
|
|
362
|
+
expectedKind,
|
|
363
|
+
expectedOriginCount: originCount,
|
|
364
|
+
validatorId,
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
const identity = getAtomicSignatureIdentity(signature);
|
|
369
|
+
if (!configuredIdentities.has(identity)) {
|
|
370
|
+
throw new UnknownOwnerError({
|
|
371
|
+
context: {
|
|
372
|
+
...(signature.kind === 'passkey'
|
|
373
|
+
? { publicKey: signature.publicKey }
|
|
374
|
+
: { signer: signature.signer }),
|
|
375
|
+
validatorId,
|
|
376
|
+
},
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
if (!byIdentity.has(identity))
|
|
380
|
+
byIdentity.set(identity, signature);
|
|
381
|
+
}
|
|
382
|
+
const ordered = [...configuredIdentities].flatMap((identity) => {
|
|
383
|
+
const signature = byIdentity.get(identity);
|
|
384
|
+
return signature ? [signature] : [];
|
|
385
|
+
});
|
|
386
|
+
const required = getAtomicOwnerThreshold(owners);
|
|
387
|
+
if (ordered.length < required) {
|
|
388
|
+
throw new InsufficientOwnerSignaturesError({
|
|
389
|
+
required,
|
|
390
|
+
provided: ordered.length,
|
|
391
|
+
validatorId,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
return ordered;
|
|
395
|
+
}
|
|
396
|
+
function packAtomicOwnerSignatures(owners, signatures, originIndex, chain, accountAddress) {
|
|
397
|
+
const signers = convertOwnerSetToSignerSet(owners);
|
|
398
|
+
if (owners.type === 'passkey' &&
|
|
399
|
+
signers.type === 'owner' &&
|
|
400
|
+
signers.kind === 'passkey') {
|
|
401
|
+
const passkeySignatures = signatures.map((signature) => {
|
|
402
|
+
if (signature.kind !== 'passkey') {
|
|
403
|
+
throw new MismatchedOwnerSignaturesError();
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
publicKey: signature.publicKey,
|
|
407
|
+
signature: signature.origin[originIndex],
|
|
408
|
+
};
|
|
409
|
+
});
|
|
410
|
+
return packPasskeyOwnerSignatures(chain, accountAddress, passkeySignatures.map(({ publicKey }) => publicKey), passkeySignatures.map(({ signature }) => signature), signers.module);
|
|
411
|
+
}
|
|
412
|
+
return concat(signatures.map((signature) => {
|
|
413
|
+
if (signature.kind !== 'ecdsa') {
|
|
414
|
+
throw new MismatchedOwnerSignaturesError();
|
|
415
|
+
}
|
|
416
|
+
return signature.origin[originIndex];
|
|
417
|
+
}));
|
|
418
|
+
}
|
|
419
|
+
async function assembleTransaction(config, preparedTransaction, signatures) {
|
|
420
|
+
const { owners, signers, validator } = assertIndependentSigningSupported(config, preparedTransaction);
|
|
421
|
+
if (signatures.length === 0) {
|
|
422
|
+
throw new InsufficientOwnerSignaturesError({
|
|
423
|
+
required: owners.threshold ?? 1,
|
|
424
|
+
provided: 0,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
const intentIds = [...new Set(signatures.map(({ intentId }) => intentId))];
|
|
428
|
+
if (intentIds.length !== 1) {
|
|
429
|
+
throw new MismatchedOwnerSignaturesError({ context: { intentIds } });
|
|
430
|
+
}
|
|
431
|
+
const quote = resolveQuote(preparedTransaction.quotes, {
|
|
432
|
+
intentId: intentIds[0],
|
|
433
|
+
});
|
|
434
|
+
const { origin } = getIntentMessages(quote.signData);
|
|
435
|
+
const accountAddress = getAddress(config);
|
|
436
|
+
const validatorConfig = {
|
|
437
|
+
address: validator.address,
|
|
438
|
+
isRoot: true,
|
|
439
|
+
};
|
|
440
|
+
let directSignatures;
|
|
441
|
+
let factorSignatures;
|
|
442
|
+
if (owners.type === 'multi-factor') {
|
|
443
|
+
const factors = getMultiFactorOwnerFactors(owners, signers);
|
|
444
|
+
const factorsById = new Map(factors.flatMap((factor) => {
|
|
445
|
+
const key = getValidatorIdKey(factor.id);
|
|
446
|
+
return key ? [[key, factor]] : [];
|
|
447
|
+
}));
|
|
448
|
+
const grouped = new Map();
|
|
449
|
+
for (const signature of signatures) {
|
|
450
|
+
if (signature.kind !== 'multi-factor') {
|
|
451
|
+
throw new MismatchedOwnerSignaturesError({
|
|
452
|
+
context: { expectedKind: 'multi-factor' },
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
const key = getValidatorIdKey(signature.validatorId);
|
|
456
|
+
if (!key || !factorsById.has(key)) {
|
|
457
|
+
throw new MismatchedOwnerSignaturesError({
|
|
458
|
+
context: { validatorId: signature.validatorId },
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
const current = grouped.get(key) ?? [];
|
|
462
|
+
current.push(signature.signature);
|
|
463
|
+
grouped.set(key, current);
|
|
464
|
+
}
|
|
465
|
+
factorSignatures = factors.flatMap((factor) => {
|
|
466
|
+
const key = getValidatorIdKey(factor.id);
|
|
467
|
+
const groupedSignatures = key ? grouped.get(key) : undefined;
|
|
468
|
+
if (!groupedSignatures)
|
|
469
|
+
return [];
|
|
470
|
+
return [
|
|
471
|
+
{
|
|
472
|
+
id: factor.id,
|
|
473
|
+
owners: factor.owners,
|
|
474
|
+
signatures: orderAtomicOwnerSignatures(factor.owners, groupedSignatures, origin.length, factor.id),
|
|
475
|
+
},
|
|
476
|
+
];
|
|
477
|
+
});
|
|
478
|
+
const required = owners.threshold ?? 1;
|
|
479
|
+
if (factorSignatures.length < required) {
|
|
480
|
+
throw new InsufficientOwnerSignaturesError({
|
|
481
|
+
required,
|
|
482
|
+
provided: factorSignatures.length,
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
directSignatures = orderAtomicOwnerSignatures(owners, signatures.map((signature) => {
|
|
488
|
+
if (signature.kind === 'multi-factor') {
|
|
489
|
+
throw new MismatchedOwnerSignaturesError({
|
|
490
|
+
context: { expectedKind: owners.type },
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
const { intentId: _, ...atomicSignature } = signature;
|
|
494
|
+
return atomicSignature;
|
|
495
|
+
}), origin.length);
|
|
496
|
+
}
|
|
497
|
+
const originSignatures = [];
|
|
498
|
+
for (const [originIndex, typedData] of origin.entries()) {
|
|
499
|
+
const chain = getChainById(typedData.domain?.chainId);
|
|
500
|
+
const validatorSignature = factorSignatures
|
|
501
|
+
? packMultiFactorOwnerSignatures(factorSignatures.map((factor) => ({
|
|
502
|
+
id: factor.id,
|
|
503
|
+
validatorAddress: getValidatorModule(factor.owners).address,
|
|
504
|
+
signature: packAtomicOwnerSignatures(factor.owners, factor.signatures, originIndex, chain, accountAddress),
|
|
505
|
+
})))
|
|
506
|
+
: packAtomicOwnerSignatures(owners, directSignatures, originIndex, chain, accountAddress);
|
|
507
|
+
originSignatures.push(await packAccountSignature(config, validatorSignature, validatorConfig));
|
|
508
|
+
}
|
|
509
|
+
return {
|
|
510
|
+
quote,
|
|
511
|
+
quotes: preparedTransaction.quotes,
|
|
512
|
+
intentInput: preparedTransaction.intentInput,
|
|
513
|
+
transaction: preparedTransaction.transaction,
|
|
514
|
+
originSignatures,
|
|
515
|
+
destinationSignature: originSignatures.at(-1) ?? '0x',
|
|
516
|
+
targetExecutionSignature: undefined,
|
|
517
|
+
};
|
|
518
|
+
}
|
|
116
519
|
function resolveQuote(quotes, options) {
|
|
117
520
|
if (!options)
|
|
118
521
|
return quotes.best;
|
|
@@ -131,6 +534,11 @@ async function getTargetExecutionSignature(config, signData, targetChain, signer
|
|
|
131
534
|
if (!signData.targetExecution) {
|
|
132
535
|
return undefined;
|
|
133
536
|
}
|
|
537
|
+
// Target executions are EVM-only (smart-session validator on destination).
|
|
538
|
+
// Non-EVM destinations don't have a validator there to verify the sig.
|
|
539
|
+
if (isNonEvmChain(targetChain)) {
|
|
540
|
+
return undefined;
|
|
541
|
+
}
|
|
134
542
|
const resolvedSigners = await resolveSignersForChain(config, signers, targetChain.id);
|
|
135
543
|
if (!isResolvedSessionSignerSet(resolvedSigners) ||
|
|
136
544
|
!resolvedSigners.verifyExecutions) {
|
|
@@ -266,10 +674,16 @@ async function signAuthorizationsInternal(config, context) {
|
|
|
266
674
|
}
|
|
267
675
|
const accountAddress = getAddress(config);
|
|
268
676
|
const { contract: eip7702Contract } = getEip7702InitCall(config, eip7702InitSignature);
|
|
677
|
+
// EIP-7702 authorization is EVM-only — there's no contract to delegate
|
|
678
|
+
// to on Solana / Tron. Skip the destination chain entirely when it's
|
|
679
|
+
// non-EVM; source chains are always EVM by construction.
|
|
269
680
|
const chains = new Map();
|
|
270
|
-
for (const chain of
|
|
681
|
+
for (const chain of context.sourceChains ?? []) {
|
|
271
682
|
chains.set(chain.id, chain);
|
|
272
683
|
}
|
|
684
|
+
if (!isNonEvmChain(context.targetChain)) {
|
|
685
|
+
chains.set(context.targetChain.id, context.targetChain);
|
|
686
|
+
}
|
|
273
687
|
const authorizations = [];
|
|
274
688
|
for (const chain of chains.values()) {
|
|
275
689
|
const walletClient = createWalletClient({
|
|
@@ -314,10 +728,11 @@ function getTransactionParams(transaction) {
|
|
|
314
728
|
const gasLimit = transaction.gasLimit;
|
|
315
729
|
const settlementLayers = transaction.settlementLayers;
|
|
316
730
|
const sourceAssets = transaction.sourceAssets;
|
|
317
|
-
const feeAsset = transaction.feeAsset;
|
|
318
731
|
const auxiliaryFunds = transaction.auxiliaryFunds;
|
|
732
|
+
const appFees = transaction.appFees;
|
|
319
733
|
const account = transaction.experimental_accountOverride;
|
|
320
734
|
const recipient = transaction.recipient;
|
|
735
|
+
const sourceCalls = transaction.sourceCalls;
|
|
321
736
|
const tokenRequests = getTokenRequests(targetChain, initialTokenRequests);
|
|
322
737
|
return {
|
|
323
738
|
sourceChains,
|
|
@@ -329,15 +744,23 @@ function getTransactionParams(transaction) {
|
|
|
329
744
|
gasLimit,
|
|
330
745
|
settlementLayers,
|
|
331
746
|
sourceAssets,
|
|
332
|
-
feeAsset,
|
|
333
747
|
auxiliaryFunds,
|
|
748
|
+
appFees,
|
|
334
749
|
account,
|
|
335
750
|
recipient,
|
|
751
|
+
sourceCalls,
|
|
336
752
|
};
|
|
337
753
|
}
|
|
338
754
|
function getTokenRequests(targetChain, initialTokenRequests) {
|
|
339
|
-
|
|
340
|
-
|
|
755
|
+
// Non-EVM destinations carry SPL mint / Tron T-prefixed addresses that
|
|
756
|
+
// aren't valid 0x-hex; skip symbol/EVM-address validation here and let
|
|
757
|
+
// the orchestrator validate per its own schema. EVM destinations keep
|
|
758
|
+
// the existing strict check so a typo on Optimism still fails fast.
|
|
759
|
+
if (initialTokenRequests && !isNonEvmChain(targetChain)) {
|
|
760
|
+
// Inside this branch targetChain is a viem `Chain`, which excludes the
|
|
761
|
+
// non-EVM transaction variant — token requests are EVM-typed here.
|
|
762
|
+
const evmRequests = initialTokenRequests;
|
|
763
|
+
validateTokenSymbols(targetChain, evmRequests.map((tokenRequest) => tokenRequest.address));
|
|
341
764
|
}
|
|
342
765
|
return initialTokenRequests ?? [];
|
|
343
766
|
}
|
|
@@ -386,12 +809,46 @@ function getIntentAccount(config, eip7702InitSignature, account) {
|
|
|
386
809
|
delegations,
|
|
387
810
|
};
|
|
388
811
|
}
|
|
389
|
-
|
|
390
|
-
|
|
812
|
+
function cloneAuxiliaryFunds(auxiliaryFunds) {
|
|
813
|
+
if (!auxiliaryFunds)
|
|
814
|
+
return undefined;
|
|
815
|
+
return Object.fromEntries(Object.entries(auxiliaryFunds).map(([chainId, funds]) => [
|
|
816
|
+
Number(chainId),
|
|
817
|
+
{ ...funds },
|
|
818
|
+
]));
|
|
819
|
+
}
|
|
820
|
+
function toCallInput(sourceCall) {
|
|
821
|
+
const { provides: _provides, ...call } = sourceCall;
|
|
822
|
+
return call;
|
|
823
|
+
}
|
|
824
|
+
function addProvidedFunds(auxiliaryFunds, chainId, calls) {
|
|
825
|
+
let next = auxiliaryFunds;
|
|
826
|
+
for (const call of calls) {
|
|
827
|
+
for (const provided of call.provides ?? []) {
|
|
828
|
+
next ??= {};
|
|
829
|
+
next[chainId] ??= {};
|
|
830
|
+
const token = provided.token;
|
|
831
|
+
next[chainId][token] = (next[chainId][token] ?? 0n) + provided.amount;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
return next;
|
|
835
|
+
}
|
|
836
|
+
async function prepareTransactionAsIntent(config, sourceChains, targetChain, callInputs, gasLimit, tokenRequests, recipientInput, sponsored, eip7702InitSignature, settlementLayers, sourceAssets, auxiliaryFunds, account, signers, sourceCalls, appFees) {
|
|
837
|
+
const targetChainId = getChainId(targetChain);
|
|
838
|
+
const calls = parseCalls(callInputs, targetChainId);
|
|
391
839
|
const accountAccessList = createAccountAccessList(sourceChains, sourceAssets);
|
|
392
840
|
function getRecipient(recipient) {
|
|
393
841
|
if (typeof recipient === 'string') {
|
|
394
|
-
//
|
|
842
|
+
// Non-EVM-addressed recipients (Solana base58 / Tron T-prefix) carry no
|
|
843
|
+
// EVM smart-account semantics; the orchestrator schema requires
|
|
844
|
+
// accountType / setupOps to be unset. Emit just the address. HyperCore
|
|
845
|
+
// is EVM-addressed (eip155:1337) so it falls through to the EOA
|
|
846
|
+
// passthrough below — the orchestrator's HyperCore gate requires
|
|
847
|
+
// accountType: 'EOA'.
|
|
848
|
+
if (isNonEvmChainId(targetChainId)) {
|
|
849
|
+
return { address: recipient };
|
|
850
|
+
}
|
|
851
|
+
// EVM (and HyperCore) passthrough — assume EOA.
|
|
395
852
|
return {
|
|
396
853
|
address: recipient,
|
|
397
854
|
accountType: 'EOA',
|
|
@@ -404,25 +861,65 @@ async function prepareTransactionAsIntent(config, sourceChains, targetChain, cal
|
|
|
404
861
|
}
|
|
405
862
|
return getIntentAccount(recipient, eip7702InitSignature, account);
|
|
406
863
|
}
|
|
864
|
+
// Resolve per-chain signer state once (each resolveSignersForChain does an
|
|
865
|
+
// isSessionEnabled RPC) and reuse it for mock signatures, the signature mode,
|
|
866
|
+
// and preClaim ops. Exclude a non-EVM destination — it has no session
|
|
867
|
+
// validator, so resolving it would waste an RPC (or throw for per-chain
|
|
868
|
+
// session signers). resolveSignatureMode derives its chain set from this map's
|
|
869
|
+
// keys, so the excluded non-EVM target is never resolved there either.
|
|
870
|
+
const sessionChainIds = [
|
|
871
|
+
...new Set([
|
|
872
|
+
...(sourceChains ?? []).map((c) => c.id),
|
|
873
|
+
...(isNonEvmChain(targetChain) ? [] : [targetChainId]),
|
|
874
|
+
]),
|
|
875
|
+
];
|
|
876
|
+
const resolvedByChain = signers?.type === 'experimental_session'
|
|
877
|
+
? new Map(await Promise.all(sessionChainIds.map(async (chainId) => [
|
|
878
|
+
chainId,
|
|
879
|
+
await resolveSignersForChain(config, signers, chainId),
|
|
880
|
+
])))
|
|
881
|
+
: undefined;
|
|
882
|
+
// Per-chain mock signatures: enables accurate per-chain session validation gas
|
|
883
|
+
// simulation. Non-EVM destinations are excluded — they have no destination-side
|
|
884
|
+
// session validator. Each chain's mock sig carries that chain's own resolved
|
|
885
|
+
// shape (ENABLE / USE / ERC-1271). Note these are per-chain and may differ in
|
|
886
|
+
// shape, whereas `signatureMode` (below) is a single global value resolved
|
|
887
|
+
// conservatively (anyVerifyExecutions across chains → mode 5) — so in mixed
|
|
888
|
+
// states a chain's mock shape need not match the global signatureMode.
|
|
889
|
+
let mockSignatures;
|
|
890
|
+
if (resolvedByChain) {
|
|
891
|
+
const entries = [...resolvedByChain.entries()].flatMap(([chainId, resolved]) => {
|
|
892
|
+
// resolvedByChain is keyed by sessionChainIds, which already excludes a
|
|
893
|
+
// non-EVM target — so no per-entry target guard is needed here.
|
|
894
|
+
if (!isResolvedSessionSignerSet(resolved))
|
|
895
|
+
return [];
|
|
896
|
+
// Match the mock shape to what the real sig will be: ERC-1271 when not
|
|
897
|
+
// verifying executions; otherwise ENABLE if the session still needs
|
|
898
|
+
// installing (enableData present), else USE (already enabled, e.g.
|
|
899
|
+
// explicit permissions — its real sig is MODE_USE with no enableData).
|
|
900
|
+
const shape = !resolved.verifyExecutions
|
|
901
|
+
? 'erc1271'
|
|
902
|
+
: resolved.enableData !== undefined
|
|
903
|
+
? 'enable'
|
|
904
|
+
: 'use';
|
|
905
|
+
return [
|
|
906
|
+
[
|
|
907
|
+
String(chainId),
|
|
908
|
+
buildMockSignature(resolved.session, config.useDevContracts, sourceChains?.length ?? 1, chainId, shape),
|
|
909
|
+
],
|
|
910
|
+
];
|
|
911
|
+
});
|
|
912
|
+
// Leave mockSignatures undefined (not {}) when there are no eligible chains,
|
|
913
|
+
// so downstream consumers can treat presence as "per-chain sigs provided".
|
|
914
|
+
if (entries.length > 0)
|
|
915
|
+
mockSignatures = Object.fromEntries(entries);
|
|
916
|
+
}
|
|
407
917
|
const intentAccount = {
|
|
408
918
|
...getIntentAccount(config, eip7702InitSignature, account),
|
|
409
|
-
...(
|
|
410
|
-
// Per-chain map: enables accurate per-chain session validation gas simulation
|
|
411
|
-
mockSignatures: Object.fromEntries([
|
|
412
|
-
...new Set([
|
|
413
|
-
...(sourceChains ?? []).map((c) => c.id),
|
|
414
|
-
targetChain.id,
|
|
415
|
-
]),
|
|
416
|
-
].map((chainId) => [
|
|
417
|
-
String(chainId),
|
|
418
|
-
buildMockSignature(resolveSessionForChain(signers, chainId).session, config.useDevContracts, sourceChains?.length ?? 1, chainId),
|
|
419
|
-
])),
|
|
420
|
-
}),
|
|
919
|
+
...(mockSignatures && { mockSignatures }),
|
|
421
920
|
};
|
|
422
921
|
const recipient = getRecipient(recipientInput);
|
|
423
|
-
const signatureMode = signers
|
|
424
|
-
? SIG_MODE_EMISSARY_EXECUTION_ERC1271
|
|
425
|
-
: SIG_MODE_ERC1271_EMISSARY;
|
|
922
|
+
const signatureMode = await resolveSignatureMode(config, signers, sourceChains, targetChainId, resolvedByChain);
|
|
426
923
|
// For session signers that need enabling, pass a dummy preclaimop per source chain
|
|
427
924
|
// so the orchestrator bakes it into the bundle before computing its HMAC. The filler
|
|
428
925
|
// executes the op via verifyExecution in ENABLE mode, enabling the session on-chain
|
|
@@ -430,11 +927,11 @@ async function prepareTransactionAsIntent(config, sourceChains, targetChain, cal
|
|
|
430
927
|
// post-facto — because the orchestrator HMAC covers preClaimOps.
|
|
431
928
|
const preClaimExecutions = {};
|
|
432
929
|
if (signers?.type === 'experimental_session' && sourceChains) {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
930
|
+
// Reuse the shared per-chain resolution (source chains are a subset of
|
|
931
|
+
// sessionChainIds) instead of resolving — and re-doing isSessionEnabled — again.
|
|
932
|
+
for (const chain of sourceChains) {
|
|
933
|
+
const chainId = chain.id;
|
|
934
|
+
const resolved = resolvedByChain?.get(chainId);
|
|
438
935
|
if (!isResolvedSessionSignerSet(resolved))
|
|
439
936
|
continue;
|
|
440
937
|
const { enableData, verifyExecutions } = resolved;
|
|
@@ -449,10 +946,40 @@ async function prepareTransactionAsIntent(config, sourceChains, targetChain, cal
|
|
|
449
946
|
];
|
|
450
947
|
}
|
|
451
948
|
}
|
|
949
|
+
let combinedAuxiliaryFunds = cloneAuxiliaryFunds(auxiliaryFunds);
|
|
950
|
+
if (sourceCalls) {
|
|
951
|
+
const accountAddress = getAddress(config);
|
|
952
|
+
const allowedChainIds = new Set([
|
|
953
|
+
...(sourceChains ?? []).map((c) => c.id),
|
|
954
|
+
...(!isNonEvmChain(targetChain) ? [targetChainId] : []),
|
|
955
|
+
]);
|
|
956
|
+
for (const [chainIdStr, calls] of Object.entries(sourceCalls)) {
|
|
957
|
+
const chainId = Number(chainIdStr);
|
|
958
|
+
if (!allowedChainIds.has(chainId)) {
|
|
959
|
+
throw new InvalidSourceCallsError({ chainId });
|
|
960
|
+
}
|
|
961
|
+
const chain = sourceChains?.find((c) => c.id === chainId) ??
|
|
962
|
+
(!isNonEvmChain(targetChain) && targetChainId === chainId
|
|
963
|
+
? targetChain
|
|
964
|
+
: undefined);
|
|
965
|
+
if (!chain) {
|
|
966
|
+
throw new InvalidSourceCallsError({ chainId });
|
|
967
|
+
}
|
|
968
|
+
combinedAuxiliaryFunds = addProvidedFunds(combinedAuxiliaryFunds, chainId, calls);
|
|
969
|
+
const resolved = await resolveCallInputs(calls.map(toCallInput), config, chain, accountAddress);
|
|
970
|
+
const userExecutions = parseCalls(resolved, chainId);
|
|
971
|
+
if (userExecutions.length === 0)
|
|
972
|
+
continue;
|
|
973
|
+
preClaimExecutions[chainId] = [
|
|
974
|
+
...(preClaimExecutions[chainId] ?? []),
|
|
975
|
+
...userExecutions,
|
|
976
|
+
];
|
|
977
|
+
}
|
|
978
|
+
}
|
|
452
979
|
const metaIntent = {
|
|
453
|
-
destinationChainId:
|
|
980
|
+
destinationChainId: targetChainId,
|
|
454
981
|
tokenRequests: tokenRequests.map((tokenRequest) => ({
|
|
455
|
-
tokenAddress: resolveTokenAddress(tokenRequest.address,
|
|
982
|
+
tokenAddress: resolveTokenAddress(tokenRequest.address, targetChainId),
|
|
456
983
|
amount: tokenRequest.amount,
|
|
457
984
|
})),
|
|
458
985
|
recipient,
|
|
@@ -461,7 +988,6 @@ async function prepareTransactionAsIntent(config, sourceChains, targetChain, cal
|
|
|
461
988
|
destinationGasUnits: gasLimit,
|
|
462
989
|
accountAccessList,
|
|
463
990
|
options: {
|
|
464
|
-
feeToken: feeAsset,
|
|
465
991
|
sponsorSettings: sponsored
|
|
466
992
|
? typeof sponsored === 'object'
|
|
467
993
|
? {
|
|
@@ -477,19 +1003,20 @@ async function prepareTransactionAsIntent(config, sourceChains, targetChain, cal
|
|
|
477
1003
|
: undefined,
|
|
478
1004
|
settlementLayers,
|
|
479
1005
|
signatureMode,
|
|
480
|
-
auxiliaryFunds,
|
|
1006
|
+
auxiliaryFunds: combinedAuxiliaryFunds,
|
|
1007
|
+
appFees,
|
|
481
1008
|
},
|
|
482
1009
|
...(Object.keys(preClaimExecutions).length > 0 && { preClaimExecutions }),
|
|
483
1010
|
};
|
|
484
1011
|
const serializedIntent = convertBigIntFields(metaIntent);
|
|
485
1012
|
const orchestrator = getOrchestrator(config._authProvider ?? createAuthProvider(config), config.endpointUrl, config.headers);
|
|
486
|
-
const { routes } = await orchestrator.createQuote(metaIntent);
|
|
1013
|
+
const { routes, traceId } = await orchestrator.createQuote(metaIntent);
|
|
487
1014
|
const best = routes[0];
|
|
488
1015
|
if (!best) {
|
|
489
1016
|
throw new Error('Orchestrator returned no quote');
|
|
490
1017
|
}
|
|
491
1018
|
return {
|
|
492
|
-
quotes: { best, all: routes },
|
|
1019
|
+
quotes: { traceId, best, all: routes },
|
|
493
1020
|
intentInput: serializedIntent,
|
|
494
1021
|
};
|
|
495
1022
|
}
|
|
@@ -529,7 +1056,14 @@ async function signIntent(config, signData, targetChain, signers, targetExecutio
|
|
|
529
1056
|
const signature = await signIntentTypedData(config, originSigners, validator, isRoot, typedData, chain, targetExecution ?? false);
|
|
530
1057
|
originSignatures.push(signature);
|
|
531
1058
|
}
|
|
532
|
-
|
|
1059
|
+
// Non-EVM destinations have no destination-side validator, so there's no
|
|
1060
|
+
// session to resolve and no signer to derive. Skipping here matters for
|
|
1061
|
+
// per-chain experimental sessions: `resolveSessionForChain` would throw
|
|
1062
|
+
// `No session configured for chain {synthetic-id}` since users never
|
|
1063
|
+
// configure a session keyed by the synthetic Solana/Tron chain id.
|
|
1064
|
+
const destinationSigners = isNonEvmChain(targetChain)
|
|
1065
|
+
? undefined
|
|
1066
|
+
: await resolveSignersForChain(config, signers, targetChain.id);
|
|
533
1067
|
const destinationSignature = await getDestinationSignature(config, destinationSigners, validator, isRoot, targetChain, destination, originSignatures, targetExecution ?? false);
|
|
534
1068
|
return {
|
|
535
1069
|
originSignatures,
|
|
@@ -537,18 +1071,25 @@ async function signIntent(config, signData, targetChain, signers, targetExecutio
|
|
|
537
1071
|
};
|
|
538
1072
|
}
|
|
539
1073
|
async function getDestinationSignature(config, signers, validator, isRoot, targetChain, destination, originSignatures, targetExecution) {
|
|
540
|
-
//
|
|
541
|
-
// session enable data
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
1074
|
+
// Non-EVM destinations have no destination-side validator, no
|
|
1075
|
+
// eip712Domain(), no smart session enable data. Settlement is solver-
|
|
1076
|
+
// mediated on the orchestrator side and the origin signatures alone
|
|
1077
|
+
// authorize the bundle. Fall through to the "reuse last origin sig"
|
|
1078
|
+
// branch instead of trying to sign on Solana / Tron.
|
|
1079
|
+
if (!isNonEvmChain(targetChain)) {
|
|
1080
|
+
// Smart sessions require a separate destination signature because the
|
|
1081
|
+
// session enable data differs per chain
|
|
1082
|
+
if (signers?.type === 'experimental_session') {
|
|
1083
|
+
return await signDestinationSeparately(config, signers, validator, isRoot, targetChain, destination, targetExecution);
|
|
1084
|
+
}
|
|
1085
|
+
// ERC-7739 with K1 validator requires a separate destination signature because
|
|
1086
|
+
// the account's eip712Domain() returns the target chain's chainId, which differs
|
|
1087
|
+
// from the origin chain used for the last origin signature
|
|
1088
|
+
const isK1Validator = validator.address.toLowerCase() ===
|
|
1089
|
+
K1_DEFAULT_VALIDATOR_ADDRESS.toLowerCase();
|
|
1090
|
+
if (isK1Validator && supportsEip712(validator)) {
|
|
1091
|
+
return await signDestinationSeparately(config, signers, validator, isRoot, targetChain, destination, targetExecution);
|
|
1092
|
+
}
|
|
552
1093
|
}
|
|
553
1094
|
const lastOriginSignature = originSignatures.at(-1);
|
|
554
1095
|
return typeof lastOriginSignature === 'object'
|
|
@@ -628,7 +1169,17 @@ function resolveClaimPolicyData(signers, parameters) {
|
|
|
628
1169
|
typeof msg.deadline !== 'bigint') {
|
|
629
1170
|
return undefined;
|
|
630
1171
|
}
|
|
631
|
-
|
|
1172
|
+
const message = parameters.message;
|
|
1173
|
+
// A session may carry several claim policies (a user policy plus one or
|
|
1174
|
+
// more synthesized cross-chain permits). The on-chain emissary validates
|
|
1175
|
+
// against the policy that matches this Permit2 message, and the calldata
|
|
1176
|
+
// layout is policy-specific — so we must build it from the matching policy,
|
|
1177
|
+
// not blindly `claimPolicies[0]`.
|
|
1178
|
+
const claimPolicy = selectPermit2ClaimPolicyForMessage(signers.session.claimPolicies, message);
|
|
1179
|
+
if (!claimPolicy) {
|
|
1180
|
+
return undefined;
|
|
1181
|
+
}
|
|
1182
|
+
return buildPermit2ClaimPolicyCalldata(resolvePermit2ClaimPolicy(claimPolicy), message);
|
|
632
1183
|
}
|
|
633
1184
|
async function signIntentTypedData(config, signers, validator, isRoot, parameters, chain, targetExecution) {
|
|
634
1185
|
if (supportsEip712(validator)) {
|
|
@@ -757,7 +1308,7 @@ async function submitIntentInternal(config, sourceChains, targetChain, quote, or
|
|
|
757
1308
|
...(authorizations.length > 0 && {
|
|
758
1309
|
authorizations: {
|
|
759
1310
|
sponsor: authorizations.map((authorization) => ({
|
|
760
|
-
chainId: authorization.chainId,
|
|
1311
|
+
chainId: toCaip2(authorization.chainId),
|
|
761
1312
|
address: authorization.address,
|
|
762
1313
|
nonce: authorization.nonce,
|
|
763
1314
|
yParity: authorization.yParity ?? 0,
|
|
@@ -774,8 +1325,9 @@ async function submitIntentInternal(config, sourceChains, targetChain, quote, or
|
|
|
774
1325
|
return {
|
|
775
1326
|
type: 'intent',
|
|
776
1327
|
id: response.intentId,
|
|
1328
|
+
traceId: response.traceId,
|
|
777
1329
|
sourceChains: sourceChains?.map((chain) => chain.id),
|
|
778
|
-
targetChain: targetChain
|
|
1330
|
+
targetChain: getChainId(targetChain),
|
|
779
1331
|
};
|
|
780
1332
|
}
|
|
781
1333
|
async function getValidatorAccount(config, signers, publicClient, chain) {
|
|
@@ -787,13 +1339,7 @@ async function getValidatorAccount(config, signers, publicClient, chain) {
|
|
|
787
1339
|
if (withOwner) {
|
|
788
1340
|
return getSmartAccount(config, publicClient, chain);
|
|
789
1341
|
}
|
|
790
|
-
|
|
791
|
-
return withGuardians
|
|
792
|
-
? await getGuardianSmartAccount(config, publicClient, chain, {
|
|
793
|
-
type: 'ecdsa',
|
|
794
|
-
accounts: withGuardians.guardians,
|
|
795
|
-
})
|
|
796
|
-
: null;
|
|
1342
|
+
return null;
|
|
797
1343
|
}
|
|
798
1344
|
function getValidator(config, signers) {
|
|
799
1345
|
if (!signers) {
|
|
@@ -812,11 +1358,11 @@ function getValidator(config, signers) {
|
|
|
812
1358
|
return getWebAuthnValidator(1, withOwner.accounts.map((account) => ({
|
|
813
1359
|
pubKey: account.publicKey,
|
|
814
1360
|
authenticatorId: account.id,
|
|
815
|
-
})));
|
|
1361
|
+
})), withOwner.module);
|
|
816
1362
|
}
|
|
817
1363
|
// Multi-factor
|
|
818
1364
|
if (withOwner.kind === 'multi-factor') {
|
|
819
|
-
return getMultiFactorValidator(1, withOwner.validators);
|
|
1365
|
+
return getMultiFactorValidator(1, withOwner.validators, withOwner.module);
|
|
820
1366
|
}
|
|
821
1367
|
}
|
|
822
1368
|
// Smart sessions
|
|
@@ -824,15 +1370,12 @@ function getValidator(config, signers) {
|
|
|
824
1370
|
if (withSession) {
|
|
825
1371
|
return getSmartSessionValidator(config);
|
|
826
1372
|
}
|
|
827
|
-
// Guardians (social recovery)
|
|
828
|
-
const withGuardians = signers.type === 'guardians' ? signers : null;
|
|
829
|
-
if (withGuardians) {
|
|
830
|
-
return getSocialRecoveryValidator(withGuardians.guardians);
|
|
831
|
-
}
|
|
832
1373
|
// Fallback
|
|
833
1374
|
return undefined;
|
|
834
1375
|
}
|
|
835
1376
|
function parseCalls(calls, chainId) {
|
|
1377
|
+
// Destination calls only run on EVM chains (non-EVM destinations reject
|
|
1378
|
+
// calls upstream), so the resolved `to` is always a viem `Address` here.
|
|
836
1379
|
return calls.map((call) => ({
|
|
837
1380
|
data: call.data ?? '0x',
|
|
838
1381
|
value: call.value ?? 0n,
|
|
@@ -852,6 +1395,8 @@ function createAccountAccessList(sourceChains, sourceAssets) {
|
|
|
852
1395
|
const chainTokenAmounts = {};
|
|
853
1396
|
for (const config of sourceAssets) {
|
|
854
1397
|
const chainId = config.chain.id;
|
|
1398
|
+
// Source assets live on viem `Chain`s, which are always EVM, so the
|
|
1399
|
+
// resolved value is a viem `Address`.
|
|
855
1400
|
const tokenAddress = resolveTokenAddress(config.address, config.chain.id);
|
|
856
1401
|
if (config.amount !== undefined) {
|
|
857
1402
|
if (!chainTokenAmounts[chainId])
|
|
@@ -999,7 +1544,9 @@ function hashErc7739TypedDataForSolady({ domain, types, primaryType, message, ve
|
|
|
999
1544
|
deps.delete(primary);
|
|
1000
1545
|
const sorted = [primary, ...Array.from(deps).sort()];
|
|
1001
1546
|
return sorted
|
|
1002
|
-
.map((t) => `${t}(${allTypes[t]
|
|
1547
|
+
.map((t) => `${t}(${allTypes[t]
|
|
1548
|
+
.map((f) => `${f.type} ${f.name}`)
|
|
1549
|
+
.join(',')})`)
|
|
1003
1550
|
.join('');
|
|
1004
1551
|
}
|
|
1005
1552
|
const contentsType = encodeTypeString(primaryType, types);
|
|
@@ -1051,4 +1598,4 @@ function hashErc7739TypedDataForSolady({ domain, types, primaryType, message, ve
|
|
|
1051
1598
|
// Final hash: keccak256("\x19\x01" || appDomainSep || structHash)
|
|
1052
1599
|
return keccak256(concat(['0x1901', appDomainSeparator, structHash]));
|
|
1053
1600
|
}
|
|
1054
|
-
export { prepareTransaction, getTransactionMessages, signTransaction, signAuthorizations, signAuthorizationsInternal, signMessage, signTypedData, submitTransaction, prepareUserOperation, signUserOperation, submitUserOperation, signIntent, prepareTransactionAsIntent, submitIntentInternal, getValidatorAccount, parseCalls, getTokenRequests, resolveCallInputs, getIntentAccount, getTargetExecutionSignature, hashErc7739TypedDataForSolady, resolveSessionForChain, };
|
|
1601
|
+
export { prepareTransaction, getTransactionMessages, signTransaction, assembleTransaction, signAuthorizations, signAuthorizationsInternal, signMessage, signTypedData, submitTransaction, prepareUserOperation, signUserOperation, submitUserOperation, signIntent, prepareTransactionAsIntent, submitIntentInternal, getValidatorAccount, parseCalls, getTokenRequests, resolveCallInputs, getIntentAccount, getTargetExecutionSignature, hashErc7739TypedDataForSolady, resolveSessionForChain, resolveSignatureMode, };
|