@rhinestone/sdk 0.0.0-dev-20260917215313 → 0.0.0-dev-20260921130703
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/README.md +2 -20
- package/dist/src/actions/runtime.d.ts.map +1 -1
- package/dist/src/api/account.d.ts +62 -91
- package/dist/src/api/account.d.ts.map +1 -1
- package/dist/src/api/account.js +28 -697
- package/dist/src/api/accounts.d.ts +1 -1
- package/dist/src/api/accounts.d.ts.map +1 -1
- package/dist/src/api/accounts.js +3 -121
- package/dist/src/api/compose-types.d.ts +0 -9
- package/dist/src/api/compose-types.d.ts.map +1 -1
- package/dist/src/api/compose.d.ts.map +1 -1
- package/dist/src/api/compose.js +5 -50
- package/dist/src/api/sdk.d.ts +2 -2
- package/dist/src/api/sdk.d.ts.map +1 -1
- package/dist/src/api/sdk.js +1 -1
- package/dist/src/chains/caip2.d.ts.map +1 -1
- package/dist/src/chains/caip2.js +0 -5
- package/dist/src/chains/non-evm.d.ts +12 -132
- package/dist/src/chains/non-evm.d.ts.map +1 -1
- package/dist/src/chains/non-evm.js +1 -30
- package/dist/src/clients/orchestrator/client.js +1 -1
- package/dist/src/clients/orchestrator/errors.d.ts +2 -22
- package/dist/src/clients/orchestrator/errors.d.ts.map +1 -1
- package/dist/src/clients/orchestrator/errors.js +6 -61
- package/dist/src/clients/orchestrator/mappers.d.ts +0 -2
- package/dist/src/clients/orchestrator/mappers.d.ts.map +1 -1
- package/dist/src/clients/orchestrator/mappers.js +2 -87
- package/dist/src/clients/orchestrator/public.d.ts +7 -49
- package/dist/src/clients/orchestrator/public.d.ts.map +1 -1
- package/dist/src/clients/orchestrator/types.d.ts +4 -17
- package/dist/src/clients/orchestrator/types.d.ts.map +1 -1
- package/dist/src/clients/orchestrator/wire.d.ts +19 -1
- package/dist/src/clients/orchestrator/wire.d.ts.map +1 -1
- package/dist/src/config/account.d.ts +11 -185
- package/dist/src/config/account.d.ts.map +1 -1
- package/dist/src/errors/execution.d.ts +1 -11
- package/dist/src/errors/execution.d.ts.map +1 -1
- package/dist/src/errors/execution.js +1 -27
- package/dist/src/errors/index.d.ts +3 -4
- package/dist/src/errors/index.d.ts.map +1 -1
- package/dist/src/errors/index.js +4 -7
- package/dist/src/hypercore/market.d.ts +1 -1
- package/dist/src/hypercore/market.js +1 -1
- package/dist/src/index.d.ts +7 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/transactions/intents/normalize.d.ts.map +1 -1
- package/dist/src/transactions/intents/normalize.js +2 -9
- package/dist/src/transactions/intents/prepare.d.ts.map +1 -1
- package/dist/src/transactions/intents/prepare.js +1 -10
- package/dist/src/transactions/intents/types.d.ts +3 -61
- package/dist/src/transactions/intents/types.d.ts.map +1 -1
- package/dist/src/utils/index.d.ts +9 -9
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +6 -6
- package/package.json +1 -5
- package/dist/src/accounts/solana/address.d.ts +0 -28
- package/dist/src/accounts/solana/address.d.ts.map +0 -1
- package/dist/src/accounts/solana/address.js +0 -105
- package/dist/src/errors/capability.d.ts +0 -23
- package/dist/src/errors/capability.d.ts.map +0 -1
- package/dist/src/errors/capability.js +0 -41
- package/dist/src/transactions/intents/solana-instructions.d.ts +0 -14
- package/dist/src/transactions/intents/solana-instructions.d.ts.map +0 -1
- package/dist/src/transactions/intents/solana-instructions.js +0 -114
- package/dist/src/transactions/intents/solana.d.ts +0 -88
- package/dist/src/transactions/intents/solana.d.ts.map +0 -1
- package/dist/src/transactions/intents/solana.js +0 -302
package/dist/src/api/account.js
CHANGED
|
@@ -1,45 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { asSwigNamespace, locateSwig } from '../accounts/solana/address.js';
|
|
3
|
-
import { formatCaip2, parseCaip2, toEvmChainReference } from '../chains/caip2.js';
|
|
1
|
+
import { parseCaip2, toEvmChainReference } from '../chains/caip2.js';
|
|
4
2
|
import { getChainById, getChainReference } from '../chains/catalog.js';
|
|
5
|
-
import { solanaAddress, } from '../chains/non-evm.js';
|
|
6
3
|
import { normalizeTokenAddress, validateTokenAddresses } from '../chains/tokens.js';
|
|
7
4
|
import { createStaticAccountRuntime } from '../config/account-runtime.js';
|
|
8
5
|
import { materializeAccountInvocationContext, resolveAccountConfig, } from '../config/resolve.js';
|
|
9
|
-
import {
|
|
10
|
-
import { IndependentSigningNotSupportedError, InvalidSolanaTransactionArtifactError, MismatchedOwnerSignaturesError, QuoteNotInPreparedTransactionError, } from '../errors/execution.js';
|
|
6
|
+
import { IndependentSigningNotSupportedError, MismatchedOwnerSignaturesError, QuoteNotInPreparedTransactionError, } from '../errors/execution.js';
|
|
11
7
|
import { resolveHyperCoreAction } from '../hypercore/resolve.js';
|
|
12
8
|
import { ecdsaSignerId, webauthnSignerId, } from '../modules/validators/signer-id.js';
|
|
13
9
|
import { projectIntentAccount, projectIntentRecipient, } from '../transactions/intents/account.js';
|
|
14
10
|
import { projectCompatibleIntentInput, projectCompatibleQuote, } from '../transactions/intents/compatibility.js';
|
|
15
11
|
import { normalizeIntentQuote } from '../transactions/intents/normalize.js';
|
|
16
|
-
import { solanaChainId, } from '../transactions/intents/solana.js';
|
|
17
|
-
import { normalizeSolanaAddressLookupTables, normalizeSolanaInstructions, } from '../transactions/intents/solana-instructions.js';
|
|
18
12
|
import { adaptSignerSelection, adaptUserOperationSignerSelection, } from './signer-selection.js';
|
|
19
|
-
function cloneArtifactValue(value) {
|
|
20
|
-
if (Array.isArray(value)) {
|
|
21
|
-
return value.map(cloneArtifactValue);
|
|
22
|
-
}
|
|
23
|
-
if (value && typeof value === 'object') {
|
|
24
|
-
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
25
|
-
key,
|
|
26
|
-
cloneArtifactValue(item),
|
|
27
|
-
]));
|
|
28
|
-
}
|
|
29
|
-
return value;
|
|
30
|
-
}
|
|
31
13
|
function toPublicQuote(quote) {
|
|
32
14
|
const compatible = projectCompatibleQuote(quote);
|
|
33
|
-
return
|
|
15
|
+
return {
|
|
34
16
|
intentId: compatible.intentId,
|
|
35
17
|
expiresAt: compatible.expiresAt,
|
|
36
18
|
estimatedFillTime: compatible.estimatedFillTime,
|
|
37
19
|
settlementLayer: compatible.settlementLayer,
|
|
38
20
|
signData: {
|
|
39
21
|
origin: [...compatible.signData.origin],
|
|
40
|
-
|
|
41
|
-
? { destination: compatible.signData.destination }
|
|
42
|
-
: {}),
|
|
22
|
+
destination: compatible.signData.destination,
|
|
43
23
|
...(compatible.signData.targetExecution
|
|
44
24
|
? { targetExecution: compatible.signData.targetExecution }
|
|
45
25
|
: {}),
|
|
@@ -49,7 +29,7 @@ function toPublicQuote(quote) {
|
|
|
49
29
|
? { tokenRequirements: compatible.tokenRequirements }
|
|
50
30
|
: {}),
|
|
51
31
|
...(compatible.bridgeFill ? { bridgeFill: compatible.bridgeFill } : {}),
|
|
52
|
-
}
|
|
32
|
+
};
|
|
53
33
|
}
|
|
54
34
|
function toPreparedTransactionData(prepared, transaction, cache) {
|
|
55
35
|
const data = {
|
|
@@ -64,51 +44,6 @@ function toPreparedTransactionData(prepared, transaction, cache) {
|
|
|
64
44
|
cache.set(data, prepared);
|
|
65
45
|
return data;
|
|
66
46
|
}
|
|
67
|
-
function solanaMetadata(input) {
|
|
68
|
-
const binding = {
|
|
69
|
-
namespace: input.namespace,
|
|
70
|
-
endpoint: input.endpoint,
|
|
71
|
-
chain: solanaChainId(input.chain),
|
|
72
|
-
caip2: input.chain.caip2,
|
|
73
|
-
accountAddress: input.accountAddress,
|
|
74
|
-
accountType: input.accountType,
|
|
75
|
-
authority: input.authority,
|
|
76
|
-
swigAddress: input.swigAddress,
|
|
77
|
-
walletAddress: input.walletAddress,
|
|
78
|
-
};
|
|
79
|
-
const action = input.action;
|
|
80
|
-
if (action.kind === 'instructions') {
|
|
81
|
-
return { kind: 'solana-instructions', ...binding };
|
|
82
|
-
}
|
|
83
|
-
const delivery = action.delivery;
|
|
84
|
-
return delivery.kind === 'cross-chain'
|
|
85
|
-
? {
|
|
86
|
-
kind: 'solana-cross-chain',
|
|
87
|
-
...binding,
|
|
88
|
-
mint: action.mint,
|
|
89
|
-
destinationChain: delivery.chainId,
|
|
90
|
-
destinationToken: delivery.token,
|
|
91
|
-
recipient: delivery.recipient,
|
|
92
|
-
}
|
|
93
|
-
: {
|
|
94
|
-
kind: 'solana',
|
|
95
|
-
...binding,
|
|
96
|
-
mint: action.mint,
|
|
97
|
-
recipient: delivery.recipient,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
function toPreparedSolanaTransactionData(prepared, transaction) {
|
|
101
|
-
return {
|
|
102
|
-
quotes: {
|
|
103
|
-
traceId: prepared.traceId,
|
|
104
|
-
best: toPublicQuote(prepared.quote),
|
|
105
|
-
all: prepared.quotes.map(toPublicQuote),
|
|
106
|
-
},
|
|
107
|
-
execution: solanaMetadata(prepared.input),
|
|
108
|
-
intentInput: projectCompatibleIntentInput(prepared.request),
|
|
109
|
-
transaction,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
47
|
function selectedPublicQuote(prepared, intentId) {
|
|
113
48
|
if (!intentId)
|
|
114
49
|
return prepared.quotes.best;
|
|
@@ -141,12 +76,8 @@ function toSignedTransactionData(prepared, signed, cache) {
|
|
|
141
76
|
...prepared,
|
|
142
77
|
quote: toPublicQuote(signed.prepared.quote),
|
|
143
78
|
originSignatures: signed.originSignatures,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
: {}),
|
|
147
|
-
...(signed.targetSignature
|
|
148
|
-
? { targetExecutionSignature: signed.targetSignature }
|
|
149
|
-
: {}),
|
|
79
|
+
destinationSignature: signed.destinationSignature,
|
|
80
|
+
targetExecutionSignature: signed.targetSignature,
|
|
150
81
|
};
|
|
151
82
|
cache.set(data, signed);
|
|
152
83
|
return data;
|
|
@@ -156,139 +87,21 @@ function referenceChain() {
|
|
|
156
87
|
// needs some EVM chain reference. With no bundled chain set, use mainnet.
|
|
157
88
|
return toEvmChainReference(1);
|
|
158
89
|
}
|
|
159
|
-
export function createAccountFacade(compatibilityConfig,
|
|
90
|
+
export function createAccountFacade(compatibilityConfig, composition) {
|
|
160
91
|
// Intent identity caches are facade-scoped. Values crossing account/SDK
|
|
161
92
|
// instances are reconstructed and validated by the receiving account.
|
|
162
93
|
const preparedIntents = new WeakMap();
|
|
163
94
|
const signedIntents = new WeakMap();
|
|
164
95
|
const context = (method) => materializeAccountInvocationContext(composition.config, compatibilityConfig, method);
|
|
165
96
|
const workflowsFor = (ctx) => composition.createAccount(ctx).workflows;
|
|
166
|
-
const hasManagedSolana = Boolean(publicConfig.solana && 'owner' in publicConfig.solana);
|
|
167
|
-
const capturedSolanaContext = hasManagedSolana
|
|
168
|
-
? context('get-address')
|
|
169
|
-
: undefined;
|
|
170
|
-
const capturedSolanaIdentity = capturedSolanaContext
|
|
171
|
-
? workflowsFor(capturedSolanaContext).getAddress(capturedSolanaContext, referenceChain())
|
|
172
|
-
: undefined;
|
|
173
|
-
const capturedSolanaEndpoint = capturedSolanaContext?.sdk.orchestratorUrl;
|
|
174
|
-
const capturedSolanaAccountType = capturedSolanaContext
|
|
175
|
-
? capturedSolanaContext.account.account.kind === 'eoa'
|
|
176
|
-
? 'EOA'
|
|
177
|
-
: capturedSolanaContext.account.account.kind === 'hca'
|
|
178
|
-
? 'GENERIC'
|
|
179
|
-
: 'ERC7579'
|
|
180
|
-
: undefined;
|
|
181
|
-
const solanaOwner = () => {
|
|
182
|
-
const branch = publicConfig.solana;
|
|
183
|
-
if (!branch || !('owner' in branch) || !branch.owner) {
|
|
184
|
-
throw new UnsupportedAccountCapabilityError('A managed Solana source is not configured on this account.', { vm: 'solana' });
|
|
185
|
-
}
|
|
186
|
-
return branch.owner.account;
|
|
187
|
-
};
|
|
188
|
-
const solanaTransfer = (ctx, transaction) => {
|
|
189
|
-
assertSupportedTransaction(transaction, publicConfig);
|
|
190
|
-
if (ctx.sdk.environment !== 'development' ||
|
|
191
|
-
ctx.sdk.orchestratorUrl !== capturedSolanaEndpoint) {
|
|
192
|
-
throw new ManagedSolanaAccountNotSupportedError('Managed Solana execution remains bound to the development environment and endpoint captured when the account was created.');
|
|
193
|
-
}
|
|
194
|
-
if (!isSolanaOrigin(transaction)) {
|
|
195
|
-
throw new InvalidSolanaTransactionArtifactError('the transaction is not a Solana-origin transfer');
|
|
196
|
-
}
|
|
197
|
-
const accountAddress = capturedSolanaIdentity;
|
|
198
|
-
if (!accountAddress || !capturedSolanaAccountType) {
|
|
199
|
-
throw new UnsupportedAccountCapabilityError('A managed Solana source is not configured on this account.', { vm: 'solana' });
|
|
200
|
-
}
|
|
201
|
-
const location = locateSwig(asSwigNamespace('dev-v1'), accountAddress);
|
|
202
|
-
const common = {
|
|
203
|
-
accountAddress,
|
|
204
|
-
accountType: capturedSolanaAccountType,
|
|
205
|
-
authority: solanaOwner().address,
|
|
206
|
-
walletAddress: location.wallet,
|
|
207
|
-
swigAddress: location.swig,
|
|
208
|
-
namespace: 'dev-v1',
|
|
209
|
-
endpoint: ctx.sdk.orchestratorUrl,
|
|
210
|
-
...(transaction.appFees ? { appFees: transaction.appFees } : {}),
|
|
211
|
-
...(transaction.protocolFees
|
|
212
|
-
? { protocolFees: transaction.protocolFees }
|
|
213
|
-
: {}),
|
|
214
|
-
};
|
|
215
|
-
if (isCrossChainSolanaOrigin(transaction)) {
|
|
216
|
-
return {
|
|
217
|
-
...common,
|
|
218
|
-
chain: transaction.sourceChains[0],
|
|
219
|
-
action: {
|
|
220
|
-
kind: 'transfer',
|
|
221
|
-
mint: transaction.sourceTokens[0].address,
|
|
222
|
-
...(transaction.tokenRequests[0].amount === undefined
|
|
223
|
-
? {}
|
|
224
|
-
: { amount: transaction.tokenRequests[0].amount }),
|
|
225
|
-
delivery: {
|
|
226
|
-
kind: 'cross-chain',
|
|
227
|
-
chainId: transaction.targetChain.id,
|
|
228
|
-
token: transaction.tokenRequests[0].address,
|
|
229
|
-
// Resolved here rather than in `normalizeTransaction` so the
|
|
230
|
-
// prepare and reconstruct paths agree on the same recipient.
|
|
231
|
-
recipient: transaction.recipient ?? accountAddress,
|
|
232
|
-
},
|
|
233
|
-
},
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
if (isSolanaInstructionExecution(transaction)) {
|
|
237
|
-
return {
|
|
238
|
-
...common,
|
|
239
|
-
chain: transaction.chain,
|
|
240
|
-
action: {
|
|
241
|
-
kind: 'instructions',
|
|
242
|
-
instructions: normalizeSolanaInstructions(transaction.instructions),
|
|
243
|
-
...(transaction.addressLookupTables
|
|
244
|
-
? { addressLookupTables: transaction.addressLookupTables }
|
|
245
|
-
: {}),
|
|
246
|
-
},
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
return {
|
|
250
|
-
...common,
|
|
251
|
-
chain: transaction.chain,
|
|
252
|
-
action: {
|
|
253
|
-
kind: 'transfer',
|
|
254
|
-
mint: transaction.tokenRequests[0].address,
|
|
255
|
-
...(transaction.tokenRequests[0].amount === undefined
|
|
256
|
-
? {}
|
|
257
|
-
: { amount: transaction.tokenRequests[0].amount }),
|
|
258
|
-
delivery: { kind: 'same-chain', recipient: transaction.recipient },
|
|
259
|
-
},
|
|
260
|
-
};
|
|
261
|
-
};
|
|
262
|
-
const assertSolanaMetadata = (actual, expected) => {
|
|
263
|
-
const expectedEntries = Object.entries(solanaMetadata(expected));
|
|
264
|
-
if (!actual ||
|
|
265
|
-
Object.keys(actual).length !== expectedEntries.length ||
|
|
266
|
-
expectedEntries.some(([key, value]) => actual[key] !== value)) {
|
|
267
|
-
throw new InvalidSolanaTransactionArtifactError('the execution binding does not match this account, environment, chain, recipient, or mint');
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
const resolveSolanaPrepared = (ctx, prepared, intentId, explicitQuote) => {
|
|
271
|
-
const transfer = solanaTransfer(ctx, prepared.transaction);
|
|
272
|
-
assertSolanaMetadata(prepared.execution, transfer);
|
|
273
|
-
const quote = explicitQuote ?? selectedPublicQuote(prepared, intentId);
|
|
274
|
-
return workflowsFor(ctx).reconstructSolanaIntent({
|
|
275
|
-
traceId: prepared.quotes.traceId,
|
|
276
|
-
transfer,
|
|
277
|
-
intentInput: prepared.intentInput,
|
|
278
|
-
quote: normalizeIntentQuote(quote),
|
|
279
|
-
quotes: prepared.quotes.all.map((candidate) => normalizeIntentQuote(candidate)),
|
|
280
|
-
});
|
|
281
|
-
};
|
|
282
97
|
const resolvePrepared = (ctx, prepared, intentId) => {
|
|
283
|
-
assertSupportedTransaction(prepared.transaction, publicConfig);
|
|
284
|
-
assertSupportedSignData(selectedPublicQuote(prepared, intentId).signData);
|
|
285
98
|
const cached = preparedIntents.get(prepared);
|
|
286
99
|
if (cached && !intentId)
|
|
287
100
|
return Promise.resolve(cached);
|
|
288
101
|
return workflowsFor(ctx).reconstructPreparedIntent(ctx, reconstructInput(ctx, prepared, intentId));
|
|
289
102
|
};
|
|
290
103
|
const account = {
|
|
291
|
-
config:
|
|
104
|
+
config: compatibilityConfig,
|
|
292
105
|
deploy(chain, params) {
|
|
293
106
|
const ctx = context('deploy');
|
|
294
107
|
return workflowsFor(ctx).deploy(ctx, toEvmChainReference(chain.id), {
|
|
@@ -316,106 +129,49 @@ export function createAccountFacade(compatibilityConfig, publicConfig, compositi
|
|
|
316
129
|
},
|
|
317
130
|
async prepareTransaction(transaction) {
|
|
318
131
|
const ctx = context('prepare-intent');
|
|
319
|
-
const initiallyNormalized = normalizeTransaction(transaction, publicConfig);
|
|
320
|
-
const normalized = 'targetChain' in initiallyNormalized &&
|
|
321
|
-
initiallyNormalized.targetChain !== undefined &&
|
|
322
|
-
'kind' in initiallyNormalized.targetChain &&
|
|
323
|
-
initiallyNormalized.targetChain.kind === 'svm' &&
|
|
324
|
-
initiallyNormalized.recipient === undefined &&
|
|
325
|
-
capturedSolanaIdentity
|
|
326
|
-
? Object.freeze({
|
|
327
|
-
...initiallyNormalized,
|
|
328
|
-
recipient: locateSwig(asSwigNamespace('dev-v1'), capturedSolanaIdentity).wallet,
|
|
329
|
-
})
|
|
330
|
-
: initiallyNormalized;
|
|
331
|
-
if (isSolanaOrigin(normalized)) {
|
|
332
|
-
const prepared = await workflowsFor(ctx).prepareSolanaIntent(solanaTransfer(ctx, normalized));
|
|
333
|
-
return toPreparedSolanaTransactionData(prepared, normalized);
|
|
334
|
-
}
|
|
335
132
|
// Before the quote, not after: the quote's `signData` registers an agent
|
|
336
133
|
// derived from the action's bytes, so the action has to be concrete here.
|
|
337
134
|
const hyperCoreAction = await resolveHyperCoreAction({
|
|
338
|
-
options:
|
|
339
|
-
account:
|
|
135
|
+
options: transaction.hyperCore,
|
|
136
|
+
account: account.getAddress(),
|
|
340
137
|
...(ctx.sdk.hyperliquid ? { hyperliquid: ctx.sdk.hyperliquid } : {}),
|
|
341
138
|
});
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
normalized.sourceChains === undefined
|
|
345
|
-
? await workflowsFor(ctx).getEligibleEvmSourceChains(destinationChainReference(normalized.targetChain))
|
|
346
|
-
: undefined;
|
|
347
|
-
if (automaticSources && automaticSources.length === 0) {
|
|
348
|
-
throw new UnsupportedAccountCapabilityError('No managed EVM source chains are eligible for this destination.');
|
|
349
|
-
}
|
|
350
|
-
const prepared = await workflowsFor(ctx).prepareIntent(ctx, adaptTransaction(ctx, normalized, hyperCoreAction, automaticSources?.map(({ id }) => id)));
|
|
351
|
-
return toPreparedTransactionData(prepared, normalized, preparedIntents);
|
|
139
|
+
const prepared = await workflowsFor(ctx).prepareIntent(ctx, adaptTransaction(ctx, transaction, hyperCoreAction));
|
|
140
|
+
return toPreparedTransactionData(prepared, transaction, preparedIntents);
|
|
352
141
|
},
|
|
353
142
|
getTransactionMessages(preparedTransaction, options) {
|
|
354
|
-
assertSupportedTransaction(preparedTransaction.transaction, publicConfig);
|
|
355
143
|
const quote = selectedPublicQuote(preparedTransaction, options?.intentId);
|
|
356
|
-
if (!isSolanaOrigin(preparedTransaction.transaction)) {
|
|
357
|
-
assertSupportedSignData(quote.signData);
|
|
358
|
-
}
|
|
359
|
-
else {
|
|
360
|
-
const ctx = context('get-intent-messages');
|
|
361
|
-
resolveSolanaPrepared(ctx, preparedTransaction, options?.intentId);
|
|
362
|
-
}
|
|
363
144
|
return {
|
|
364
145
|
origin: [...quote.signData.origin],
|
|
365
|
-
|
|
366
|
-
? { destination: quote.signData.destination }
|
|
367
|
-
: {}),
|
|
146
|
+
destination: quote.signData.destination,
|
|
368
147
|
...(quote.signData.targetExecution
|
|
369
148
|
? { targetExecution: quote.signData.targetExecution }
|
|
370
149
|
: {}),
|
|
371
150
|
};
|
|
372
151
|
},
|
|
373
|
-
signTransaction: (
|
|
152
|
+
signTransaction: ((preparedTransaction, options) => {
|
|
374
153
|
const ctx = context('sign-intent');
|
|
375
154
|
const workflows = workflowsFor(ctx);
|
|
376
|
-
if (isSolanaOrigin(preparedTransaction.transaction)) {
|
|
377
|
-
if (options && 'owner' in options) {
|
|
378
|
-
throw new IndependentSigningNotSupportedError({
|
|
379
|
-
context: { vm: 'solana' },
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
const prepared = resolveSolanaPrepared(ctx, preparedTransaction, options?.intentId);
|
|
383
|
-
const signed = await workflows.signSolanaIntent({
|
|
384
|
-
prepared,
|
|
385
|
-
owner: solanaOwner(),
|
|
386
|
-
});
|
|
387
|
-
return {
|
|
388
|
-
...preparedTransaction,
|
|
389
|
-
quote: toPublicQuote(signed.prepared.quote),
|
|
390
|
-
originSignatures: [signed.signature],
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
155
|
if (options && 'owner' in options) {
|
|
394
156
|
// Independent owner signing is unsupported for smart-session intents;
|
|
395
157
|
// reject before resolving sessions (which would issue an RPC read),
|
|
396
158
|
// matching the legacy fast-fail.
|
|
397
159
|
if (preparedTransaction.transaction.signers?.type === 'session') {
|
|
398
|
-
|
|
160
|
+
return Promise.reject(new IndependentSigningNotSupportedError());
|
|
399
161
|
}
|
|
400
162
|
const signerId = signerIdForOwner(options.owner);
|
|
401
|
-
|
|
402
|
-
return workflows.signIntentAsOwner(ctx, internal, {
|
|
163
|
+
return resolvePrepared(ctx, preparedTransaction, options.intentId).then((internal) => workflows.signIntentAsOwner(ctx, internal, {
|
|
403
164
|
signerId,
|
|
404
165
|
...(options.validatorId === undefined
|
|
405
166
|
? {}
|
|
406
167
|
: { validatorId: options.validatorId }),
|
|
407
|
-
});
|
|
168
|
+
}));
|
|
408
169
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
170
|
+
return resolvePrepared(ctx, preparedTransaction, options?.intentId)
|
|
171
|
+
.then((internal) => workflows.signIntent(ctx, internal))
|
|
172
|
+
.then(({ intent }) => toSignedTransactionData(preparedTransaction, intent, signedIntents));
|
|
412
173
|
}),
|
|
413
174
|
async assembleTransaction(preparedTransaction, signatures) {
|
|
414
|
-
if (isSolanaOrigin(preparedTransaction.transaction)) {
|
|
415
|
-
throw new IndependentSigningNotSupportedError({
|
|
416
|
-
context: { vm: 'solana' },
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
175
|
const ctx = context('assemble-intent');
|
|
420
176
|
const workflows = workflowsFor(ctx);
|
|
421
177
|
const intentIds = [...new Set(signatures.map(({ intentId }) => intentId))];
|
|
@@ -427,10 +183,6 @@ export function createAccountFacade(compatibilityConfig, publicConfig, compositi
|
|
|
427
183
|
return toSignedTransactionData(preparedTransaction, signed, signedIntents);
|
|
428
184
|
},
|
|
429
185
|
async signAuthorizations(preparedTransaction) {
|
|
430
|
-
assertSupportedTransaction(preparedTransaction.transaction, publicConfig);
|
|
431
|
-
if (isSolanaOrigin(preparedTransaction.transaction)) {
|
|
432
|
-
throw new UnsupportedAccountCapabilityError('EIP-7702 authorizations are unavailable for Solana-origin transactions.', { vm: 'solana' });
|
|
433
|
-
}
|
|
434
186
|
const ctx = context('sign-authorizations');
|
|
435
187
|
const intentInput = adaptTransaction(ctx, preparedTransaction.transaction);
|
|
436
188
|
const chains = authorizationChains(intentInput);
|
|
@@ -465,7 +217,6 @@ export function createAccountFacade(compatibilityConfig, publicConfig, compositi
|
|
|
465
217
|
return result.signature;
|
|
466
218
|
},
|
|
467
219
|
async signIntent(signData, targetChain, signers) {
|
|
468
|
-
assertSupportedSignData(signData);
|
|
469
220
|
const ctx = context('sign-intent');
|
|
470
221
|
const result = await workflowsFor(ctx).signIntentFromSignData(ctx, {
|
|
471
222
|
signData: {
|
|
@@ -487,35 +238,8 @@ export function createAccountFacade(compatibilityConfig, publicConfig, compositi
|
|
|
487
238
|
};
|
|
488
239
|
},
|
|
489
240
|
async submitTransaction(signedTransaction, options) {
|
|
490
|
-
assertSupportedTransaction(signedTransaction.transaction, publicConfig);
|
|
491
241
|
const ctx = context('submit-intent');
|
|
492
242
|
const workflows = workflowsFor(ctx);
|
|
493
|
-
if (isSolanaOrigin(signedTransaction.transaction)) {
|
|
494
|
-
if (options && Object.keys(options).length > 0) {
|
|
495
|
-
throw new UnsupportedAccountCapabilityError('Solana submission does not accept EVM authorizations or dry-run options.', { vm: 'solana' });
|
|
496
|
-
}
|
|
497
|
-
if (signedTransaction.originSignatures.length !== 1 ||
|
|
498
|
-
typeof signedTransaction.originSignatures[0] !== 'string' ||
|
|
499
|
-
signedTransaction.destinationSignature !== undefined ||
|
|
500
|
-
signedTransaction.targetExecutionSignature !== undefined) {
|
|
501
|
-
throw new InvalidSolanaTransactionArtifactError('submission requires exactly one raw origin signature and no destination or target signature', { intentId: signedTransaction.quote.intentId });
|
|
502
|
-
}
|
|
503
|
-
const prepared = resolveSolanaPrepared(ctx, signedTransaction, signedTransaction.quote.intentId, signedTransaction.quote);
|
|
504
|
-
const submitted = await workflows.submitSolanaIntent({
|
|
505
|
-
prepared,
|
|
506
|
-
signature: signedTransaction.originSignatures[0],
|
|
507
|
-
});
|
|
508
|
-
return {
|
|
509
|
-
type: 'intent',
|
|
510
|
-
id: submitted.intentId,
|
|
511
|
-
traceId: submitted.traceId,
|
|
512
|
-
...(submitted.sourceChains
|
|
513
|
-
? { sourceChains: [...submitted.sourceChains] }
|
|
514
|
-
: {}),
|
|
515
|
-
targetChain: submitted.targetChain,
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
assertSupportedSignData(signedTransaction.quote.signData);
|
|
519
243
|
// Fast path for the same-instance signed object; otherwise (cross-instance
|
|
520
244
|
// replay or caller-tampered signatures) rebuild from the public shape.
|
|
521
245
|
const cached = signedIntents.get(signedTransaction);
|
|
@@ -637,25 +361,10 @@ export function createAccountFacade(compatibilityConfig, publicConfig, compositi
|
|
|
637
361
|
})
|
|
638
362
|
.then((status) => status.receipt);
|
|
639
363
|
}),
|
|
640
|
-
getAddress
|
|
641
|
-
if (vm === 'solana') {
|
|
642
|
-
if (publicConfig.solana &&
|
|
643
|
-
'address' in publicConfig.solana &&
|
|
644
|
-
publicConfig.solana.address) {
|
|
645
|
-
return publicConfig.solana.address;
|
|
646
|
-
}
|
|
647
|
-
if (capturedSolanaIdentity) {
|
|
648
|
-
return locateSwig(asSwigNamespace('dev-v1'), capturedSolanaIdentity)
|
|
649
|
-
.wallet;
|
|
650
|
-
}
|
|
651
|
-
throw new AccountVmNotConfiguredError('solana');
|
|
652
|
-
}
|
|
653
|
-
if (vm !== 'evm' || !publicConfig.evm) {
|
|
654
|
-
throw new AccountVmNotConfiguredError(String(vm));
|
|
655
|
-
}
|
|
364
|
+
getAddress() {
|
|
656
365
|
const ctx = context('get-address');
|
|
657
366
|
return workflowsFor(ctx).getAddress(ctx, referenceChain());
|
|
658
|
-
}
|
|
367
|
+
},
|
|
659
368
|
getPortfolio(onTestnets = false) {
|
|
660
369
|
const ctx = context('get-portfolio');
|
|
661
370
|
return workflowsFor(ctx)
|
|
@@ -824,367 +533,6 @@ chainIds) {
|
|
|
824
533
|
* authorise. With no session scope there is nothing to narrow against and the
|
|
825
534
|
* explicit filter stands on its own.
|
|
826
535
|
*/
|
|
827
|
-
function assertSupportedSignData(signData) {
|
|
828
|
-
const payloads = [
|
|
829
|
-
...signData.origin,
|
|
830
|
-
signData.destination,
|
|
831
|
-
...(signData.targetExecution ? [signData.targetExecution] : []),
|
|
832
|
-
];
|
|
833
|
-
for (const payload of payloads) {
|
|
834
|
-
if (!payload ||
|
|
835
|
-
typeof payload !== 'object' ||
|
|
836
|
-
(payload.kind !== undefined &&
|
|
837
|
-
payload.kind !== 'eip712') ||
|
|
838
|
-
typeof payload.primaryType !== 'string' ||
|
|
839
|
-
!payload.domain ||
|
|
840
|
-
typeof payload.types !== 'object' ||
|
|
841
|
-
typeof payload.message !== 'object') {
|
|
842
|
-
throw new UnsupportedAccountCapabilityError('Only EIP-712 intent signing data is supported by this account.', { capability: 'intent-signing' });
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
function isSameChainSolanaOrigin(transaction) {
|
|
847
|
-
return ('chain' in transaction &&
|
|
848
|
-
typeof transaction.chain === 'object' &&
|
|
849
|
-
transaction.chain !== null &&
|
|
850
|
-
'kind' in transaction.chain &&
|
|
851
|
-
transaction.chain.kind === 'svm');
|
|
852
|
-
}
|
|
853
|
-
function isCrossChainSolanaOrigin(transaction) {
|
|
854
|
-
const source = transaction
|
|
855
|
-
.sourceChains?.[0];
|
|
856
|
-
return (typeof source === 'object' &&
|
|
857
|
-
source !== null &&
|
|
858
|
-
'kind' in source &&
|
|
859
|
-
source.kind === 'svm');
|
|
860
|
-
}
|
|
861
|
-
function isSolanaInstructionExecution(transaction) {
|
|
862
|
-
return (isSameChainSolanaOrigin(transaction) &&
|
|
863
|
-
transaction.instructions !== undefined);
|
|
864
|
-
}
|
|
865
|
-
function isSolanaOrigin(transaction) {
|
|
866
|
-
return (isSameChainSolanaOrigin(transaction) ||
|
|
867
|
-
isCrossChainSolanaOrigin(transaction));
|
|
868
|
-
}
|
|
869
|
-
function assertSolanaObjectKeys(value, allowed, label) {
|
|
870
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
871
|
-
throw new UnsupportedAccountCapabilityError(`Managed Solana ${label} must be an object.`, { vm: 'solana', field: label });
|
|
872
|
-
}
|
|
873
|
-
const unsupported = Object.keys(value).find((key) => !allowed.includes(key));
|
|
874
|
-
if (unsupported) {
|
|
875
|
-
throw new UnsupportedAccountCapabilityError(`Solana-origin transfers do not support \`${label}.${unsupported}\`.`, { vm: 'solana', field: `${label}.${unsupported}` });
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
function assertSupportedSolanaTransaction(input, config) {
|
|
879
|
-
const runsInstructions = input.instructions !== undefined;
|
|
880
|
-
const allowed = new Set(runsInstructions
|
|
881
|
-
? ['chain', 'instructions', 'addressLookupTables', 'sponsored']
|
|
882
|
-
: [
|
|
883
|
-
'chain',
|
|
884
|
-
'tokenRequests',
|
|
885
|
-
'recipient',
|
|
886
|
-
'sponsored',
|
|
887
|
-
'appFees',
|
|
888
|
-
'protocolFees',
|
|
889
|
-
]);
|
|
890
|
-
const unsupported = Object.keys(input).find((key) => !allowed.has(key));
|
|
891
|
-
if (unsupported) {
|
|
892
|
-
throw new UnsupportedAccountCapabilityError(`Solana-origin transfers do not support \`${unsupported}\`.`, { vm: 'solana', field: unsupported });
|
|
893
|
-
}
|
|
894
|
-
if (!config.solana || !('owner' in config.solana)) {
|
|
895
|
-
throw new UnsupportedAccountCapabilityError('A managed Solana source is required for Solana-origin transfers.', { vm: 'solana' });
|
|
896
|
-
}
|
|
897
|
-
solanaChainId(input.chain);
|
|
898
|
-
if (input.sponsored !== undefined && input.sponsored !== false) {
|
|
899
|
-
throw new UnsupportedAccountCapabilityError('Managed Solana transfers are not sponsorable; omit `sponsored` or set it to false.', { vm: 'solana' });
|
|
900
|
-
}
|
|
901
|
-
if (runsInstructions) {
|
|
902
|
-
// Shape and limits are enforced by the normalizer, which also produces the
|
|
903
|
-
// canonical form `normalizeTransaction` stores.
|
|
904
|
-
normalizeSolanaInstructions(input.instructions);
|
|
905
|
-
normalizeSolanaAddressLookupTables(input.addressLookupTables);
|
|
906
|
-
return;
|
|
907
|
-
}
|
|
908
|
-
if (!Array.isArray(input.tokenRequests) || input.tokenRequests.length !== 1) {
|
|
909
|
-
throw new UnsupportedAccountCapabilityError('Managed Solana transfers require exactly one SPL token request.', { vm: 'solana' });
|
|
910
|
-
}
|
|
911
|
-
const request = input.tokenRequests[0];
|
|
912
|
-
assertSolanaObjectKeys(request, ['address', 'amount'], 'tokenRequests[0]');
|
|
913
|
-
if (typeof request.address !== 'string') {
|
|
914
|
-
throw new UnsupportedAccountCapabilityError('Managed Solana transfers require one valid SPL mint address.', { vm: 'solana' });
|
|
915
|
-
}
|
|
916
|
-
try {
|
|
917
|
-
solanaAddress(request.address);
|
|
918
|
-
}
|
|
919
|
-
catch {
|
|
920
|
-
throw new UnsupportedAccountCapabilityError('Managed Solana transfers require one valid SPL mint address.', { vm: 'solana' });
|
|
921
|
-
}
|
|
922
|
-
if (request.amount !== undefined &&
|
|
923
|
-
(typeof request.amount !== 'bigint' || request.amount <= 0n)) {
|
|
924
|
-
throw new UnsupportedAccountCapabilityError('A Solana token amount must be a positive bigint when provided.', { vm: 'solana' });
|
|
925
|
-
}
|
|
926
|
-
if (input.appFees !== undefined) {
|
|
927
|
-
assertSolanaObjectKeys(input.appFees, ['feeBps'], 'appFees');
|
|
928
|
-
}
|
|
929
|
-
if (input.protocolFees !== undefined) {
|
|
930
|
-
assertSolanaObjectKeys(input.protocolFees, ['feeBps'], 'protocolFees');
|
|
931
|
-
}
|
|
932
|
-
if (typeof input.recipient !== 'string') {
|
|
933
|
-
throw new UnsupportedAccountCapabilityError('Managed Solana transfers require an explicit recipient wallet.', { vm: 'solana' });
|
|
934
|
-
}
|
|
935
|
-
try {
|
|
936
|
-
solanaAddress(input.recipient);
|
|
937
|
-
}
|
|
938
|
-
catch {
|
|
939
|
-
throw new UnsupportedAccountCapabilityError('Managed Solana transfers require a valid recipient wallet.', { vm: 'solana' });
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
function assertSupportedSolanaOriginDelivery(input, config) {
|
|
943
|
-
const allowed = new Set([
|
|
944
|
-
'sourceChains',
|
|
945
|
-
'sourceTokens',
|
|
946
|
-
'targetChain',
|
|
947
|
-
'tokenRequests',
|
|
948
|
-
'recipient',
|
|
949
|
-
'sponsored',
|
|
950
|
-
'appFees',
|
|
951
|
-
'protocolFees',
|
|
952
|
-
]);
|
|
953
|
-
const unsupported = Object.keys(input).find((key) => !allowed.has(key));
|
|
954
|
-
if (unsupported) {
|
|
955
|
-
throw new UnsupportedAccountCapabilityError(`Solana-origin transfers do not support \`${unsupported}\`.`, { vm: 'solana', field: unsupported });
|
|
956
|
-
}
|
|
957
|
-
if (!config.solana || !('owner' in config.solana)) {
|
|
958
|
-
throw new UnsupportedAccountCapabilityError('A managed Solana source is required for Solana-origin transfers.', { vm: 'solana' });
|
|
959
|
-
}
|
|
960
|
-
const sources = input.sourceChains;
|
|
961
|
-
if (sources.length !== 1) {
|
|
962
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery spends exactly one Solana cluster.', { vm: 'solana', field: 'sourceChains' });
|
|
963
|
-
}
|
|
964
|
-
solanaChainId(sources[0]);
|
|
965
|
-
if (!Array.isArray(input.sourceTokens) || input.sourceTokens.length !== 1) {
|
|
966
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery requires exactly one source SPL mint.', { vm: 'solana', field: 'sourceTokens' });
|
|
967
|
-
}
|
|
968
|
-
const sourceToken = input.sourceTokens[0];
|
|
969
|
-
assertSolanaObjectKeys(sourceToken, ['address'], 'sourceTokens[0]');
|
|
970
|
-
try {
|
|
971
|
-
solanaAddress(sourceToken.address);
|
|
972
|
-
}
|
|
973
|
-
catch {
|
|
974
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery requires one valid SPL mint address.', { vm: 'solana', field: 'sourceTokens[0].address' });
|
|
975
|
-
}
|
|
976
|
-
const target = input.targetChain;
|
|
977
|
-
if (!target ||
|
|
978
|
-
typeof target !== 'object' ||
|
|
979
|
-
'kind' in target ||
|
|
980
|
-
'caip2' in target ||
|
|
981
|
-
typeof target.id !== 'number' ||
|
|
982
|
-
!Number.isSafeInteger(target.id) ||
|
|
983
|
-
target.id < 0 ||
|
|
984
|
-
!formatCaip2(target.id).startsWith('eip155:')) {
|
|
985
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery requires a viem EVM destination chain.', { vm: 'solana', field: 'targetChain' });
|
|
986
|
-
}
|
|
987
|
-
if (!Array.isArray(input.tokenRequests) || input.tokenRequests.length !== 1) {
|
|
988
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery requires exactly one destination token request.', { vm: 'solana', field: 'tokenRequests' });
|
|
989
|
-
}
|
|
990
|
-
const request = input.tokenRequests[0];
|
|
991
|
-
assertSolanaObjectKeys(request, ['address', 'amount'], 'tokenRequests[0]');
|
|
992
|
-
if (typeof request.address !== 'string' || !isAddress(request.address)) {
|
|
993
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery requires an EVM destination token address.', { vm: 'solana', field: 'tokenRequests[0].address' });
|
|
994
|
-
}
|
|
995
|
-
if (request.amount !== undefined &&
|
|
996
|
-
(typeof request.amount !== 'bigint' || request.amount <= 0n)) {
|
|
997
|
-
throw new UnsupportedAccountCapabilityError('A delivery amount must be a positive bigint when provided.', { vm: 'solana', field: 'tokenRequests[0].amount' });
|
|
998
|
-
}
|
|
999
|
-
if (input.recipient !== undefined &&
|
|
1000
|
-
(typeof input.recipient !== 'string' || !isAddress(input.recipient))) {
|
|
1001
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery recipient must be an EVM address.', { vm: 'solana', field: 'recipient' });
|
|
1002
|
-
}
|
|
1003
|
-
if (input.sponsored !== undefined && input.sponsored !== false) {
|
|
1004
|
-
throw new UnsupportedAccountCapabilityError('Managed Solana transfers are not sponsorable; omit `sponsored` or set it to false.', { vm: 'solana' });
|
|
1005
|
-
}
|
|
1006
|
-
if (input.appFees !== undefined) {
|
|
1007
|
-
assertSolanaObjectKeys(input.appFees, ['feeBps'], 'appFees');
|
|
1008
|
-
}
|
|
1009
|
-
if (input.protocolFees !== undefined) {
|
|
1010
|
-
assertSolanaObjectKeys(input.protocolFees, ['feeBps'], 'protocolFees');
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
function assertSupportedTransaction(transaction, config) {
|
|
1014
|
-
const input = transaction;
|
|
1015
|
-
if (isCrossChainSolanaOrigin(transaction)) {
|
|
1016
|
-
assertSupportedSolanaOriginDelivery(input, config);
|
|
1017
|
-
return;
|
|
1018
|
-
}
|
|
1019
|
-
const hasChain = Object.hasOwn(input, 'chain') && input.chain !== undefined;
|
|
1020
|
-
const hasTarget = Object.hasOwn(input, 'targetChain') && input.targetChain !== undefined;
|
|
1021
|
-
if (hasChain === hasTarget) {
|
|
1022
|
-
throw new UnsupportedAccountCapabilityError('A transaction must set exactly one of `chain` or `targetChain`.');
|
|
1023
|
-
}
|
|
1024
|
-
if (hasChain && typeof input.chain === 'object' && input.chain !== null) {
|
|
1025
|
-
const chain = input.chain;
|
|
1026
|
-
if (chain.kind === 'svm') {
|
|
1027
|
-
assertSupportedSolanaTransaction(input, config);
|
|
1028
|
-
return;
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
if (hasTarget && typeof input.targetChain === 'object' && input.targetChain) {
|
|
1032
|
-
const target = input.targetChain;
|
|
1033
|
-
if (!('kind' in target || 'caip2' in target) &&
|
|
1034
|
-
(typeof target.id !== 'number' ||
|
|
1035
|
-
!Number.isSafeInteger(target.id) ||
|
|
1036
|
-
target.id < 0 ||
|
|
1037
|
-
!formatCaip2(target.id).startsWith('eip155:'))) {
|
|
1038
|
-
throw new UnsupportedAccountCapabilityError('A viem EVM destination must use an eip155 chain ID.');
|
|
1039
|
-
}
|
|
1040
|
-
if (('kind' in target || 'caip2' in target) &&
|
|
1041
|
-
!((target.kind === 'svm' &&
|
|
1042
|
-
typeof target.caip2 === 'string' &&
|
|
1043
|
-
target.caip2.startsWith('solana:')) ||
|
|
1044
|
-
(target.kind === 'tvm' &&
|
|
1045
|
-
typeof target.caip2 === 'string' &&
|
|
1046
|
-
target.caip2.startsWith('tron:')) ||
|
|
1047
|
-
(target.kind === 'stellar' &&
|
|
1048
|
-
typeof target.caip2 === 'string' &&
|
|
1049
|
-
target.caip2.startsWith('stellar:')) ||
|
|
1050
|
-
(target.kind === 'hypercore' &&
|
|
1051
|
-
(target.caip2 === 'hypercore:spot' ||
|
|
1052
|
-
target.caip2 === 'hypercore:perp')))) {
|
|
1053
|
-
throw new UnsupportedAccountCapabilityError('The destination chain descriptor has mismatched VM and CAIP-2 fields.');
|
|
1054
|
-
}
|
|
1055
|
-
if (target.kind === 'svm') {
|
|
1056
|
-
if (!Array.isArray(input.tokenRequests) ||
|
|
1057
|
-
input.tokenRequests.length === 0) {
|
|
1058
|
-
throw new UnsupportedAccountCapabilityError('Solana destinations require at least one token request.', { vm: 'solana' });
|
|
1059
|
-
}
|
|
1060
|
-
for (const request of input.tokenRequests) {
|
|
1061
|
-
if (typeof request.address !== 'string') {
|
|
1062
|
-
throw new UnsupportedAccountCapabilityError('Solana token requests require a Solana address.');
|
|
1063
|
-
}
|
|
1064
|
-
solanaAddress(request.address);
|
|
1065
|
-
if (request.amount !== undefined &&
|
|
1066
|
-
(typeof request.amount !== 'bigint' || request.amount <= 0n)) {
|
|
1067
|
-
throw new UnsupportedAccountCapabilityError('Solana token request amounts must be positive.');
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
// Anything other than a Solana address string would slip past both
|
|
1071
|
-
// recipient defaults and then be dropped downstream, quoting a delivery
|
|
1072
|
-
// with no receiver at all.
|
|
1073
|
-
if (input.recipient !== undefined) {
|
|
1074
|
-
if (typeof input.recipient !== 'string') {
|
|
1075
|
-
throw new UnsupportedAccountCapabilityError('A Solana delivery recipient must be a Solana address.', { vm: 'solana' });
|
|
1076
|
-
}
|
|
1077
|
-
solanaAddress(input.recipient);
|
|
1078
|
-
}
|
|
1079
|
-
if (input.calls !== undefined ||
|
|
1080
|
-
input.instructions !== undefined ||
|
|
1081
|
-
input.hyperCore !== undefined) {
|
|
1082
|
-
throw new UnsupportedAccountCapabilityError('Solana destinations support token delivery only; calls, custom instructions and HyperCore actions are unavailable.', { vm: 'solana' });
|
|
1083
|
-
}
|
|
1084
|
-
if (input.recipient === undefined && !config.solana) {
|
|
1085
|
-
throw new AccountVmNotConfiguredError('solana');
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
if (input.sourceChains !== undefined) {
|
|
1090
|
-
if (!Array.isArray(input.sourceChains)) {
|
|
1091
|
-
throw new UnsupportedAccountCapabilityError('`sourceChains` must be an array of managed source chains.');
|
|
1092
|
-
}
|
|
1093
|
-
if (input.sourceChains.length === 0) {
|
|
1094
|
-
throw new UnsupportedAccountCapabilityError('`sourceChains` must contain at least one managed source.');
|
|
1095
|
-
}
|
|
1096
|
-
const ids = input.sourceChains.map((value) => typeof value === 'object' && value
|
|
1097
|
-
? value.id
|
|
1098
|
-
: null);
|
|
1099
|
-
const invalidSource = input.sourceChains.some((value) => {
|
|
1100
|
-
if (!value || typeof value !== 'object')
|
|
1101
|
-
return true;
|
|
1102
|
-
const source = value;
|
|
1103
|
-
return (typeof source.id !== 'number' ||
|
|
1104
|
-
!Number.isSafeInteger(source.id) ||
|
|
1105
|
-
source.id < 0 ||
|
|
1106
|
-
Object.hasOwn(source, 'kind') ||
|
|
1107
|
-
Object.hasOwn(source, 'caip2') ||
|
|
1108
|
-
!formatCaip2(source.id).startsWith('eip155:'));
|
|
1109
|
-
});
|
|
1110
|
-
if (invalidSource) {
|
|
1111
|
-
throw new UnsupportedAccountCapabilityError('Only viem EVM chains from the managed EVM account can be used as sources.');
|
|
1112
|
-
}
|
|
1113
|
-
if (new Set(ids).size !== ids.length) {
|
|
1114
|
-
throw new UnsupportedAccountCapabilityError('`sourceChains` cannot contain duplicate chains.');
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
const calls = input.calls;
|
|
1118
|
-
if (input.recipient !== undefined) {
|
|
1119
|
-
if (Array.isArray(calls) && calls.length > 0) {
|
|
1120
|
-
throw new UnsupportedAccountCapabilityError('An explicit delivery recipient cannot execute destination calls. Omit `recipient` to execute with the invoking managed EVM account.');
|
|
1121
|
-
}
|
|
1122
|
-
if (!Array.isArray(input.tokenRequests) ||
|
|
1123
|
-
input.tokenRequests.length === 0) {
|
|
1124
|
-
throw new UnsupportedAccountCapabilityError('An explicit delivery recipient requires at least one token request.');
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
export function normalizeTransaction(transaction, config) {
|
|
1129
|
-
assertSupportedTransaction(transaction, config);
|
|
1130
|
-
if (isCrossChainSolanaOrigin(transaction)) {
|
|
1131
|
-
return Object.freeze({
|
|
1132
|
-
...transaction,
|
|
1133
|
-
sourceChains: [
|
|
1134
|
-
Object.freeze({ ...transaction.sourceChains[0] }),
|
|
1135
|
-
],
|
|
1136
|
-
sourceTokens: [
|
|
1137
|
-
Object.freeze({ ...transaction.sourceTokens[0] }),
|
|
1138
|
-
],
|
|
1139
|
-
tokenRequests: [
|
|
1140
|
-
Object.freeze({ ...transaction.tokenRequests[0] }),
|
|
1141
|
-
],
|
|
1142
|
-
...(transaction.appFees
|
|
1143
|
-
? { appFees: Object.freeze({ ...transaction.appFees }) }
|
|
1144
|
-
: {}),
|
|
1145
|
-
...(transaction.protocolFees
|
|
1146
|
-
? { protocolFees: Object.freeze({ ...transaction.protocolFees }) }
|
|
1147
|
-
: {}),
|
|
1148
|
-
});
|
|
1149
|
-
}
|
|
1150
|
-
if (isSolanaInstructionExecution(transaction)) {
|
|
1151
|
-
const lookupTables = normalizeSolanaAddressLookupTables(transaction.addressLookupTables);
|
|
1152
|
-
const { addressLookupTables: _tables, ...rest } = transaction;
|
|
1153
|
-
return Object.freeze({
|
|
1154
|
-
...rest,
|
|
1155
|
-
chain: Object.freeze({ ...transaction.chain }),
|
|
1156
|
-
instructions: normalizeSolanaInstructions(transaction.instructions),
|
|
1157
|
-
...(lookupTables ? { addressLookupTables: lookupTables } : {}),
|
|
1158
|
-
});
|
|
1159
|
-
}
|
|
1160
|
-
if (isSameChainSolanaOrigin(transaction)) {
|
|
1161
|
-
const request = transaction.tokenRequests[0];
|
|
1162
|
-
return Object.freeze({
|
|
1163
|
-
...transaction,
|
|
1164
|
-
chain: Object.freeze({ ...transaction.chain }),
|
|
1165
|
-
tokenRequests: [Object.freeze({ ...request })],
|
|
1166
|
-
...(transaction.appFees
|
|
1167
|
-
? { appFees: Object.freeze({ ...transaction.appFees }) }
|
|
1168
|
-
: {}),
|
|
1169
|
-
...(transaction.protocolFees
|
|
1170
|
-
? { protocolFees: Object.freeze({ ...transaction.protocolFees }) }
|
|
1171
|
-
: {}),
|
|
1172
|
-
});
|
|
1173
|
-
}
|
|
1174
|
-
if ('targetChain' in transaction &&
|
|
1175
|
-
transaction.targetChain !== undefined &&
|
|
1176
|
-
'kind' in transaction.targetChain &&
|
|
1177
|
-
transaction.targetChain.kind === 'svm' &&
|
|
1178
|
-
transaction.recipient === undefined &&
|
|
1179
|
-
config.solana &&
|
|
1180
|
-
'address' in config.solana) {
|
|
1181
|
-
return Object.freeze({
|
|
1182
|
-
...transaction,
|
|
1183
|
-
recipient: config.solana.address,
|
|
1184
|
-
});
|
|
1185
|
-
}
|
|
1186
|
-
return Object.freeze({ ...transaction });
|
|
1187
|
-
}
|
|
1188
536
|
function narrowQuoterPin(derived, explicit) {
|
|
1189
537
|
if (!explicit)
|
|
1190
538
|
return derived;
|
|
@@ -1196,13 +544,7 @@ function narrowQuoterPin(derived, explicit) {
|
|
|
1196
544
|
: derived.include.filter((quoter) => !explicit.exclude.includes(quoter));
|
|
1197
545
|
return { include: narrowed };
|
|
1198
546
|
}
|
|
1199
|
-
export function adaptTransaction(context, transaction, hyperCoreAction
|
|
1200
|
-
const nonEvmOrigin = 'chain' in transaction && transaction.chain && 'kind' in transaction.chain
|
|
1201
|
-
? transaction.chain.kind
|
|
1202
|
-
: transaction.sourceChains?.find((chain) => 'kind' in chain)?.kind;
|
|
1203
|
-
if (nonEvmOrigin) {
|
|
1204
|
-
throw new UnsupportedAccountCapabilityError('Non-EVM origin execution is not supported yet. Use a viem EVM chain as the managed source.', { vm: nonEvmOrigin });
|
|
1205
|
-
}
|
|
547
|
+
export function adaptTransaction(context, transaction, hyperCoreAction) {
|
|
1206
548
|
const destination = 'chain' in transaction
|
|
1207
549
|
? getChainReference(transaction.chain.id)
|
|
1208
550
|
: 'kind' in transaction.targetChain
|
|
@@ -1211,7 +553,7 @@ export function adaptTransaction(context, transaction, hyperCoreAction, automati
|
|
|
1211
553
|
const destinationChainId = destination.kind === 'evm' ? destination.id : undefined;
|
|
1212
554
|
const sourceChains = 'chain' in transaction
|
|
1213
555
|
? [getChainReference(transaction.chain.id)]
|
|
1214
|
-
:
|
|
556
|
+
: transaction.sourceChains?.map((chain) => getChainReference(chain.id));
|
|
1215
557
|
const evmSources = sourceChains?.flatMap((chain) => chain.kind === 'evm' ? [chain] : []);
|
|
1216
558
|
return {
|
|
1217
559
|
destination,
|
|
@@ -1386,12 +728,9 @@ function adaptSessionSelection(transaction, chains) {
|
|
|
1386
728
|
]));
|
|
1387
729
|
}
|
|
1388
730
|
const chainIds = new Set(chains.flatMap((chain) => chain.kind === 'evm' && chain.id !== undefined ? [chain.id] : []));
|
|
1389
|
-
if ('chain' in transaction
|
|
731
|
+
if ('chain' in transaction)
|
|
1390
732
|
chainIds.add(transaction.chain.id);
|
|
1391
|
-
|
|
1392
|
-
if ('targetChain' in transaction &&
|
|
1393
|
-
transaction.targetChain &&
|
|
1394
|
-
'id' in transaction.targetChain) {
|
|
733
|
+
if ('targetChain' in transaction && 'id' in transaction.targetChain) {
|
|
1395
734
|
chainIds.add(transaction.targetChain.id);
|
|
1396
735
|
}
|
|
1397
736
|
return Object.fromEntries([...chainIds].map((chainId) => [
|
|
@@ -1405,18 +744,11 @@ function adaptSessionSelection(transaction, chains) {
|
|
|
1405
744
|
function adaptSourceAssets(sourceAssets, chainIds) {
|
|
1406
745
|
if (!sourceAssets)
|
|
1407
746
|
return chainIds ? { chainIds } : undefined;
|
|
1408
|
-
const eligible = chainIds ? new Set(chainIds) : undefined;
|
|
1409
|
-
const assertEligible = (chainId) => {
|
|
1410
|
-
if (eligible && !eligible.has(chainId)) {
|
|
1411
|
-
throw new UnsupportedAccountCapabilityError(`Source assets reference chain ${chainId}, which is outside the eligible source scope.`, { chainId });
|
|
1412
|
-
}
|
|
1413
|
-
};
|
|
1414
747
|
if (Array.isArray(sourceAssets)) {
|
|
1415
748
|
if (sourceAssets.length > 0 && typeof sourceAssets[0] === 'object') {
|
|
1416
749
|
const chainTokens = {};
|
|
1417
750
|
const chainTokenAmounts = {};
|
|
1418
751
|
for (const item of sourceAssets) {
|
|
1419
|
-
assertEligible(item.chain.id);
|
|
1420
752
|
const token = normalizeTokenAddress(item.address, item.chain.id, false);
|
|
1421
753
|
if (item.amount === undefined)
|
|
1422
754
|
(chainTokens[item.chain.id] ??= []).push(token);
|
|
@@ -1439,8 +771,7 @@ function adaptSourceAssets(sourceAssets, chainIds) {
|
|
|
1439
771
|
}
|
|
1440
772
|
// ChainTokenMap: validate every per-chain list too, so symbols can't slip
|
|
1441
773
|
// through this input the way they can't through tokenRequests / ExactInputConfig.
|
|
1442
|
-
for (const
|
|
1443
|
-
assertEligible(Number(chainId));
|
|
774
|
+
for (const tokens of Object.values(sourceAssets)) {
|
|
1444
775
|
validateTokenAddresses(tokens);
|
|
1445
776
|
}
|
|
1446
777
|
return { chainTokens: sourceAssets };
|