@eth-optimism/actions-sdk 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/lend/core/LendProvider.js +2 -2
- package/dist/actions/lend/core/LendProvider.js.map +1 -1
- package/dist/actions/shared/morpho/contracts.d.ts +1 -1
- package/dist/actions/shared/morpho/contracts.js +1 -1
- package/dist/actions/swap/core/SwapProvider.d.ts.map +1 -1
- package/dist/actions/swap/core/SwapProvider.js +4 -4
- package/dist/actions/swap/core/SwapProvider.js.map +1 -1
- package/dist/actions/swap/namespaces/WalletSwapNamespace.d.ts.map +1 -1
- package/dist/actions/swap/namespaces/WalletSwapNamespace.js +5 -2
- package/dist/actions/swap/namespaces/WalletSwapNamespace.js.map +1 -1
- package/dist/constants/supportedChains.d.ts +5790 -2
- package/dist/constants/supportedChains.d.ts.map +1 -1
- package/dist/constants/supportedChains.js +66 -20
- package/dist/constants/supportedChains.js.map +1 -1
- package/dist/core/error/errors.d.ts +24 -0
- package/dist/core/error/errors.d.ts.map +1 -1
- package/dist/core/error/errors.js +28 -0
- package/dist/core/error/errors.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/services/ChainManager.d.ts.map +1 -1
- package/dist/services/ChainManager.js +16 -0
- package/dist/services/ChainManager.js.map +1 -1
- package/dist/types/actions.d.ts +12 -7
- package/dist/types/actions.d.ts.map +1 -1
- package/dist/types/actions.js +22 -1
- package/dist/types/actions.js.map +1 -1
- package/dist/types/providers.d.ts +20 -6
- package/dist/types/providers.d.ts.map +1 -1
- package/dist/types/providers.js +12 -1
- package/dist/types/providers.js.map +1 -1
- package/dist/utils/__tests__/lendConfig.test.d.ts +2 -0
- package/dist/utils/__tests__/lendConfig.test.d.ts.map +1 -0
- package/dist/utils/__tests__/lendConfig.test.js +44 -0
- package/dist/utils/__tests__/lendConfig.test.js.map +1 -0
- package/dist/utils/lendConfig.d.ts +9 -0
- package/dist/utils/lendConfig.d.ts.map +1 -0
- package/dist/utils/lendConfig.js +12 -0
- package/dist/utils/lendConfig.js.map +1 -0
- package/dist/wallet/core/wallets/abstract/Wallet.d.ts +9 -0
- package/dist/wallet/core/wallets/abstract/Wallet.d.ts.map +1 -1
- package/dist/wallet/core/wallets/abstract/Wallet.js +11 -0
- package/dist/wallet/core/wallets/abstract/Wallet.js.map +1 -1
- package/dist/wallet/core/wallets/abstract/__tests__/Wallet.spec.js +13 -0
- package/dist/wallet/core/wallets/abstract/__tests__/Wallet.spec.js.map +1 -1
- package/dist/wallet/core/wallets/eoa/EOAWallet.d.ts +12 -4
- package/dist/wallet/core/wallets/eoa/EOAWallet.d.ts.map +1 -1
- package/dist/wallet/core/wallets/eoa/EOAWallet.js +17 -12
- package/dist/wallet/core/wallets/eoa/EOAWallet.js.map +1 -1
- package/dist/wallet/core/wallets/eoa/__tests__/EOAWallet.spec.js +37 -20
- package/dist/wallet/core/wallets/eoa/__tests__/EOAWallet.spec.js.map +1 -1
- package/dist/wallet/node/wallets/hosted/privy/__tests__/PrivyWallet.spec.js +4 -1
- package/dist/wallet/node/wallets/hosted/privy/__tests__/PrivyWallet.spec.js.map +1 -1
- package/dist/wallet/node/wallets/hosted/turnkey/__tests__/TurnkeyWallet.spec.js +2 -1
- package/dist/wallet/node/wallets/hosted/turnkey/__tests__/TurnkeyWallet.spec.js.map +1 -1
- package/dist/wallet/node/wallets/local/__tests__/LocalWallet.spec.js +2 -1
- package/dist/wallet/node/wallets/local/__tests__/LocalWallet.spec.js.map +1 -1
- package/dist/wallet/react/wallets/hosted/dynamic/__tests__/DynamicWallet.spec.js +2 -1
- package/dist/wallet/react/wallets/hosted/dynamic/__tests__/DynamicWallet.spec.js.map +1 -1
- package/dist/wallet/react/wallets/hosted/privy/__tests__/PrivyWallet.spec.js +2 -1
- package/dist/wallet/react/wallets/hosted/privy/__tests__/PrivyWallet.spec.js.map +1 -1
- package/dist/wallet/react/wallets/hosted/turnkey/__tests__/TurnkeyWallet.spec.js +2 -1
- package/dist/wallet/react/wallets/hosted/turnkey/__tests__/TurnkeyWallet.spec.js.map +1 -1
- package/package.json +1 -1
- package/src/actions/lend/core/LendProvider.ts +2 -2
- package/src/actions/shared/morpho/contracts.ts +1 -1
- package/src/actions/swap/core/SwapProvider.ts +4 -5
- package/src/actions/swap/namespaces/WalletSwapNamespace.ts +5 -4
- package/src/constants/supportedChains.ts +81 -21
- package/src/core/error/errors.ts +37 -0
- package/src/index.ts +13 -1
- package/src/services/ChainManager.ts +19 -0
- package/src/types/actions.ts +25 -10
- package/src/types/providers.ts +24 -6
- package/src/utils/__tests__/lendConfig.test.ts +52 -0
- package/src/utils/lendConfig.ts +17 -0
- package/src/wallet/core/wallets/abstract/Wallet.ts +12 -0
- package/src/wallet/core/wallets/abstract/__tests__/Wallet.spec.ts +15 -0
- package/src/wallet/core/wallets/eoa/EOAWallet.ts +17 -12
- package/src/wallet/core/wallets/eoa/__tests__/EOAWallet.spec.ts +43 -24
- package/src/wallet/node/wallets/hosted/privy/__tests__/PrivyWallet.spec.ts +4 -1
- package/src/wallet/node/wallets/hosted/turnkey/__tests__/TurnkeyWallet.spec.ts +2 -1
- package/src/wallet/node/wallets/local/__tests__/LocalWallet.spec.ts +2 -1
- package/src/wallet/react/wallets/hosted/dynamic/__tests__/DynamicWallet.spec.ts +2 -1
- package/src/wallet/react/wallets/hosted/privy/__tests__/PrivyWallet.spec.ts +2 -1
- package/src/wallet/react/wallets/hosted/turnkey/__tests__/TurnkeyWallet.spec.ts +2 -1
|
@@ -78,7 +78,7 @@ export function getMorphoContracts(
|
|
|
78
78
|
/**
|
|
79
79
|
* Get all chain IDs where Morpho contracts are deployed.
|
|
80
80
|
* Returns chains present in the local contracts registry.
|
|
81
|
-
* Filtering against
|
|
81
|
+
* Filtering against SUPPORTED_CHAIN_IDS and developer-configured chains
|
|
82
82
|
* is handled by the LendProvider base class.
|
|
83
83
|
*/
|
|
84
84
|
export function getSupportedChainIds(): number[] {
|
|
@@ -3,11 +3,12 @@ import { formatUnits } from 'viem'
|
|
|
3
3
|
|
|
4
4
|
import { UNIVERSAL_ROUTER_MSG_SENDER } from '@/actions/swap/core/markets.js'
|
|
5
5
|
import type { SupportedChainId } from '@/constants/supportedChains.js'
|
|
6
|
-
import {
|
|
6
|
+
import { SUPPORTED_CHAIN_IDS } from '@/constants/supportedChains.js'
|
|
7
7
|
import {
|
|
8
8
|
MarketNotAllowedError,
|
|
9
9
|
ProviderNotConfiguredError,
|
|
10
10
|
QuoteExpiredError,
|
|
11
|
+
QuoteRecipientMissingError,
|
|
11
12
|
} from '@/core/error/errors.js'
|
|
12
13
|
import type { ChainManager } from '@/services/ChainManager.js'
|
|
13
14
|
import type {
|
|
@@ -221,7 +222,7 @@ export abstract class SwapProvider<
|
|
|
221
222
|
const configuredChains = this.chainManager.getSupportedChains()
|
|
222
223
|
return this.protocolSupportedChainIds().filter(
|
|
223
224
|
(id) =>
|
|
224
|
-
(
|
|
225
|
+
(SUPPORTED_CHAIN_IDS as readonly number[]).includes(id) &&
|
|
225
226
|
configuredChains.includes(id),
|
|
226
227
|
)
|
|
227
228
|
}
|
|
@@ -436,9 +437,7 @@ export abstract class SwapProvider<
|
|
|
436
437
|
quote: SwapQuote,
|
|
437
438
|
): Promise<SwapTransaction> {
|
|
438
439
|
if (!quote.recipient) {
|
|
439
|
-
throw new
|
|
440
|
-
'SwapQuote.recipient missing — _getQuote must populate it',
|
|
441
|
-
)
|
|
440
|
+
throw new QuoteRecipientMissingError()
|
|
442
441
|
}
|
|
443
442
|
const approvals = await this._buildApprovals(quote)
|
|
444
443
|
|
|
@@ -3,6 +3,7 @@ import { isAddressEqual } from 'viem'
|
|
|
3
3
|
import { QUOTE_DISCRIMINATOR } from '@/actions/swap/core/SwapProvider.js'
|
|
4
4
|
import { BaseSwapNamespace } from '@/actions/swap/namespaces/BaseSwapNamespace.js'
|
|
5
5
|
import type { SupportedChainId } from '@/constants/supportedChains.js'
|
|
6
|
+
import { QuoteRecipientMismatchError } from '@/core/error/errors.js'
|
|
6
7
|
import type { SwapExecuteParamsResolved } from '@/services/nameservices/ens/types.js'
|
|
7
8
|
import type { RecipientResolver } from '@/services/nameservices/ens/utils.js'
|
|
8
9
|
import type { SwapSettings } from '@/types/actions.js'
|
|
@@ -91,10 +92,10 @@ export class WalletSwapNamespace extends BaseSwapNamespace {
|
|
|
91
92
|
*/
|
|
92
93
|
private requireQuoteForThisWallet(quote: SwapQuote): SwapQuote {
|
|
93
94
|
if (!isAddressEqual(quote.recipient, this.wallet.address)) {
|
|
94
|
-
throw new
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
)
|
|
95
|
+
throw new QuoteRecipientMismatchError({
|
|
96
|
+
quoteRecipient: quote.recipient,
|
|
97
|
+
walletAddress: this.wallet.address,
|
|
98
|
+
})
|
|
98
99
|
}
|
|
99
100
|
return quote
|
|
100
101
|
}
|
|
@@ -20,26 +20,86 @@ import {
|
|
|
20
20
|
worldchain,
|
|
21
21
|
} from 'viem/chains'
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
23
|
+
const slug = (name: string): string => name.toLowerCase().replace(/\s+/g, '-')
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Single source of truth for the chains the SDK supports. All other
|
|
27
|
+
* chain-related constants in this module are derived from this list.
|
|
28
|
+
*/
|
|
29
|
+
const SUPPORTED_CHAINS = [
|
|
30
|
+
mainnet,
|
|
31
|
+
sepolia,
|
|
32
|
+
optimism,
|
|
33
|
+
optimismSepolia,
|
|
34
|
+
base,
|
|
35
|
+
baseSepolia,
|
|
36
|
+
unichain,
|
|
37
|
+
unichainSepolia,
|
|
38
|
+
worldchain,
|
|
39
|
+
bob,
|
|
40
|
+
celo,
|
|
41
|
+
fraxtal,
|
|
42
|
+
ink,
|
|
43
|
+
lisk,
|
|
44
|
+
metalL2,
|
|
45
|
+
mode,
|
|
46
|
+
soneium,
|
|
47
|
+
superseed,
|
|
48
|
+
swellchain,
|
|
43
49
|
] as const
|
|
44
50
|
|
|
45
|
-
export type SupportedChainId = (typeof
|
|
51
|
+
export type SupportedChainId = (typeof SUPPORTED_CHAINS)[number]['id']
|
|
52
|
+
|
|
53
|
+
export const SUPPORTED_CHAIN_IDS = SUPPORTED_CHAINS.map(
|
|
54
|
+
(c) => c.id,
|
|
55
|
+
) as readonly SupportedChainId[]
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Extra names that resolve to a chain in addition to its canonical
|
|
59
|
+
* `slug(chain.name)`. Add entries here only when there's a shorter or
|
|
60
|
+
* more familiar name worth accepting alongside the viem one.
|
|
61
|
+
*/
|
|
62
|
+
const EXTRA_CHAIN_ALIASES: Partial<
|
|
63
|
+
Record<SupportedChainId, readonly string[]>
|
|
64
|
+
> = {
|
|
65
|
+
[mainnet.id]: ['mainnet'], // viem: 'ethereum'
|
|
66
|
+
[optimism.id]: ['optimism'], // viem: 'op-mainnet'
|
|
67
|
+
[worldchain.id]: ['worldchain'], // viem: 'world-chain'
|
|
68
|
+
[metalL2.id]: ['metal'], // viem: 'metal-l2'
|
|
69
|
+
[mode.id]: ['mode'], // viem: 'mode-mainnet'
|
|
70
|
+
[soneium.id]: ['soneium'], // viem: 'soneium-mainnet'
|
|
71
|
+
[swellchain.id]: ['swell'], // viem: 'swellchain'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Canonical CLI / human-friendly shortname for each supported chain:
|
|
76
|
+
* `slug(chain.name)` (e.g. `base-sepolia`, `ethereum`, `op-mainnet`).
|
|
77
|
+
* Use this for display. For input parsing prefer `chainIdFromShortname`,
|
|
78
|
+
* which also accepts entries from `EXTRA_CHAIN_ALIASES`.
|
|
79
|
+
*/
|
|
80
|
+
export const SUPPORTED_CHAIN_SHORTNAMES: Record<SupportedChainId, string> =
|
|
81
|
+
Object.fromEntries(
|
|
82
|
+
SUPPORTED_CHAINS.map((chain) => [chain.id, slug(chain.name)]),
|
|
83
|
+
) as Record<SupportedChainId, string>
|
|
84
|
+
|
|
85
|
+
const NAME_TO_ID: Record<string, SupportedChainId> = (() => {
|
|
86
|
+
const index: Record<string, SupportedChainId> = {}
|
|
87
|
+
for (const chain of SUPPORTED_CHAINS) {
|
|
88
|
+
const id = chain.id as SupportedChainId
|
|
89
|
+
index[SUPPORTED_CHAIN_SHORTNAMES[id]] = id
|
|
90
|
+
for (const alias of EXTRA_CHAIN_ALIASES[id] ?? []) index[alias] = id
|
|
91
|
+
}
|
|
92
|
+
return index
|
|
93
|
+
})()
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Resolve a user-typed chain name to a `SupportedChainId`. Accepts both
|
|
97
|
+
* the canonical shortname (`mainnet`, `optimism`) and the viem `chain.name`
|
|
98
|
+
* slug (`ethereum`, `op-mainnet`). Case-insensitive. Returns `undefined`
|
|
99
|
+
* for unknown names.
|
|
100
|
+
*/
|
|
101
|
+
export function chainIdFromShortname(
|
|
102
|
+
name: string,
|
|
103
|
+
): SupportedChainId | undefined {
|
|
104
|
+
return NAME_TO_ID[name.toLowerCase()]
|
|
105
|
+
}
|
package/src/core/error/errors.ts
CHANGED
|
@@ -285,3 +285,40 @@ export class InvalidParamsError extends ActionsError {
|
|
|
285
285
|
this.received = params.received
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
|
+
|
|
289
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
290
|
+
// Swap Quote
|
|
291
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Thrown when a pre-built `SwapQuote` is dispatched against a wallet whose
|
|
295
|
+
* address differs from the quote's `recipient`. Some routers (Velodrome
|
|
296
|
+
* v2/leaf) encode the recipient directly into calldata, so silently swapping
|
|
297
|
+
* recipients would route output tokens to the wrong address.
|
|
298
|
+
*/
|
|
299
|
+
export class QuoteRecipientMismatchError extends ActionsError {
|
|
300
|
+
override name = 'QuoteRecipientMismatchError' as const
|
|
301
|
+
quoteRecipient: string
|
|
302
|
+
walletAddress: string
|
|
303
|
+
|
|
304
|
+
constructor(params: { quoteRecipient: string; walletAddress: string }) {
|
|
305
|
+
super(
|
|
306
|
+
`SwapQuote was generated for a different recipient (${params.quoteRecipient}); re-quote via wallet.swap.getQuote(...) so calldata is bound to this wallet (${params.walletAddress})`,
|
|
307
|
+
)
|
|
308
|
+
this.quoteRecipient = params.quoteRecipient
|
|
309
|
+
this.walletAddress = params.walletAddress
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Thrown when a provider's `_getQuote` returns a `SwapQuote` without a
|
|
315
|
+
* `recipient`. The base namespace requires every quote to be wallet-bound
|
|
316
|
+
* before approvals or calldata are built.
|
|
317
|
+
*/
|
|
318
|
+
export class QuoteRecipientMissingError extends ActionsError {
|
|
319
|
+
override name = 'QuoteRecipientMissingError' as const
|
|
320
|
+
|
|
321
|
+
constructor() {
|
|
322
|
+
super('SwapQuote.recipient missing — _getQuote must populate it')
|
|
323
|
+
}
|
|
324
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -58,7 +58,9 @@ export {
|
|
|
58
58
|
WSTETH,
|
|
59
59
|
} from '@/constants/assets.js'
|
|
60
60
|
export {
|
|
61
|
-
|
|
61
|
+
chainIdFromShortname,
|
|
62
|
+
SUPPORTED_CHAIN_IDS,
|
|
63
|
+
SUPPORTED_CHAIN_SHORTNAMES,
|
|
62
64
|
type SupportedChainId,
|
|
63
65
|
} from '@/constants/supportedChains.js'
|
|
64
66
|
export * from '@/core/error/errors.js'
|
|
@@ -77,9 +79,11 @@ export {
|
|
|
77
79
|
} from '@/services/nameservices/ens/utils.js'
|
|
78
80
|
export type {
|
|
79
81
|
ActionsConfig,
|
|
82
|
+
ApprovalMode,
|
|
80
83
|
ApyBreakdown,
|
|
81
84
|
Asset,
|
|
82
85
|
EOATransactionReceipt,
|
|
86
|
+
LendAction,
|
|
83
87
|
LendConfig,
|
|
84
88
|
LendMarket,
|
|
85
89
|
LendMarketConfig,
|
|
@@ -88,6 +92,7 @@ export type {
|
|
|
88
92
|
LendMarketPosition,
|
|
89
93
|
LendMarketSupply,
|
|
90
94
|
LendProviderConfig,
|
|
95
|
+
LendProviderName,
|
|
91
96
|
LendTransaction,
|
|
92
97
|
LendTransactionReceipt,
|
|
93
98
|
SwapConfig,
|
|
@@ -112,7 +117,14 @@ export type {
|
|
|
112
117
|
WalletConfig,
|
|
113
118
|
WalletSwapParams,
|
|
114
119
|
} from '@/types/index.js'
|
|
120
|
+
export {
|
|
121
|
+
APPROVAL_MODES,
|
|
122
|
+
LEND_ACTIONS,
|
|
123
|
+
LEND_PROVIDER_NAMES,
|
|
124
|
+
SWAP_PROVIDER_NAMES,
|
|
125
|
+
} from '@/types/index.js'
|
|
115
126
|
export { getAssetAddress, isAssetSupportedOnChain } from '@/utils/assets.js'
|
|
127
|
+
export { getLendMarketAllowlist } from '@/utils/lendConfig.js'
|
|
116
128
|
export { serializeBigInt } from '@/utils/serializers.js'
|
|
117
129
|
export * from '@/wallet/core/error/errors.js'
|
|
118
130
|
export { Wallet } from '@/wallet/core/wallets/abstract/Wallet.js'
|
|
@@ -11,11 +11,28 @@ import {
|
|
|
11
11
|
} from 'viem'
|
|
12
12
|
import type { BundlerClient, SmartAccount } from 'viem/account-abstraction'
|
|
13
13
|
import { createBundlerClient } from 'viem/account-abstraction'
|
|
14
|
+
import { mainnet, sepolia } from 'viem/chains'
|
|
14
15
|
|
|
15
16
|
import type { SupportedChainId } from '@/constants/supportedChains.js'
|
|
16
17
|
import { ChainNotSupportedError } from '@/core/error/errors.js'
|
|
17
18
|
import type { ChainConfig } from '@/types/chain.js'
|
|
18
19
|
|
|
20
|
+
/** viem `pollingInterval` (ms) for L2-class chains with ~1-2s blocks. */
|
|
21
|
+
const L2_POLLING_INTERVAL_MS = 1000
|
|
22
|
+
/** viem `pollingInterval` (ms) for L1-class chains with ~12s blocks. */
|
|
23
|
+
const L1_POLLING_INTERVAL_MS = 4000
|
|
24
|
+
|
|
25
|
+
const L1_CHAIN_IDS: ReadonlySet<SupportedChainId> = new Set([
|
|
26
|
+
mainnet.id,
|
|
27
|
+
sepolia.id,
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
function pollingIntervalForChain(chainId: SupportedChainId): number {
|
|
31
|
+
return L1_CHAIN_IDS.has(chainId)
|
|
32
|
+
? L1_POLLING_INTERVAL_MS
|
|
33
|
+
: L2_POLLING_INTERVAL_MS
|
|
34
|
+
}
|
|
35
|
+
|
|
19
36
|
/**
|
|
20
37
|
* Chain Manager Service
|
|
21
38
|
* @description Manages public clients and chain infrastructure for the Verbs SDK.
|
|
@@ -99,6 +116,7 @@ export class ChainManager {
|
|
|
99
116
|
const client = createPublicClient({
|
|
100
117
|
chain: this.getChain(chainId),
|
|
101
118
|
transport: http(bundlerUrl),
|
|
119
|
+
pollingInterval: pollingIntervalForChain(chainId),
|
|
102
120
|
})
|
|
103
121
|
return createBundlerClient({
|
|
104
122
|
account,
|
|
@@ -191,6 +209,7 @@ export class ChainManager {
|
|
|
191
209
|
const client = createPublicClient({
|
|
192
210
|
chain,
|
|
193
211
|
transport: this.getTransportForChain(chainConfig.chainId),
|
|
212
|
+
pollingInterval: pollingIntervalForChain(chainConfig.chainId),
|
|
194
213
|
})
|
|
195
214
|
|
|
196
215
|
clients.set(chainConfig.chainId, client)
|
package/src/types/actions.ts
CHANGED
|
@@ -4,14 +4,24 @@ import type { ChainManager } from '@/services/ChainManager.js'
|
|
|
4
4
|
import type { Asset } from '@/types/asset.js'
|
|
5
5
|
import type { ChainConfig } from '@/types/chain.js'
|
|
6
6
|
import type { LendProviderConfig } from '@/types/lend/index.js'
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
LendProviders,
|
|
9
|
+
SwapProviderName,
|
|
10
|
+
SwapProviders,
|
|
11
|
+
} from '@/types/providers.js'
|
|
8
12
|
import type { SwapProviderConfig } from '@/types/swap/index.js'
|
|
9
13
|
import type { ProviderSpec } from '@/wallet/core/providers/hosted/types/index.js'
|
|
10
14
|
|
|
11
15
|
// Re-export provider configs for convenience
|
|
12
16
|
export type { LendProviderConfig, SwapProviderConfig }
|
|
13
|
-
// Re-export centralized provider maps
|
|
14
|
-
export type {
|
|
17
|
+
// Re-export centralized provider maps and constants
|
|
18
|
+
export type {
|
|
19
|
+
LendProviderName,
|
|
20
|
+
LendProviders,
|
|
21
|
+
SwapProviderName,
|
|
22
|
+
SwapProviders,
|
|
23
|
+
} from '@/types/providers.js'
|
|
24
|
+
export { LEND_PROVIDER_NAMES, SWAP_PROVIDER_NAMES } from '@/types/providers.js'
|
|
15
25
|
|
|
16
26
|
/** Require at least one property to be defined */
|
|
17
27
|
type RequireAtLeastOne<T> = {
|
|
@@ -42,12 +52,6 @@ export type LendConfig = RequireAtLeastOne<{
|
|
|
42
52
|
settings?: LendSettings
|
|
43
53
|
}
|
|
44
54
|
|
|
45
|
-
/** Names of available swap providers — derived from SwapProviders registry */
|
|
46
|
-
export type SwapProviderName = keyof SwapProviders
|
|
47
|
-
|
|
48
|
-
/** Names of available lend providers — derived from LendProviders registry */
|
|
49
|
-
export type LendProviderName = keyof LendProviders
|
|
50
|
-
|
|
51
55
|
/** Routing strategy for selecting a provider when multiple are configured. */
|
|
52
56
|
export type SwapRoutingStrategy = 'price'
|
|
53
57
|
|
|
@@ -151,7 +155,18 @@ export interface ActionsContext {
|
|
|
151
155
|
* Default is `"exact"` for safety. Demo / dogfood configs typically opt into
|
|
152
156
|
* `"max"` to avoid an extra approval tx per swap or supply.
|
|
153
157
|
*/
|
|
154
|
-
export
|
|
158
|
+
export const APPROVAL_MODES = ['exact', 'max'] as const
|
|
159
|
+
|
|
160
|
+
export type ApprovalMode = (typeof APPROVAL_MODES)[number]
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The lend write actions exposed by the SDK's wallet namespace
|
|
164
|
+
* (`openPosition` / `closePosition`). Useful for callers that emit
|
|
165
|
+
* action-tagged output envelopes or branch on the action being performed.
|
|
166
|
+
*/
|
|
167
|
+
export const LEND_ACTIONS = ['open', 'close'] as const
|
|
168
|
+
|
|
169
|
+
export type LendAction = (typeof LEND_ACTIONS)[number]
|
|
155
170
|
|
|
156
171
|
/**
|
|
157
172
|
* Actions SDK configuration
|
package/src/types/providers.ts
CHANGED
|
@@ -3,20 +3,38 @@ import type { SwapProvider } from '@/actions/swap/core/SwapProvider.js'
|
|
|
3
3
|
import type { LendProviderConfig } from '@/types/lend/index.js'
|
|
4
4
|
import type { SwapProviderConfig } from '@/types/swap/index.js'
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Runtime list of lend provider names. Source of truth for both the
|
|
8
|
+
* `LendProviderName` type union and any consumer (CLI, validators) that
|
|
9
|
+
* needs to enumerate provider names at runtime.
|
|
10
|
+
*/
|
|
11
|
+
export const LEND_PROVIDER_NAMES = ['morpho', 'aave'] as const
|
|
12
|
+
|
|
13
|
+
/** Names of available lend providers. */
|
|
14
|
+
export type LendProviderName = (typeof LEND_PROVIDER_NAMES)[number]
|
|
15
|
+
|
|
6
16
|
/**
|
|
7
17
|
* Map of available lend providers keyed by provider name.
|
|
8
|
-
* Add new
|
|
18
|
+
* Add new providers by extending `LEND_PROVIDER_NAMES`.
|
|
9
19
|
*/
|
|
10
20
|
export type LendProviders = {
|
|
11
|
-
|
|
12
|
-
aave?: LendProvider<LendProviderConfig>
|
|
21
|
+
[K in LendProviderName]?: LendProvider<LendProviderConfig>
|
|
13
22
|
}
|
|
14
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Runtime list of swap provider names. Source of truth for both the
|
|
26
|
+
* `SwapProviderName` type union and any consumer (CLI, validators) that
|
|
27
|
+
* needs to enumerate provider names at runtime.
|
|
28
|
+
*/
|
|
29
|
+
export const SWAP_PROVIDER_NAMES = ['uniswap', 'velodrome'] as const
|
|
30
|
+
|
|
31
|
+
/** Names of available swap providers. */
|
|
32
|
+
export type SwapProviderName = (typeof SWAP_PROVIDER_NAMES)[number]
|
|
33
|
+
|
|
15
34
|
/**
|
|
16
35
|
* Map of available swap providers keyed by provider name.
|
|
17
|
-
* Add new
|
|
36
|
+
* Add new providers by extending `SWAP_PROVIDER_NAMES`.
|
|
18
37
|
*/
|
|
19
38
|
export type SwapProviders = {
|
|
20
|
-
|
|
21
|
-
velodrome?: SwapProvider<SwapProviderConfig>
|
|
39
|
+
[K in SwapProviderName]?: SwapProvider<SwapProviderConfig>
|
|
22
40
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { MockUSDCAsset } from '@/__mocks__/MockAssets.js'
|
|
4
|
+
import type { LendConfig } from '@/types/actions.js'
|
|
5
|
+
import type { LendMarketConfig } from '@/types/lend/base.js'
|
|
6
|
+
import { getLendMarketAllowlist } from '@/utils/lendConfig.js'
|
|
7
|
+
|
|
8
|
+
const morphoMarket: LendMarketConfig = {
|
|
9
|
+
address: '0x0000000000000000000000000000000000000001',
|
|
10
|
+
chainId: 130,
|
|
11
|
+
name: 'Morpho USDC',
|
|
12
|
+
asset: MockUSDCAsset,
|
|
13
|
+
lendProvider: 'morpho',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const aaveMarket: LendMarketConfig = {
|
|
17
|
+
address: '0x0000000000000000000000000000000000000002',
|
|
18
|
+
chainId: 130,
|
|
19
|
+
name: 'Aave USDC',
|
|
20
|
+
asset: MockUSDCAsset,
|
|
21
|
+
lendProvider: 'aave',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('getLendMarketAllowlist', () => {
|
|
25
|
+
it('returns empty list when lend config is undefined', () => {
|
|
26
|
+
expect(getLendMarketAllowlist(undefined)).toEqual([])
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('flattens allowlists across all providers', () => {
|
|
30
|
+
const lend: LendConfig = {
|
|
31
|
+
morpho: { marketAllowlist: [morphoMarket] },
|
|
32
|
+
aave: { marketAllowlist: [aaveMarket] },
|
|
33
|
+
}
|
|
34
|
+
expect(getLendMarketAllowlist(lend)).toEqual([morphoMarket, aaveMarket])
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('skips the settings sibling key', () => {
|
|
38
|
+
const lend: LendConfig = {
|
|
39
|
+
morpho: { marketAllowlist: [morphoMarket] },
|
|
40
|
+
settings: { approvalMode: 'max' },
|
|
41
|
+
}
|
|
42
|
+
expect(getLendMarketAllowlist(lend)).toEqual([morphoMarket])
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('returns empty list when no provider declares an allowlist', () => {
|
|
46
|
+
const lend: LendConfig = {
|
|
47
|
+
morpho: {},
|
|
48
|
+
settings: { approvalMode: 'exact' },
|
|
49
|
+
}
|
|
50
|
+
expect(getLendMarketAllowlist(lend)).toEqual([])
|
|
51
|
+
})
|
|
52
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LendConfig } from '@/types/actions.js'
|
|
2
|
+
import type { LendMarketConfig } from '@/types/lend/base.js'
|
|
3
|
+
import { LEND_PROVIDER_NAMES } from '@/types/providers.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Flatten every provider's `marketAllowlist` from a `LendConfig` into a single
|
|
7
|
+
* list. Returns an empty list when `lend` is undefined or no provider declares
|
|
8
|
+
* an allowlist.
|
|
9
|
+
*/
|
|
10
|
+
export function getLendMarketAllowlist(
|
|
11
|
+
lend: LendConfig | undefined,
|
|
12
|
+
): readonly LendMarketConfig[] {
|
|
13
|
+
if (!lend) return []
|
|
14
|
+
return LEND_PROVIDER_NAMES.flatMap(
|
|
15
|
+
(name) => lend[name]?.marketAllowlist ?? [],
|
|
16
|
+
)
|
|
17
|
+
}
|
|
@@ -84,6 +84,18 @@ export abstract class Wallet {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Check whether a wallet namespace (`lend`, `swap`) is configured on this
|
|
89
|
+
* wallet. Useful for callers that branch on capability instead of catching
|
|
90
|
+
* a `TypeError` from `wallet.lend!.openPosition(...)` later. Returns `false`
|
|
91
|
+
* when the namespace is undefined (no providers were registered for it).
|
|
92
|
+
* @param namespace - Wallet namespace name to probe.
|
|
93
|
+
* @returns `true` when the namespace is configured.
|
|
94
|
+
*/
|
|
95
|
+
has(namespace: 'lend' | 'swap'): boolean {
|
|
96
|
+
return this[namespace] !== undefined
|
|
97
|
+
}
|
|
98
|
+
|
|
87
99
|
/**
|
|
88
100
|
* Get asset balances across the requested chains (or all supported chains).
|
|
89
101
|
* @description Fetches ETH and ERC20 token balances for this wallet. By default queries every chain returned by the SDK's `ChainManager`. Pass `options.chainIds` to restrict the query to a subset of those chains; each id is validated against the configured chains and an `InvalidParamsError` / `ChainNotSupportedError` is thrown for unusable input. Uses the configured supported assets from `ActionsConfig.assets` if provided.
|
|
@@ -125,4 +125,19 @@ describe('Wallet (base)', () => {
|
|
|
125
125
|
expect(wallet.lend).toBeDefined()
|
|
126
126
|
expect(wallet.lend).toEqual({})
|
|
127
127
|
})
|
|
128
|
+
|
|
129
|
+
describe('has', () => {
|
|
130
|
+
it("returns false for a namespace that wasn't configured", () => {
|
|
131
|
+
const wallet = new TestWallet(chainManager, address, signer)
|
|
132
|
+
expect(wallet.has('lend')).toBe(false)
|
|
133
|
+
expect(wallet.has('swap')).toBe(false)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('returns true once a namespace has been attached', () => {
|
|
137
|
+
const wallet = new TestWallet(chainManager, address, signer)
|
|
138
|
+
wallet.lend = {} as WalletLendNamespace
|
|
139
|
+
expect(wallet.has('lend')).toBe(true)
|
|
140
|
+
expect(wallet.has('swap')).toBe(false)
|
|
141
|
+
})
|
|
142
|
+
})
|
|
128
143
|
})
|
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
LocalAccount,
|
|
6
6
|
WalletClient,
|
|
7
7
|
} from 'viem'
|
|
8
|
-
import { createWalletClient } from 'viem'
|
|
8
|
+
import { createWalletClient, nonceManager } from 'viem'
|
|
9
9
|
|
|
10
10
|
import type { SupportedChainId } from '@/constants/supportedChains.js'
|
|
11
11
|
import type { TransactionData } from '@/types/lend/index.js'
|
|
@@ -23,8 +23,10 @@ export abstract class EOAWallet extends Wallet {
|
|
|
23
23
|
/**
|
|
24
24
|
* Create a WalletClient for this EOA wallet.
|
|
25
25
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* Attaches viem's default `nonceManager` to the signer so back-to-back
|
|
27
|
+
* `sendTransaction` calls receive sequential nonces without re-fetching
|
|
28
|
+
* `eth_getTransactionCount('pending')` per tx. This avoids races on
|
|
29
|
+
* load-balanced RPCs where pending state lags by one block.
|
|
28
30
|
* @param chainId - The chain ID to create the wallet client for
|
|
29
31
|
* @returns Promise resolving to a WalletClient configured for the specified chain
|
|
30
32
|
*/
|
|
@@ -39,8 +41,11 @@ export abstract class EOAWallet extends Wallet {
|
|
|
39
41
|
[]
|
|
40
42
|
>
|
|
41
43
|
> {
|
|
44
|
+
const account: LocalAccount = this.signer.nonceManager
|
|
45
|
+
? this.signer
|
|
46
|
+
: { ...this.signer, nonceManager }
|
|
42
47
|
return createWalletClient({
|
|
43
|
-
account
|
|
48
|
+
account,
|
|
44
49
|
chain: this.chainManager.getChain(chainId),
|
|
45
50
|
transport: this.chainManager.getTransportForChain(chainId),
|
|
46
51
|
})
|
|
@@ -70,8 +75,14 @@ export abstract class EOAWallet extends Wallet {
|
|
|
70
75
|
/**
|
|
71
76
|
* Send multiple transactions sequentially from this EOA wallet.
|
|
72
77
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
78
|
+
* Each transaction is awaited to inclusion (one confirmation) via `send()`
|
|
79
|
+
* before the next is signed. The `nonceManager` attached in `walletClient()`
|
|
80
|
+
* keeps nonces in sequence locally, so the wait does not need extra
|
|
81
|
+
* confirmations to guarantee nonce monotonicity.
|
|
82
|
+
*
|
|
83
|
+
* Note: this method assumes a sequencer-ordered chain (e.g. OP-stack L2s).
|
|
84
|
+
* On chains with deeper reorg risk, consider an additional confirmations
|
|
85
|
+
* pass at the call site.
|
|
75
86
|
* @param transactionData - Array of transactions to send
|
|
76
87
|
* @param chainId - Chain to send the transactions on
|
|
77
88
|
* @returns Promise resolving to array of transaction receipts (one per transaction)
|
|
@@ -83,12 +94,6 @@ export abstract class EOAWallet extends Wallet {
|
|
|
83
94
|
const receipts: EOATransactionReceipt[] = []
|
|
84
95
|
for (const tx of transactionData) {
|
|
85
96
|
const receipt = await this.send(tx, chainId)
|
|
86
|
-
const publicClient = this.chainManager.getPublicClient(chainId)
|
|
87
|
-
// wait an extra confirmation so give time for nonce to be updated
|
|
88
|
-
await publicClient.waitForTransactionReceipt({
|
|
89
|
-
hash: receipt.transactionHash,
|
|
90
|
-
confirmations: 2,
|
|
91
|
-
})
|
|
92
97
|
receipts.push(receipt)
|
|
93
98
|
}
|
|
94
99
|
return receipts
|