@rhinestone/sdk 0.0.0-dev-20260917224650 → 0.0.0-dev-20260921165705
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 +11 -26
- 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 +47 -736
- 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/auth.d.ts +1 -1
- package/dist/src/clients/orchestrator/auth.d.ts.map +1 -1
- package/dist/src/clients/orchestrator/auth.js +2 -2
- package/dist/src/clients/orchestrator/client.js +6 -6
- 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/port.d.ts +3 -3
- package/dist/src/clients/orchestrator/port.d.ts.map +1 -1
- package/dist/src/clients/orchestrator/public.d.ts +7 -55
- package/dist/src/clients/orchestrator/public.d.ts.map +1 -1
- package/dist/src/clients/orchestrator/types.d.ts +5 -23
- 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 +13 -216
- 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 +6 -11
- package/dist/src/transactions/intents/submit.d.ts.map +1 -1
- package/dist/src/transactions/intents/submit.js +0 -4
- 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 +13 -8
- 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 -89
- package/dist/src/transactions/intents/solana.d.ts.map +0 -1
- package/dist/src/transactions/intents/solana.js +0 -305
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;
|
|
@@ -129,9 +64,8 @@ function reconstructInput(context, prepared, intentId) {
|
|
|
129
64
|
: normalizeIntentQuote(candidate)),
|
|
130
65
|
// The public prepared data only carries the serialized request, so the
|
|
131
66
|
// reconstructed request holds decimal strings where the internal type
|
|
132
|
-
// declares bigints. Sound because
|
|
133
|
-
//
|
|
134
|
-
// `options.sponsorSettings`.
|
|
67
|
+
// declares bigints. Sound because nothing after preparation reads it: the
|
|
68
|
+
// sponsorship digest was taken at quote time.
|
|
135
69
|
request: prepared.intentInput,
|
|
136
70
|
intentInput: adaptTransaction(context, prepared.transaction),
|
|
137
71
|
};
|
|
@@ -141,12 +75,8 @@ function toSignedTransactionData(prepared, signed, cache) {
|
|
|
141
75
|
...prepared,
|
|
142
76
|
quote: toPublicQuote(signed.prepared.quote),
|
|
143
77
|
originSignatures: signed.originSignatures,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
: {}),
|
|
147
|
-
...(signed.targetSignature
|
|
148
|
-
? { targetExecutionSignature: signed.targetSignature }
|
|
149
|
-
: {}),
|
|
78
|
+
destinationSignature: signed.destinationSignature,
|
|
79
|
+
targetExecutionSignature: signed.targetSignature,
|
|
150
80
|
};
|
|
151
81
|
cache.set(data, signed);
|
|
152
82
|
return data;
|
|
@@ -156,143 +86,21 @@ function referenceChain() {
|
|
|
156
86
|
// needs some EVM chain reference. With no bundled chain set, use mainnet.
|
|
157
87
|
return toEvmChainReference(1);
|
|
158
88
|
}
|
|
159
|
-
export function createAccountFacade(compatibilityConfig,
|
|
89
|
+
export function createAccountFacade(compatibilityConfig, composition) {
|
|
160
90
|
// Intent identity caches are facade-scoped. Values crossing account/SDK
|
|
161
91
|
// instances are reconstructed and validated by the receiving account.
|
|
162
92
|
const preparedIntents = new WeakMap();
|
|
163
93
|
const signedIntents = new WeakMap();
|
|
164
94
|
const context = (method) => materializeAccountInvocationContext(composition.config, compatibilityConfig, method);
|
|
165
95
|
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
|
-
const sponsorSettings = toSponsorSettings(transaction.sponsored);
|
|
216
|
-
return sponsorSettings ? { sponsorSettings } : {};
|
|
217
|
-
})(),
|
|
218
|
-
};
|
|
219
|
-
if (isCrossChainSolanaOrigin(transaction)) {
|
|
220
|
-
return {
|
|
221
|
-
...common,
|
|
222
|
-
chain: transaction.sourceChains[0],
|
|
223
|
-
action: {
|
|
224
|
-
kind: 'transfer',
|
|
225
|
-
mint: transaction.sourceTokens[0].address,
|
|
226
|
-
...(transaction.tokenRequests[0].amount === undefined
|
|
227
|
-
? {}
|
|
228
|
-
: { amount: transaction.tokenRequests[0].amount }),
|
|
229
|
-
delivery: {
|
|
230
|
-
kind: 'cross-chain',
|
|
231
|
-
chainId: transaction.targetChain.id,
|
|
232
|
-
token: transaction.tokenRequests[0].address,
|
|
233
|
-
// Resolved here rather than in `normalizeTransaction` so the
|
|
234
|
-
// prepare and reconstruct paths agree on the same recipient.
|
|
235
|
-
recipient: transaction.recipient ?? accountAddress,
|
|
236
|
-
},
|
|
237
|
-
},
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
if (isSolanaInstructionExecution(transaction)) {
|
|
241
|
-
return {
|
|
242
|
-
...common,
|
|
243
|
-
chain: transaction.chain,
|
|
244
|
-
action: {
|
|
245
|
-
kind: 'instructions',
|
|
246
|
-
instructions: normalizeSolanaInstructions(transaction.instructions),
|
|
247
|
-
...(transaction.addressLookupTables
|
|
248
|
-
? { addressLookupTables: transaction.addressLookupTables }
|
|
249
|
-
: {}),
|
|
250
|
-
},
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
return {
|
|
254
|
-
...common,
|
|
255
|
-
chain: transaction.chain,
|
|
256
|
-
action: {
|
|
257
|
-
kind: 'transfer',
|
|
258
|
-
mint: transaction.tokenRequests[0].address,
|
|
259
|
-
...(transaction.tokenRequests[0].amount === undefined
|
|
260
|
-
? {}
|
|
261
|
-
: { amount: transaction.tokenRequests[0].amount }),
|
|
262
|
-
delivery: { kind: 'same-chain', recipient: transaction.recipient },
|
|
263
|
-
},
|
|
264
|
-
};
|
|
265
|
-
};
|
|
266
|
-
const assertSolanaMetadata = (actual, expected) => {
|
|
267
|
-
const expectedEntries = Object.entries(solanaMetadata(expected));
|
|
268
|
-
if (!actual ||
|
|
269
|
-
Object.keys(actual).length !== expectedEntries.length ||
|
|
270
|
-
expectedEntries.some(([key, value]) => actual[key] !== value)) {
|
|
271
|
-
throw new InvalidSolanaTransactionArtifactError('the execution binding does not match this account, environment, chain, recipient, or mint');
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
|
-
const resolveSolanaPrepared = (ctx, prepared, intentId, explicitQuote) => {
|
|
275
|
-
const transfer = solanaTransfer(ctx, prepared.transaction);
|
|
276
|
-
assertSolanaMetadata(prepared.execution, transfer);
|
|
277
|
-
const quote = explicitQuote ?? selectedPublicQuote(prepared, intentId);
|
|
278
|
-
return workflowsFor(ctx).reconstructSolanaIntent({
|
|
279
|
-
traceId: prepared.quotes.traceId,
|
|
280
|
-
transfer,
|
|
281
|
-
intentInput: prepared.intentInput,
|
|
282
|
-
quote: normalizeIntentQuote(quote),
|
|
283
|
-
quotes: prepared.quotes.all.map((candidate) => normalizeIntentQuote(candidate)),
|
|
284
|
-
});
|
|
285
|
-
};
|
|
286
96
|
const resolvePrepared = (ctx, prepared, intentId) => {
|
|
287
|
-
assertSupportedTransaction(prepared.transaction, publicConfig);
|
|
288
|
-
assertSupportedSignData(selectedPublicQuote(prepared, intentId).signData);
|
|
289
97
|
const cached = preparedIntents.get(prepared);
|
|
290
98
|
if (cached && !intentId)
|
|
291
99
|
return Promise.resolve(cached);
|
|
292
100
|
return workflowsFor(ctx).reconstructPreparedIntent(ctx, reconstructInput(ctx, prepared, intentId));
|
|
293
101
|
};
|
|
294
102
|
const account = {
|
|
295
|
-
config:
|
|
103
|
+
config: compatibilityConfig,
|
|
296
104
|
deploy(chain, params) {
|
|
297
105
|
const ctx = context('deploy');
|
|
298
106
|
return workflowsFor(ctx).deploy(ctx, toEvmChainReference(chain.id), {
|
|
@@ -320,106 +128,49 @@ export function createAccountFacade(compatibilityConfig, publicConfig, compositi
|
|
|
320
128
|
},
|
|
321
129
|
async prepareTransaction(transaction) {
|
|
322
130
|
const ctx = context('prepare-intent');
|
|
323
|
-
const initiallyNormalized = normalizeTransaction(transaction, publicConfig);
|
|
324
|
-
const normalized = 'targetChain' in initiallyNormalized &&
|
|
325
|
-
initiallyNormalized.targetChain !== undefined &&
|
|
326
|
-
'kind' in initiallyNormalized.targetChain &&
|
|
327
|
-
initiallyNormalized.targetChain.kind === 'svm' &&
|
|
328
|
-
initiallyNormalized.recipient === undefined &&
|
|
329
|
-
capturedSolanaIdentity
|
|
330
|
-
? Object.freeze({
|
|
331
|
-
...initiallyNormalized,
|
|
332
|
-
recipient: locateSwig(asSwigNamespace('dev-v1'), capturedSolanaIdentity).wallet,
|
|
333
|
-
})
|
|
334
|
-
: initiallyNormalized;
|
|
335
|
-
if (isSolanaOrigin(normalized)) {
|
|
336
|
-
const prepared = await workflowsFor(ctx).prepareSolanaIntent(solanaTransfer(ctx, normalized));
|
|
337
|
-
return toPreparedSolanaTransactionData(prepared, normalized);
|
|
338
|
-
}
|
|
339
131
|
// Before the quote, not after: the quote's `signData` registers an agent
|
|
340
132
|
// derived from the action's bytes, so the action has to be concrete here.
|
|
341
133
|
const hyperCoreAction = await resolveHyperCoreAction({
|
|
342
|
-
options:
|
|
343
|
-
account:
|
|
134
|
+
options: transaction.hyperCore,
|
|
135
|
+
account: account.getAddress(),
|
|
344
136
|
...(ctx.sdk.hyperliquid ? { hyperliquid: ctx.sdk.hyperliquid } : {}),
|
|
345
137
|
});
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
normalized.sourceChains === undefined
|
|
349
|
-
? await workflowsFor(ctx).getEligibleEvmSourceChains(destinationChainReference(normalized.targetChain))
|
|
350
|
-
: undefined;
|
|
351
|
-
if (automaticSources && automaticSources.length === 0) {
|
|
352
|
-
throw new UnsupportedAccountCapabilityError('No managed EVM source chains are eligible for this destination.');
|
|
353
|
-
}
|
|
354
|
-
const prepared = await workflowsFor(ctx).prepareIntent(ctx, adaptTransaction(ctx, normalized, hyperCoreAction, automaticSources?.map(({ id }) => id)));
|
|
355
|
-
return toPreparedTransactionData(prepared, normalized, preparedIntents);
|
|
138
|
+
const prepared = await workflowsFor(ctx).prepareIntent(ctx, adaptTransaction(ctx, transaction, hyperCoreAction));
|
|
139
|
+
return toPreparedTransactionData(prepared, transaction, preparedIntents);
|
|
356
140
|
},
|
|
357
141
|
getTransactionMessages(preparedTransaction, options) {
|
|
358
|
-
assertSupportedTransaction(preparedTransaction.transaction, publicConfig);
|
|
359
142
|
const quote = selectedPublicQuote(preparedTransaction, options?.intentId);
|
|
360
|
-
if (!isSolanaOrigin(preparedTransaction.transaction)) {
|
|
361
|
-
assertSupportedSignData(quote.signData);
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
364
|
-
const ctx = context('get-intent-messages');
|
|
365
|
-
resolveSolanaPrepared(ctx, preparedTransaction, options?.intentId);
|
|
366
|
-
}
|
|
367
143
|
return {
|
|
368
144
|
origin: [...quote.signData.origin],
|
|
369
|
-
|
|
370
|
-
? { destination: quote.signData.destination }
|
|
371
|
-
: {}),
|
|
145
|
+
destination: quote.signData.destination,
|
|
372
146
|
...(quote.signData.targetExecution
|
|
373
147
|
? { targetExecution: quote.signData.targetExecution }
|
|
374
148
|
: {}),
|
|
375
149
|
};
|
|
376
150
|
},
|
|
377
|
-
signTransaction: (
|
|
151
|
+
signTransaction: ((preparedTransaction, options) => {
|
|
378
152
|
const ctx = context('sign-intent');
|
|
379
153
|
const workflows = workflowsFor(ctx);
|
|
380
|
-
if (isSolanaOrigin(preparedTransaction.transaction)) {
|
|
381
|
-
if (options && 'owner' in options) {
|
|
382
|
-
throw new IndependentSigningNotSupportedError({
|
|
383
|
-
context: { vm: 'solana' },
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
const prepared = resolveSolanaPrepared(ctx, preparedTransaction, options?.intentId);
|
|
387
|
-
const signed = await workflows.signSolanaIntent({
|
|
388
|
-
prepared,
|
|
389
|
-
owner: solanaOwner(),
|
|
390
|
-
});
|
|
391
|
-
return {
|
|
392
|
-
...preparedTransaction,
|
|
393
|
-
quote: toPublicQuote(signed.prepared.quote),
|
|
394
|
-
originSignatures: [signed.signature],
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
154
|
if (options && 'owner' in options) {
|
|
398
155
|
// Independent owner signing is unsupported for smart-session intents;
|
|
399
156
|
// reject before resolving sessions (which would issue an RPC read),
|
|
400
157
|
// matching the legacy fast-fail.
|
|
401
158
|
if (preparedTransaction.transaction.signers?.type === 'session') {
|
|
402
|
-
|
|
159
|
+
return Promise.reject(new IndependentSigningNotSupportedError());
|
|
403
160
|
}
|
|
404
161
|
const signerId = signerIdForOwner(options.owner);
|
|
405
|
-
|
|
406
|
-
return workflows.signIntentAsOwner(ctx, internal, {
|
|
162
|
+
return resolvePrepared(ctx, preparedTransaction, options.intentId).then((internal) => workflows.signIntentAsOwner(ctx, internal, {
|
|
407
163
|
signerId,
|
|
408
164
|
...(options.validatorId === undefined
|
|
409
165
|
? {}
|
|
410
166
|
: { validatorId: options.validatorId }),
|
|
411
|
-
});
|
|
167
|
+
}));
|
|
412
168
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
169
|
+
return resolvePrepared(ctx, preparedTransaction, options?.intentId)
|
|
170
|
+
.then((internal) => workflows.signIntent(ctx, internal))
|
|
171
|
+
.then(({ intent }) => toSignedTransactionData(preparedTransaction, intent, signedIntents));
|
|
416
172
|
}),
|
|
417
173
|
async assembleTransaction(preparedTransaction, signatures) {
|
|
418
|
-
if (isSolanaOrigin(preparedTransaction.transaction)) {
|
|
419
|
-
throw new IndependentSigningNotSupportedError({
|
|
420
|
-
context: { vm: 'solana' },
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
174
|
const ctx = context('assemble-intent');
|
|
424
175
|
const workflows = workflowsFor(ctx);
|
|
425
176
|
const intentIds = [...new Set(signatures.map(({ intentId }) => intentId))];
|
|
@@ -431,10 +182,6 @@ export function createAccountFacade(compatibilityConfig, publicConfig, compositi
|
|
|
431
182
|
return toSignedTransactionData(preparedTransaction, signed, signedIntents);
|
|
432
183
|
},
|
|
433
184
|
async signAuthorizations(preparedTransaction) {
|
|
434
|
-
assertSupportedTransaction(preparedTransaction.transaction, publicConfig);
|
|
435
|
-
if (isSolanaOrigin(preparedTransaction.transaction)) {
|
|
436
|
-
throw new UnsupportedAccountCapabilityError('EIP-7702 authorizations are unavailable for Solana-origin transactions.', { vm: 'solana' });
|
|
437
|
-
}
|
|
438
185
|
const ctx = context('sign-authorizations');
|
|
439
186
|
const intentInput = adaptTransaction(ctx, preparedTransaction.transaction);
|
|
440
187
|
const chains = authorizationChains(intentInput);
|
|
@@ -469,7 +216,6 @@ export function createAccountFacade(compatibilityConfig, publicConfig, compositi
|
|
|
469
216
|
return result.signature;
|
|
470
217
|
},
|
|
471
218
|
async signIntent(signData, targetChain, signers) {
|
|
472
|
-
assertSupportedSignData(signData);
|
|
473
219
|
const ctx = context('sign-intent');
|
|
474
220
|
const result = await workflowsFor(ctx).signIntentFromSignData(ctx, {
|
|
475
221
|
signData: {
|
|
@@ -491,35 +237,8 @@ export function createAccountFacade(compatibilityConfig, publicConfig, compositi
|
|
|
491
237
|
};
|
|
492
238
|
},
|
|
493
239
|
async submitTransaction(signedTransaction, options) {
|
|
494
|
-
assertSupportedTransaction(signedTransaction.transaction, publicConfig);
|
|
495
240
|
const ctx = context('submit-intent');
|
|
496
241
|
const workflows = workflowsFor(ctx);
|
|
497
|
-
if (isSolanaOrigin(signedTransaction.transaction)) {
|
|
498
|
-
if (options && Object.keys(options).length > 0) {
|
|
499
|
-
throw new UnsupportedAccountCapabilityError('Solana submission does not accept EVM authorizations or dry-run options.', { vm: 'solana' });
|
|
500
|
-
}
|
|
501
|
-
if (signedTransaction.originSignatures.length !== 1 ||
|
|
502
|
-
typeof signedTransaction.originSignatures[0] !== 'string' ||
|
|
503
|
-
signedTransaction.destinationSignature !== undefined ||
|
|
504
|
-
signedTransaction.targetExecutionSignature !== undefined) {
|
|
505
|
-
throw new InvalidSolanaTransactionArtifactError('submission requires exactly one raw origin signature and no destination or target signature', { intentId: signedTransaction.quote.intentId });
|
|
506
|
-
}
|
|
507
|
-
const prepared = resolveSolanaPrepared(ctx, signedTransaction, signedTransaction.quote.intentId, signedTransaction.quote);
|
|
508
|
-
const submitted = await workflows.submitSolanaIntent({
|
|
509
|
-
prepared,
|
|
510
|
-
signature: signedTransaction.originSignatures[0],
|
|
511
|
-
});
|
|
512
|
-
return {
|
|
513
|
-
type: 'intent',
|
|
514
|
-
id: submitted.intentId,
|
|
515
|
-
traceId: submitted.traceId,
|
|
516
|
-
...(submitted.sourceChains
|
|
517
|
-
? { sourceChains: [...submitted.sourceChains] }
|
|
518
|
-
: {}),
|
|
519
|
-
targetChain: submitted.targetChain,
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
assertSupportedSignData(signedTransaction.quote.signData);
|
|
523
242
|
// Fast path for the same-instance signed object; otherwise (cross-instance
|
|
524
243
|
// replay or caller-tampered signatures) rebuild from the public shape.
|
|
525
244
|
const cached = signedIntents.get(signedTransaction);
|
|
@@ -641,25 +360,10 @@ export function createAccountFacade(compatibilityConfig, publicConfig, compositi
|
|
|
641
360
|
})
|
|
642
361
|
.then((status) => status.receipt);
|
|
643
362
|
}),
|
|
644
|
-
getAddress
|
|
645
|
-
if (vm === 'solana') {
|
|
646
|
-
if (publicConfig.solana &&
|
|
647
|
-
'address' in publicConfig.solana &&
|
|
648
|
-
publicConfig.solana.address) {
|
|
649
|
-
return publicConfig.solana.address;
|
|
650
|
-
}
|
|
651
|
-
if (capturedSolanaIdentity) {
|
|
652
|
-
return locateSwig(asSwigNamespace('dev-v1'), capturedSolanaIdentity)
|
|
653
|
-
.wallet;
|
|
654
|
-
}
|
|
655
|
-
throw new AccountVmNotConfiguredError('solana');
|
|
656
|
-
}
|
|
657
|
-
if (vm !== 'evm' || !publicConfig.evm) {
|
|
658
|
-
throw new AccountVmNotConfiguredError(String(vm));
|
|
659
|
-
}
|
|
363
|
+
getAddress() {
|
|
660
364
|
const ctx = context('get-address');
|
|
661
365
|
return workflowsFor(ctx).getAddress(ctx, referenceChain());
|
|
662
|
-
}
|
|
366
|
+
},
|
|
663
367
|
getPortfolio(onTestnets = false) {
|
|
664
368
|
const ctx = context('get-portfolio');
|
|
665
369
|
return workflowsFor(ctx)
|
|
@@ -828,369 +532,6 @@ chainIds) {
|
|
|
828
532
|
* authorise. With no session scope there is nothing to narrow against and the
|
|
829
533
|
* explicit filter stands on its own.
|
|
830
534
|
*/
|
|
831
|
-
function assertSupportedSignData(signData) {
|
|
832
|
-
const payloads = [
|
|
833
|
-
...signData.origin,
|
|
834
|
-
signData.destination,
|
|
835
|
-
...(signData.targetExecution ? [signData.targetExecution] : []),
|
|
836
|
-
];
|
|
837
|
-
for (const payload of payloads) {
|
|
838
|
-
if (!payload ||
|
|
839
|
-
typeof payload !== 'object' ||
|
|
840
|
-
(payload.kind !== undefined &&
|
|
841
|
-
payload.kind !== 'eip712') ||
|
|
842
|
-
typeof payload.primaryType !== 'string' ||
|
|
843
|
-
!payload.domain ||
|
|
844
|
-
typeof payload.types !== 'object' ||
|
|
845
|
-
typeof payload.message !== 'object') {
|
|
846
|
-
throw new UnsupportedAccountCapabilityError('Only EIP-712 intent signing data is supported by this account.', { capability: 'intent-signing' });
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
function isSameChainSolanaOrigin(transaction) {
|
|
851
|
-
return ('chain' in transaction &&
|
|
852
|
-
typeof transaction.chain === 'object' &&
|
|
853
|
-
transaction.chain !== null &&
|
|
854
|
-
'kind' in transaction.chain &&
|
|
855
|
-
transaction.chain.kind === 'svm');
|
|
856
|
-
}
|
|
857
|
-
function isCrossChainSolanaOrigin(transaction) {
|
|
858
|
-
const source = transaction
|
|
859
|
-
.sourceChains?.[0];
|
|
860
|
-
return (typeof source === 'object' &&
|
|
861
|
-
source !== null &&
|
|
862
|
-
'kind' in source &&
|
|
863
|
-
source.kind === 'svm');
|
|
864
|
-
}
|
|
865
|
-
function isSolanaInstructionExecution(transaction) {
|
|
866
|
-
return (isSameChainSolanaOrigin(transaction) &&
|
|
867
|
-
transaction.instructions !== undefined);
|
|
868
|
-
}
|
|
869
|
-
function isSolanaOrigin(transaction) {
|
|
870
|
-
return (isSameChainSolanaOrigin(transaction) ||
|
|
871
|
-
isCrossChainSolanaOrigin(transaction));
|
|
872
|
-
}
|
|
873
|
-
function assertSolanaObjectKeys(value, allowed, label) {
|
|
874
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
875
|
-
throw new UnsupportedAccountCapabilityError(`Managed Solana ${label} must be an object.`, { vm: 'solana', field: label });
|
|
876
|
-
}
|
|
877
|
-
const unsupported = Object.keys(value).find((key) => !allowed.includes(key));
|
|
878
|
-
if (unsupported) {
|
|
879
|
-
throw new UnsupportedAccountCapabilityError(`Solana-origin transfers do not support \`${label}.${unsupported}\`.`, { vm: 'solana', field: `${label}.${unsupported}` });
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
function assertSupportedSolanaTransaction(input, config) {
|
|
883
|
-
const runsInstructions = input.instructions !== undefined;
|
|
884
|
-
const allowed = new Set(runsInstructions
|
|
885
|
-
? ['chain', 'instructions', 'addressLookupTables', 'sponsored']
|
|
886
|
-
: [
|
|
887
|
-
'chain',
|
|
888
|
-
'tokenRequests',
|
|
889
|
-
'recipient',
|
|
890
|
-
'sponsored',
|
|
891
|
-
'appFees',
|
|
892
|
-
'protocolFees',
|
|
893
|
-
]);
|
|
894
|
-
const unsupported = Object.keys(input).find((key) => !allowed.has(key));
|
|
895
|
-
if (unsupported) {
|
|
896
|
-
throw new UnsupportedAccountCapabilityError(`Solana-origin transfers do not support \`${unsupported}\`.`, { vm: 'solana', field: unsupported });
|
|
897
|
-
}
|
|
898
|
-
if (!config.solana || !('owner' in config.solana)) {
|
|
899
|
-
throw new UnsupportedAccountCapabilityError('A managed Solana source is required for Solana-origin transfers.', { vm: 'solana' });
|
|
900
|
-
}
|
|
901
|
-
solanaChainId(input.chain);
|
|
902
|
-
if (runsInstructions) {
|
|
903
|
-
// Shape and limits are enforced by the normalizer, which also produces the
|
|
904
|
-
// canonical form `normalizeTransaction` stores.
|
|
905
|
-
normalizeSolanaInstructions(input.instructions);
|
|
906
|
-
normalizeSolanaAddressLookupTables(input.addressLookupTables);
|
|
907
|
-
return;
|
|
908
|
-
}
|
|
909
|
-
if (!Array.isArray(input.tokenRequests) || input.tokenRequests.length !== 1) {
|
|
910
|
-
throw new UnsupportedAccountCapabilityError('Managed Solana transfers require exactly one SPL token request.', { vm: 'solana' });
|
|
911
|
-
}
|
|
912
|
-
const request = input.tokenRequests[0];
|
|
913
|
-
assertSolanaObjectKeys(request, ['address', 'amount'], 'tokenRequests[0]');
|
|
914
|
-
if (typeof request.address !== 'string') {
|
|
915
|
-
throw new UnsupportedAccountCapabilityError('Managed Solana transfers require one valid SPL mint address.', { vm: 'solana' });
|
|
916
|
-
}
|
|
917
|
-
try {
|
|
918
|
-
solanaAddress(request.address);
|
|
919
|
-
}
|
|
920
|
-
catch {
|
|
921
|
-
throw new UnsupportedAccountCapabilityError('Managed Solana transfers require one valid SPL mint address.', { vm: 'solana' });
|
|
922
|
-
}
|
|
923
|
-
if (request.amount !== undefined &&
|
|
924
|
-
(typeof request.amount !== 'bigint' || request.amount <= 0n)) {
|
|
925
|
-
throw new UnsupportedAccountCapabilityError('A Solana token amount must be a positive bigint when provided.', { vm: 'solana' });
|
|
926
|
-
}
|
|
927
|
-
if (input.appFees !== undefined) {
|
|
928
|
-
assertSolanaObjectKeys(input.appFees, ['feeBps'], 'appFees');
|
|
929
|
-
}
|
|
930
|
-
if (input.protocolFees !== undefined) {
|
|
931
|
-
assertSolanaObjectKeys(input.protocolFees, ['feeBps'], 'protocolFees');
|
|
932
|
-
}
|
|
933
|
-
if (typeof input.recipient !== 'string') {
|
|
934
|
-
throw new UnsupportedAccountCapabilityError('Managed Solana transfers require an explicit recipient wallet.', { vm: 'solana' });
|
|
935
|
-
}
|
|
936
|
-
try {
|
|
937
|
-
solanaAddress(input.recipient);
|
|
938
|
-
}
|
|
939
|
-
catch {
|
|
940
|
-
throw new UnsupportedAccountCapabilityError('Managed Solana transfers require a valid recipient wallet.', { vm: 'solana' });
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
function assertSupportedSolanaOriginDelivery(input, config) {
|
|
944
|
-
const allowed = new Set([
|
|
945
|
-
'sourceChains',
|
|
946
|
-
'sourceTokens',
|
|
947
|
-
'targetChain',
|
|
948
|
-
'tokenRequests',
|
|
949
|
-
'recipient',
|
|
950
|
-
'sponsored',
|
|
951
|
-
'appFees',
|
|
952
|
-
'protocolFees',
|
|
953
|
-
]);
|
|
954
|
-
const unsupported = Object.keys(input).find((key) => !allowed.has(key));
|
|
955
|
-
if (unsupported) {
|
|
956
|
-
throw new UnsupportedAccountCapabilityError(`Solana-origin transfers do not support \`${unsupported}\`.`, { vm: 'solana', field: unsupported });
|
|
957
|
-
}
|
|
958
|
-
if (!config.solana || !('owner' in config.solana)) {
|
|
959
|
-
throw new UnsupportedAccountCapabilityError('A managed Solana source is required for Solana-origin transfers.', { vm: 'solana' });
|
|
960
|
-
}
|
|
961
|
-
const sources = input.sourceChains;
|
|
962
|
-
if (sources.length !== 1) {
|
|
963
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery spends exactly one Solana cluster.', { vm: 'solana', field: 'sourceChains' });
|
|
964
|
-
}
|
|
965
|
-
solanaChainId(sources[0]);
|
|
966
|
-
if (!Array.isArray(input.sourceTokens) || input.sourceTokens.length !== 1) {
|
|
967
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery requires exactly one source SPL mint.', { vm: 'solana', field: 'sourceTokens' });
|
|
968
|
-
}
|
|
969
|
-
const sourceToken = input.sourceTokens[0];
|
|
970
|
-
assertSolanaObjectKeys(sourceToken, ['address'], 'sourceTokens[0]');
|
|
971
|
-
try {
|
|
972
|
-
solanaAddress(sourceToken.address);
|
|
973
|
-
}
|
|
974
|
-
catch {
|
|
975
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery requires one valid SPL mint address.', { vm: 'solana', field: 'sourceTokens[0].address' });
|
|
976
|
-
}
|
|
977
|
-
const target = input.targetChain;
|
|
978
|
-
if (!target ||
|
|
979
|
-
typeof target !== 'object' ||
|
|
980
|
-
'kind' in target ||
|
|
981
|
-
'caip2' in target ||
|
|
982
|
-
typeof target.id !== 'number' ||
|
|
983
|
-
!Number.isSafeInteger(target.id) ||
|
|
984
|
-
target.id < 0 ||
|
|
985
|
-
!formatCaip2(target.id).startsWith('eip155:')) {
|
|
986
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery requires a viem EVM destination chain.', { vm: 'solana', field: 'targetChain' });
|
|
987
|
-
}
|
|
988
|
-
if (!Array.isArray(input.tokenRequests) || input.tokenRequests.length !== 1) {
|
|
989
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery requires exactly one destination token request.', { vm: 'solana', field: 'tokenRequests' });
|
|
990
|
-
}
|
|
991
|
-
const request = input.tokenRequests[0];
|
|
992
|
-
assertSolanaObjectKeys(request, ['address', 'amount'], 'tokenRequests[0]');
|
|
993
|
-
if (typeof request.address !== 'string' || !isAddress(request.address)) {
|
|
994
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery requires an EVM destination token address.', { vm: 'solana', field: 'tokenRequests[0].address' });
|
|
995
|
-
}
|
|
996
|
-
if (request.amount !== undefined &&
|
|
997
|
-
(typeof request.amount !== 'bigint' || request.amount <= 0n)) {
|
|
998
|
-
throw new UnsupportedAccountCapabilityError('A delivery amount must be a positive bigint when provided.', { vm: 'solana', field: 'tokenRequests[0].amount' });
|
|
999
|
-
}
|
|
1000
|
-
if (input.recipient !== undefined &&
|
|
1001
|
-
(typeof input.recipient !== 'string' || !isAddress(input.recipient))) {
|
|
1002
|
-
throw new UnsupportedAccountCapabilityError('A Solana-origin delivery recipient must be an EVM address.', { vm: 'solana', field: 'recipient' });
|
|
1003
|
-
}
|
|
1004
|
-
if (input.appFees !== undefined) {
|
|
1005
|
-
assertSolanaObjectKeys(input.appFees, ['feeBps'], 'appFees');
|
|
1006
|
-
}
|
|
1007
|
-
if (input.protocolFees !== undefined) {
|
|
1008
|
-
assertSolanaObjectKeys(input.protocolFees, ['feeBps'], 'protocolFees');
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
function assertSupportedTransaction(transaction, config) {
|
|
1012
|
-
const input = transaction;
|
|
1013
|
-
if (isCrossChainSolanaOrigin(transaction)) {
|
|
1014
|
-
assertSupportedSolanaOriginDelivery(input, config);
|
|
1015
|
-
return;
|
|
1016
|
-
}
|
|
1017
|
-
const hasChain = Object.hasOwn(input, 'chain') && input.chain !== undefined;
|
|
1018
|
-
const hasTarget = Object.hasOwn(input, 'targetChain') && input.targetChain !== undefined;
|
|
1019
|
-
if (hasChain === hasTarget) {
|
|
1020
|
-
throw new UnsupportedAccountCapabilityError('A transaction must set exactly one of `chain` or `targetChain`.');
|
|
1021
|
-
}
|
|
1022
|
-
if (hasChain && typeof input.chain === 'object' && input.chain !== null) {
|
|
1023
|
-
const chain = input.chain;
|
|
1024
|
-
if (chain.kind === 'svm') {
|
|
1025
|
-
assertSupportedSolanaTransaction(input, config);
|
|
1026
|
-
return;
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
if (hasTarget && typeof input.targetChain === 'object' && input.targetChain) {
|
|
1030
|
-
const target = input.targetChain;
|
|
1031
|
-
if (!('kind' in target || 'caip2' in target) &&
|
|
1032
|
-
(typeof target.id !== 'number' ||
|
|
1033
|
-
!Number.isSafeInteger(target.id) ||
|
|
1034
|
-
target.id < 0 ||
|
|
1035
|
-
!formatCaip2(target.id).startsWith('eip155:'))) {
|
|
1036
|
-
throw new UnsupportedAccountCapabilityError('A viem EVM destination must use an eip155 chain ID.');
|
|
1037
|
-
}
|
|
1038
|
-
if (('kind' in target || 'caip2' in target) &&
|
|
1039
|
-
!((target.kind === 'svm' &&
|
|
1040
|
-
typeof target.caip2 === 'string' &&
|
|
1041
|
-
target.caip2.startsWith('solana:')) ||
|
|
1042
|
-
(target.kind === 'tvm' &&
|
|
1043
|
-
typeof target.caip2 === 'string' &&
|
|
1044
|
-
target.caip2.startsWith('tron:')) ||
|
|
1045
|
-
(target.kind === 'stellar' &&
|
|
1046
|
-
typeof target.caip2 === 'string' &&
|
|
1047
|
-
target.caip2.startsWith('stellar:')) ||
|
|
1048
|
-
(target.kind === 'hypercore' &&
|
|
1049
|
-
(target.caip2 === 'hypercore:spot' ||
|
|
1050
|
-
target.caip2 === 'hypercore:perp')))) {
|
|
1051
|
-
throw new UnsupportedAccountCapabilityError('The destination chain descriptor has mismatched VM and CAIP-2 fields.');
|
|
1052
|
-
}
|
|
1053
|
-
if (target.kind === 'svm') {
|
|
1054
|
-
if (!Array.isArray(input.tokenRequests) ||
|
|
1055
|
-
input.tokenRequests.length === 0) {
|
|
1056
|
-
throw new UnsupportedAccountCapabilityError('Solana destinations require at least one token request.', { vm: 'solana' });
|
|
1057
|
-
}
|
|
1058
|
-
for (const request of input.tokenRequests) {
|
|
1059
|
-
if (typeof request.address !== 'string') {
|
|
1060
|
-
throw new UnsupportedAccountCapabilityError('Solana token requests require a Solana address.');
|
|
1061
|
-
}
|
|
1062
|
-
solanaAddress(request.address);
|
|
1063
|
-
if (request.amount !== undefined &&
|
|
1064
|
-
(typeof request.amount !== 'bigint' || request.amount <= 0n)) {
|
|
1065
|
-
throw new UnsupportedAccountCapabilityError('Solana token request amounts must be positive.');
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
// Anything other than a Solana address string would slip past both
|
|
1069
|
-
// recipient defaults and then be dropped downstream, quoting a delivery
|
|
1070
|
-
// with no receiver at all.
|
|
1071
|
-
if (input.recipient !== undefined) {
|
|
1072
|
-
if (typeof input.recipient !== 'string') {
|
|
1073
|
-
throw new UnsupportedAccountCapabilityError('A Solana delivery recipient must be a Solana address.', { vm: 'solana' });
|
|
1074
|
-
}
|
|
1075
|
-
solanaAddress(input.recipient);
|
|
1076
|
-
}
|
|
1077
|
-
if (input.calls !== undefined ||
|
|
1078
|
-
input.instructions !== undefined ||
|
|
1079
|
-
input.hyperCore !== undefined) {
|
|
1080
|
-
throw new UnsupportedAccountCapabilityError('Solana destinations support token delivery only; calls, custom instructions and HyperCore actions are unavailable.', { vm: 'solana' });
|
|
1081
|
-
}
|
|
1082
|
-
if (input.recipient === undefined && !config.solana) {
|
|
1083
|
-
throw new AccountVmNotConfiguredError('solana');
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
if (input.sourceChains !== undefined) {
|
|
1088
|
-
if (!Array.isArray(input.sourceChains)) {
|
|
1089
|
-
throw new UnsupportedAccountCapabilityError('`sourceChains` must be an array of managed source chains.');
|
|
1090
|
-
}
|
|
1091
|
-
if (input.sourceChains.length === 0) {
|
|
1092
|
-
throw new UnsupportedAccountCapabilityError('`sourceChains` must contain at least one managed source.');
|
|
1093
|
-
}
|
|
1094
|
-
const ids = input.sourceChains.map((value) => typeof value === 'object' && value
|
|
1095
|
-
? value.id
|
|
1096
|
-
: null);
|
|
1097
|
-
const invalidSource = input.sourceChains.some((value) => {
|
|
1098
|
-
if (!value || typeof value !== 'object')
|
|
1099
|
-
return true;
|
|
1100
|
-
const source = value;
|
|
1101
|
-
return (typeof source.id !== 'number' ||
|
|
1102
|
-
!Number.isSafeInteger(source.id) ||
|
|
1103
|
-
source.id < 0 ||
|
|
1104
|
-
Object.hasOwn(source, 'kind') ||
|
|
1105
|
-
Object.hasOwn(source, 'caip2') ||
|
|
1106
|
-
!formatCaip2(source.id).startsWith('eip155:'));
|
|
1107
|
-
});
|
|
1108
|
-
if (invalidSource) {
|
|
1109
|
-
throw new UnsupportedAccountCapabilityError('Only viem EVM chains from the managed EVM account can be used as sources.');
|
|
1110
|
-
}
|
|
1111
|
-
if (new Set(ids).size !== ids.length) {
|
|
1112
|
-
throw new UnsupportedAccountCapabilityError('`sourceChains` cannot contain duplicate chains.');
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
const calls = input.calls;
|
|
1116
|
-
if (input.recipient !== undefined) {
|
|
1117
|
-
if (Array.isArray(calls) && calls.length > 0) {
|
|
1118
|
-
throw new UnsupportedAccountCapabilityError('An explicit delivery recipient cannot execute destination calls. Omit `recipient` to execute with the invoking managed EVM account.');
|
|
1119
|
-
}
|
|
1120
|
-
if (!Array.isArray(input.tokenRequests) ||
|
|
1121
|
-
input.tokenRequests.length === 0) {
|
|
1122
|
-
throw new UnsupportedAccountCapabilityError('An explicit delivery recipient requires at least one token request.');
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
function freezeSponsorship(sponsored) {
|
|
1127
|
-
if (sponsored === undefined || typeof sponsored === 'boolean')
|
|
1128
|
-
return {};
|
|
1129
|
-
return { sponsored: Object.freeze({ ...sponsored }) };
|
|
1130
|
-
}
|
|
1131
|
-
export function normalizeTransaction(transaction, config) {
|
|
1132
|
-
assertSupportedTransaction(transaction, config);
|
|
1133
|
-
if (isCrossChainSolanaOrigin(transaction)) {
|
|
1134
|
-
return Object.freeze({
|
|
1135
|
-
...transaction,
|
|
1136
|
-
sourceChains: [
|
|
1137
|
-
Object.freeze({ ...transaction.sourceChains[0] }),
|
|
1138
|
-
],
|
|
1139
|
-
sourceTokens: [
|
|
1140
|
-
Object.freeze({ ...transaction.sourceTokens[0] }),
|
|
1141
|
-
],
|
|
1142
|
-
tokenRequests: [
|
|
1143
|
-
Object.freeze({ ...transaction.tokenRequests[0] }),
|
|
1144
|
-
],
|
|
1145
|
-
...(transaction.appFees
|
|
1146
|
-
? { appFees: Object.freeze({ ...transaction.appFees }) }
|
|
1147
|
-
: {}),
|
|
1148
|
-
...(transaction.protocolFees
|
|
1149
|
-
? { protocolFees: Object.freeze({ ...transaction.protocolFees }) }
|
|
1150
|
-
: {}),
|
|
1151
|
-
...freezeSponsorship(transaction.sponsored),
|
|
1152
|
-
});
|
|
1153
|
-
}
|
|
1154
|
-
if (isSolanaInstructionExecution(transaction)) {
|
|
1155
|
-
const lookupTables = normalizeSolanaAddressLookupTables(transaction.addressLookupTables);
|
|
1156
|
-
const { addressLookupTables: _tables, ...rest } = transaction;
|
|
1157
|
-
return Object.freeze({
|
|
1158
|
-
...rest,
|
|
1159
|
-
chain: Object.freeze({ ...transaction.chain }),
|
|
1160
|
-
instructions: normalizeSolanaInstructions(transaction.instructions),
|
|
1161
|
-
...(lookupTables ? { addressLookupTables: lookupTables } : {}),
|
|
1162
|
-
...freezeSponsorship(transaction.sponsored),
|
|
1163
|
-
});
|
|
1164
|
-
}
|
|
1165
|
-
if (isSameChainSolanaOrigin(transaction)) {
|
|
1166
|
-
const request = transaction.tokenRequests[0];
|
|
1167
|
-
return Object.freeze({
|
|
1168
|
-
...transaction,
|
|
1169
|
-
chain: Object.freeze({ ...transaction.chain }),
|
|
1170
|
-
tokenRequests: [Object.freeze({ ...request })],
|
|
1171
|
-
...(transaction.appFees
|
|
1172
|
-
? { appFees: Object.freeze({ ...transaction.appFees }) }
|
|
1173
|
-
: {}),
|
|
1174
|
-
...(transaction.protocolFees
|
|
1175
|
-
? { protocolFees: Object.freeze({ ...transaction.protocolFees }) }
|
|
1176
|
-
: {}),
|
|
1177
|
-
...freezeSponsorship(transaction.sponsored),
|
|
1178
|
-
});
|
|
1179
|
-
}
|
|
1180
|
-
if ('targetChain' in transaction &&
|
|
1181
|
-
transaction.targetChain !== undefined &&
|
|
1182
|
-
'kind' in transaction.targetChain &&
|
|
1183
|
-
transaction.targetChain.kind === 'svm' &&
|
|
1184
|
-
transaction.recipient === undefined &&
|
|
1185
|
-
config.solana &&
|
|
1186
|
-
'address' in config.solana) {
|
|
1187
|
-
return Object.freeze({
|
|
1188
|
-
...transaction,
|
|
1189
|
-
recipient: config.solana.address,
|
|
1190
|
-
});
|
|
1191
|
-
}
|
|
1192
|
-
return Object.freeze({ ...transaction });
|
|
1193
|
-
}
|
|
1194
535
|
function narrowQuoterPin(derived, explicit) {
|
|
1195
536
|
if (!explicit)
|
|
1196
537
|
return derived;
|
|
@@ -1202,39 +543,7 @@ function narrowQuoterPin(derived, explicit) {
|
|
|
1202
543
|
: derived.include.filter((quoter) => !explicit.exclude.includes(quoter));
|
|
1203
544
|
return { include: narrowed };
|
|
1204
545
|
}
|
|
1205
|
-
|
|
1206
|
-
* Translates the public `sponsored` input into the wire's `sponsorSettings`.
|
|
1207
|
-
* Shared by both VMs so the two cannot drift.
|
|
1208
|
-
*/
|
|
1209
|
-
function toSponsorSettings(sponsored) {
|
|
1210
|
-
if (!sponsored)
|
|
1211
|
-
return undefined;
|
|
1212
|
-
if (typeof sponsored === 'boolean') {
|
|
1213
|
-
return {
|
|
1214
|
-
gas: sponsored,
|
|
1215
|
-
bridgeFees: sponsored,
|
|
1216
|
-
swapFees: sponsored,
|
|
1217
|
-
protocolFees: sponsored,
|
|
1218
|
-
};
|
|
1219
|
-
}
|
|
1220
|
-
return {
|
|
1221
|
-
gas: sponsored.gas,
|
|
1222
|
-
bridgeFees: sponsored.bridging,
|
|
1223
|
-
swapFees: sponsored.swaps,
|
|
1224
|
-
protocolFees: sponsored.protocolFees ?? false,
|
|
1225
|
-
// Omitted rather than sent false when unset: the field rides the
|
|
1226
|
-
// server-signature surface, so a key that is present-but-false is not the
|
|
1227
|
-
// same bytes as absent.
|
|
1228
|
-
...(sponsored.swapValue ? { swapValue: true } : {}),
|
|
1229
|
-
};
|
|
1230
|
-
}
|
|
1231
|
-
export function adaptTransaction(context, transaction, hyperCoreAction, automaticSourceChainIds) {
|
|
1232
|
-
const nonEvmOrigin = 'chain' in transaction && transaction.chain && 'kind' in transaction.chain
|
|
1233
|
-
? transaction.chain.kind
|
|
1234
|
-
: transaction.sourceChains?.find((chain) => 'kind' in chain)?.kind;
|
|
1235
|
-
if (nonEvmOrigin) {
|
|
1236
|
-
throw new UnsupportedAccountCapabilityError('Non-EVM origin execution is not supported yet. Use a viem EVM chain as the managed source.', { vm: nonEvmOrigin });
|
|
1237
|
-
}
|
|
546
|
+
export function adaptTransaction(context, transaction, hyperCoreAction) {
|
|
1238
547
|
const destination = 'chain' in transaction
|
|
1239
548
|
? getChainReference(transaction.chain.id)
|
|
1240
549
|
: 'kind' in transaction.targetChain
|
|
@@ -1243,7 +552,7 @@ export function adaptTransaction(context, transaction, hyperCoreAction, automati
|
|
|
1243
552
|
const destinationChainId = destination.kind === 'evm' ? destination.id : undefined;
|
|
1244
553
|
const sourceChains = 'chain' in transaction
|
|
1245
554
|
? [getChainReference(transaction.chain.id)]
|
|
1246
|
-
:
|
|
555
|
+
: transaction.sourceChains?.map((chain) => getChainReference(chain.id));
|
|
1247
556
|
const evmSources = sourceChains?.flatMap((chain) => chain.kind === 'evm' ? [chain] : []);
|
|
1248
557
|
return {
|
|
1249
558
|
destination,
|
|
@@ -1279,10 +588,23 @@ export function adaptTransaction(context, transaction, hyperCoreAction, automati
|
|
|
1279
588
|
...('chain' in transaction && transaction.customDeadline !== undefined
|
|
1280
589
|
? { customDeadline: transaction.customDeadline }
|
|
1281
590
|
: {}),
|
|
1282
|
-
...(
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
591
|
+
...(transaction.sponsored
|
|
592
|
+
? {
|
|
593
|
+
sponsorSettings: typeof transaction.sponsored === 'boolean'
|
|
594
|
+
? {
|
|
595
|
+
gas: transaction.sponsored,
|
|
596
|
+
bridgeFees: transaction.sponsored,
|
|
597
|
+
swapFees: transaction.sponsored,
|
|
598
|
+
protocolFees: transaction.sponsored,
|
|
599
|
+
}
|
|
600
|
+
: {
|
|
601
|
+
gas: transaction.sponsored.gas,
|
|
602
|
+
bridgeFees: transaction.sponsored.bridging,
|
|
603
|
+
swapFees: transaction.sponsored.swaps,
|
|
604
|
+
protocolFees: transaction.sponsored.protocolFees ?? false,
|
|
605
|
+
},
|
|
606
|
+
}
|
|
607
|
+
: {}),
|
|
1286
608
|
...(transaction.settlementLayers
|
|
1287
609
|
? { settlementLayers: transaction.settlementLayers }
|
|
1288
610
|
: {}),
|
|
@@ -1399,12 +721,9 @@ function adaptSessionSelection(transaction, chains) {
|
|
|
1399
721
|
]));
|
|
1400
722
|
}
|
|
1401
723
|
const chainIds = new Set(chains.flatMap((chain) => chain.kind === 'evm' && chain.id !== undefined ? [chain.id] : []));
|
|
1402
|
-
if ('chain' in transaction
|
|
724
|
+
if ('chain' in transaction)
|
|
1403
725
|
chainIds.add(transaction.chain.id);
|
|
1404
|
-
|
|
1405
|
-
if ('targetChain' in transaction &&
|
|
1406
|
-
transaction.targetChain &&
|
|
1407
|
-
'id' in transaction.targetChain) {
|
|
726
|
+
if ('targetChain' in transaction && 'id' in transaction.targetChain) {
|
|
1408
727
|
chainIds.add(transaction.targetChain.id);
|
|
1409
728
|
}
|
|
1410
729
|
return Object.fromEntries([...chainIds].map((chainId) => [
|
|
@@ -1418,18 +737,11 @@ function adaptSessionSelection(transaction, chains) {
|
|
|
1418
737
|
function adaptSourceAssets(sourceAssets, chainIds) {
|
|
1419
738
|
if (!sourceAssets)
|
|
1420
739
|
return chainIds ? { chainIds } : undefined;
|
|
1421
|
-
const eligible = chainIds ? new Set(chainIds) : undefined;
|
|
1422
|
-
const assertEligible = (chainId) => {
|
|
1423
|
-
if (eligible && !eligible.has(chainId)) {
|
|
1424
|
-
throw new UnsupportedAccountCapabilityError(`Source assets reference chain ${chainId}, which is outside the eligible source scope.`, { chainId });
|
|
1425
|
-
}
|
|
1426
|
-
};
|
|
1427
740
|
if (Array.isArray(sourceAssets)) {
|
|
1428
741
|
if (sourceAssets.length > 0 && typeof sourceAssets[0] === 'object') {
|
|
1429
742
|
const chainTokens = {};
|
|
1430
743
|
const chainTokenAmounts = {};
|
|
1431
744
|
for (const item of sourceAssets) {
|
|
1432
|
-
assertEligible(item.chain.id);
|
|
1433
745
|
const token = normalizeTokenAddress(item.address, item.chain.id, false);
|
|
1434
746
|
if (item.amount === undefined)
|
|
1435
747
|
(chainTokens[item.chain.id] ??= []).push(token);
|
|
@@ -1452,8 +764,7 @@ function adaptSourceAssets(sourceAssets, chainIds) {
|
|
|
1452
764
|
}
|
|
1453
765
|
// ChainTokenMap: validate every per-chain list too, so symbols can't slip
|
|
1454
766
|
// through this input the way they can't through tokenRequests / ExactInputConfig.
|
|
1455
|
-
for (const
|
|
1456
|
-
assertEligible(Number(chainId));
|
|
767
|
+
for (const tokens of Object.values(sourceAssets)) {
|
|
1457
768
|
validateTokenAddresses(tokens);
|
|
1458
769
|
}
|
|
1459
770
|
return { chainTokens: sourceAssets };
|