@sip-protocol/sdk 0.7.3 → 0.7.4
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/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
- package/dist/browser.d.mts +10 -4
- package/dist/browser.d.ts +10 -4
- package/dist/browser.js +47556 -19603
- package/dist/browser.mjs +628 -48
- package/dist/chunk-4GRJ5MAW.mjs +152 -0
- package/dist/chunk-5D7A3L3W.mjs +717 -0
- package/dist/chunk-64AYA5F5.mjs +7834 -0
- package/dist/chunk-GMDGB22A.mjs +379 -0
- package/dist/chunk-I534WKN7.mjs +328 -0
- package/dist/chunk-IBZVA5Y7.mjs +1003 -0
- package/dist/chunk-PRRZAWJE.mjs +223 -0
- package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
- package/dist/{chunk-3M3HNQCW.mjs → chunk-YWGJ77A2.mjs} +28656 -13103
- package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
- package/dist/constants-LHAAUC2T.mjs +51 -0
- package/dist/dist-2OGQ7FED.mjs +3957 -0
- package/dist/dist-IFHPYLDX.mjs +254 -0
- package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
- package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
- package/dist/{index-DIBZHOOQ.d.ts → index-DXh2IGkz.d.ts} +21239 -10304
- package/dist/{index-8MQz13eJ.d.mts → index-DeE1ZzA4.d.mts} +21239 -10304
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +48396 -19623
- package/dist/index.mjs +537 -19
- package/dist/interface-Bf7w1PLW.d.mts +679 -0
- package/dist/interface-Bf7w1PLW.d.ts +679 -0
- package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
- package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
- package/dist/proofs/halo2.d.mts +151 -0
- package/dist/proofs/halo2.d.ts +151 -0
- package/dist/proofs/halo2.js +350 -0
- package/dist/proofs/halo2.mjs +11 -0
- package/dist/proofs/kimchi.d.mts +160 -0
- package/dist/proofs/kimchi.d.ts +160 -0
- package/dist/proofs/kimchi.js +431 -0
- package/dist/proofs/kimchi.mjs +13 -0
- package/dist/proofs/noir.d.mts +1 -1
- package/dist/proofs/noir.d.ts +1 -1
- package/dist/proofs/noir.js +74 -18
- package/dist/proofs/noir.mjs +84 -24
- package/dist/solana-U3MEGU7W.mjs +280 -0
- package/dist/validity_proof-3POXLPNY.mjs +21 -0
- package/package.json +54 -21
- package/src/adapters/index.ts +41 -0
- package/src/adapters/jupiter.ts +571 -0
- package/src/adapters/near-intents.ts +135 -0
- package/src/advisor/advisor.ts +653 -0
- package/src/advisor/index.ts +54 -0
- package/src/advisor/tools.ts +303 -0
- package/src/advisor/types.ts +164 -0
- package/src/chains/ethereum/announcement.ts +536 -0
- package/src/chains/ethereum/bnb-optimizations.ts +474 -0
- package/src/chains/ethereum/commitment.ts +522 -0
- package/src/chains/ethereum/constants.ts +462 -0
- package/src/chains/ethereum/deployment.ts +596 -0
- package/src/chains/ethereum/gas-estimation.ts +538 -0
- package/src/chains/ethereum/index.ts +268 -0
- package/src/chains/ethereum/optimizations.ts +614 -0
- package/src/chains/ethereum/privacy-adapter.ts +855 -0
- package/src/chains/ethereum/registry.ts +584 -0
- package/src/chains/ethereum/rpc.ts +905 -0
- package/src/chains/ethereum/stealth.ts +491 -0
- package/src/chains/ethereum/token.ts +790 -0
- package/src/chains/ethereum/transfer.ts +637 -0
- package/src/chains/ethereum/types.ts +456 -0
- package/src/chains/ethereum/viewing-key.ts +455 -0
- package/src/chains/near/commitment.ts +608 -0
- package/src/chains/near/constants.ts +284 -0
- package/src/chains/near/function-call.ts +871 -0
- package/src/chains/near/history.ts +654 -0
- package/src/chains/near/implicit-account.ts +840 -0
- package/src/chains/near/index.ts +393 -0
- package/src/chains/near/native-transfer.ts +658 -0
- package/src/chains/near/nep141.ts +775 -0
- package/src/chains/near/privacy-adapter.ts +889 -0
- package/src/chains/near/resolver.ts +971 -0
- package/src/chains/near/rpc.ts +1016 -0
- package/src/chains/near/stealth.ts +419 -0
- package/src/chains/near/types.ts +317 -0
- package/src/chains/near/viewing-key.ts +876 -0
- package/src/chains/solana/anchor-transfer.ts +386 -0
- package/src/chains/solana/commitment.ts +577 -0
- package/src/chains/solana/constants.ts +126 -12
- package/src/chains/solana/ephemeral-keys.ts +543 -0
- package/src/chains/solana/index.ts +252 -1
- package/src/chains/solana/key-derivation.ts +418 -0
- package/src/chains/solana/kit-compat.ts +334 -0
- package/src/chains/solana/optimizations.ts +560 -0
- package/src/chains/solana/privacy-adapter.ts +605 -0
- package/src/chains/solana/providers/generic.ts +47 -6
- package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
- package/src/chains/solana/providers/helius-enhanced.ts +623 -0
- package/src/chains/solana/providers/helius.ts +186 -33
- package/src/chains/solana/providers/index.ts +31 -0
- package/src/chains/solana/providers/interface.ts +61 -18
- package/src/chains/solana/providers/quicknode.ts +409 -0
- package/src/chains/solana/providers/triton.ts +426 -0
- package/src/chains/solana/providers/webhook.ts +338 -67
- package/src/chains/solana/rpc-client.ts +1150 -0
- package/src/chains/solana/scan.ts +83 -66
- package/src/chains/solana/sol-transfer.ts +732 -0
- package/src/chains/solana/spl-transfer.ts +886 -0
- package/src/chains/solana/stealth-scanner.ts +703 -0
- package/src/chains/solana/sunspot-verifier.ts +453 -0
- package/src/chains/solana/transaction-builder.ts +755 -0
- package/src/chains/solana/transfer.ts +74 -5
- package/src/chains/solana/types.ts +57 -6
- package/src/chains/solana/utils.ts +110 -0
- package/src/chains/solana/viewing-key.ts +807 -0
- package/src/compliance/fireblocks.ts +921 -0
- package/src/compliance/index.ts +23 -0
- package/src/compliance/range-sas.ts +398 -33
- package/src/config/endpoints.ts +100 -0
- package/src/crypto.ts +11 -8
- package/src/errors.ts +82 -0
- package/src/evm/erc4337-relayer.ts +830 -0
- package/src/evm/index.ts +47 -0
- package/src/fees/calculator.ts +396 -0
- package/src/fees/index.ts +87 -0
- package/src/fees/near-contract.ts +429 -0
- package/src/fees/types.ts +268 -0
- package/src/index.ts +686 -1
- package/src/intent.ts +6 -3
- package/src/logger.ts +324 -0
- package/src/network/index.ts +80 -0
- package/src/network/proxy.ts +691 -0
- package/src/optimizations/index.ts +541 -0
- package/src/oracle/types.ts +1 -0
- package/src/privacy-backends/arcium-types.ts +727 -0
- package/src/privacy-backends/arcium.ts +719 -0
- package/src/privacy-backends/combined-privacy.ts +866 -0
- package/src/privacy-backends/cspl-token.ts +595 -0
- package/src/privacy-backends/cspl-types.ts +512 -0
- package/src/privacy-backends/cspl.ts +907 -0
- package/src/privacy-backends/health.ts +488 -0
- package/src/privacy-backends/inco-types.ts +323 -0
- package/src/privacy-backends/inco.ts +616 -0
- package/src/privacy-backends/index.ts +254 -4
- package/src/privacy-backends/interface.ts +649 -6
- package/src/privacy-backends/lru-cache.ts +343 -0
- package/src/privacy-backends/magicblock.ts +458 -0
- package/src/privacy-backends/mock.ts +258 -0
- package/src/privacy-backends/privacycash.ts +13 -17
- package/src/privacy-backends/private-swap.ts +570 -0
- package/src/privacy-backends/rate-limiter.ts +683 -0
- package/src/privacy-backends/registry.ts +414 -2
- package/src/privacy-backends/router.ts +283 -3
- package/src/privacy-backends/shadowwire.ts +449 -0
- package/src/privacy-backends/sip-native.ts +3 -0
- package/src/privacy-logger.ts +191 -0
- package/src/production-safety.ts +373 -0
- package/src/proofs/aggregator.ts +1029 -0
- package/src/proofs/browser-composer.ts +1150 -0
- package/src/proofs/browser.ts +113 -25
- package/src/proofs/cache/index.ts +127 -0
- package/src/proofs/cache/interface.ts +545 -0
- package/src/proofs/cache/key-generator.ts +188 -0
- package/src/proofs/cache/lru-cache.ts +481 -0
- package/src/proofs/cache/multi-tier-cache.ts +575 -0
- package/src/proofs/cache/persistent-cache.ts +788 -0
- package/src/proofs/compliance-proof.ts +872 -0
- package/src/proofs/composer/base.ts +923 -0
- package/src/proofs/composer/index.ts +25 -0
- package/src/proofs/composer/interface.ts +518 -0
- package/src/proofs/composer/types.ts +383 -0
- package/src/proofs/converters/halo2.ts +452 -0
- package/src/proofs/converters/index.ts +208 -0
- package/src/proofs/converters/interface.ts +363 -0
- package/src/proofs/converters/kimchi.ts +462 -0
- package/src/proofs/converters/noir.ts +451 -0
- package/src/proofs/fallback.ts +888 -0
- package/src/proofs/halo2.ts +42 -0
- package/src/proofs/index.ts +471 -0
- package/src/proofs/interface.ts +13 -0
- package/src/proofs/kimchi.ts +42 -0
- package/src/proofs/lazy.ts +1004 -0
- package/src/proofs/mock.ts +25 -1
- package/src/proofs/noir.ts +110 -29
- package/src/proofs/orchestrator.ts +960 -0
- package/src/proofs/parallel/concurrency.ts +297 -0
- package/src/proofs/parallel/dependency-graph.ts +602 -0
- package/src/proofs/parallel/executor.ts +420 -0
- package/src/proofs/parallel/index.ts +131 -0
- package/src/proofs/parallel/interface.ts +685 -0
- package/src/proofs/parallel/worker-pool.ts +644 -0
- package/src/proofs/providers/halo2.ts +560 -0
- package/src/proofs/providers/index.ts +34 -0
- package/src/proofs/providers/kimchi.ts +641 -0
- package/src/proofs/validator.ts +881 -0
- package/src/proofs/verifier.ts +867 -0
- package/src/quantum/index.ts +112 -0
- package/src/quantum/winternitz-vault.ts +639 -0
- package/src/quantum/wots.ts +611 -0
- package/src/settlement/backends/direct-chain.ts +1 -0
- package/src/settlement/index.ts +9 -0
- package/src/settlement/router.ts +732 -46
- package/src/solana/index.ts +72 -0
- package/src/solana/jito-relayer.ts +687 -0
- package/src/solana/noir-verifier-types.ts +430 -0
- package/src/solana/noir-verifier.ts +816 -0
- package/src/stealth/address-derivation.ts +193 -0
- package/src/stealth/ed25519.ts +431 -0
- package/src/stealth/index.ts +233 -0
- package/src/stealth/meta-address.ts +221 -0
- package/src/stealth/secp256k1.ts +368 -0
- package/src/stealth/utils.ts +194 -0
- package/src/stealth.ts +50 -1504
- package/src/sync/index.ts +106 -0
- package/src/sync/manager.ts +504 -0
- package/src/sync/mock-provider.ts +318 -0
- package/src/sync/oblivious.ts +625 -0
- package/src/tokens/index.ts +15 -0
- package/src/tokens/registry.ts +301 -0
- package/src/utils/deprecation.ts +94 -0
- package/src/utils/index.ts +9 -0
- package/src/wallet/ethereum/index.ts +68 -0
- package/src/wallet/ethereum/metamask-privacy.ts +420 -0
- package/src/wallet/ethereum/multi-wallet.ts +646 -0
- package/src/wallet/ethereum/privacy-adapter.ts +700 -0
- package/src/wallet/ethereum/types.ts +3 -1
- package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
- package/src/wallet/hardware/index.ts +10 -0
- package/src/wallet/hardware/ledger-privacy.ts +414 -0
- package/src/wallet/index.ts +71 -0
- package/src/wallet/near/adapter.ts +626 -0
- package/src/wallet/near/index.ts +86 -0
- package/src/wallet/near/meteor-wallet.ts +1153 -0
- package/src/wallet/near/my-near-wallet.ts +790 -0
- package/src/wallet/near/wallet-selector.ts +702 -0
- package/src/wallet/solana/adapter.ts +6 -4
- package/src/wallet/solana/index.ts +13 -0
- package/src/wallet/solana/privacy-adapter.ts +567 -0
- package/src/wallet/sui/types.ts +6 -4
- package/src/zcash/rpc-client.ts +13 -6
- package/dist/chunk-2XIVXWHA.mjs +0 -1930
- package/dist/chunk-3INS3PR5.mjs +0 -884
- package/dist/chunk-3OVABDRH.mjs +0 -17096
- package/dist/chunk-7RFRWDCW.mjs +0 -1504
- package/dist/chunk-DLDWZFYC.mjs +0 -1495
- package/dist/chunk-E6SZWREQ.mjs +0 -57
- package/dist/chunk-F6F73W35.mjs +0 -16166
- package/dist/chunk-G33LB27A.mjs +0 -16166
- package/dist/chunk-HGU6HZRC.mjs +0 -231
- package/dist/chunk-L2K34JCU.mjs +0 -1496
- package/dist/chunk-OFDBEIEK.mjs +0 -16166
- package/dist/chunk-SF7YSLF5.mjs +0 -1515
- package/dist/chunk-SN4ZDTVW.mjs +0 -16166
- package/dist/chunk-WWUSGOXE.mjs +0 -17129
- package/dist/constants-VOI7BSLK.mjs +0 -27
- package/dist/index-B71aXVzk.d.ts +0 -13264
- package/dist/index-BYZbDjal.d.ts +0 -11390
- package/dist/index-CHB3KuOB.d.mts +0 -11859
- package/dist/index-CzWPI6Le.d.ts +0 -11859
- package/dist/index-pOIIuwfV.d.mts +0 -13264
- package/dist/index-xbWjohNq.d.mts +0 -11390
- package/dist/solana-4O4K45VU.mjs +0 -46
- package/dist/solana-5EMCTPTS.mjs +0 -46
- package/dist/solana-NDABAZ6P.mjs +0 -56
- package/dist/solana-Q4NAVBTS.mjs +0 -46
- package/dist/solana-ZYO63LY5.mjs +0 -46
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ERC-4337 Relayer for EVM Gas Abstraction
|
|
3
|
+
*
|
|
4
|
+
* Enables users to submit shielded EVM transactions without paying gas directly,
|
|
5
|
+
* using ERC-4337 account abstraction with Paymasters.
|
|
6
|
+
*
|
|
7
|
+
* This breaks the link between the user's wallet and their privacy transaction,
|
|
8
|
+
* providing true sender privacy on EVM chains (Base, Arbitrum, Optimism, etc.).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { ERC4337Relayer, createPimlicoRelayer } from '@sip-protocol/sdk'
|
|
13
|
+
*
|
|
14
|
+
* // Create relayer with Pimlico bundler
|
|
15
|
+
* const relayer = createPimlicoRelayer({
|
|
16
|
+
* apiKey: process.env.PIMLICO_API_KEY!,
|
|
17
|
+
* chain: 'base',
|
|
18
|
+
* })
|
|
19
|
+
*
|
|
20
|
+
* // Relay a shielded transfer
|
|
21
|
+
* const result = await relayer.relayTransaction({
|
|
22
|
+
* to: stealthAddress,
|
|
23
|
+
* data: transferCalldata,
|
|
24
|
+
* value: 0n,
|
|
25
|
+
* signer,
|
|
26
|
+
* })
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @see https://eips.ethereum.org/EIPS/eip-4337
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Supported EVM chains for ERC-4337 relaying
|
|
34
|
+
*/
|
|
35
|
+
export type SupportedEVMChain =
|
|
36
|
+
| 'ethereum'
|
|
37
|
+
| 'base'
|
|
38
|
+
| 'arbitrum'
|
|
39
|
+
| 'optimism'
|
|
40
|
+
| 'polygon'
|
|
41
|
+
| 'sepolia'
|
|
42
|
+
| 'base-sepolia'
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Chain IDs for supported networks
|
|
46
|
+
*/
|
|
47
|
+
export const EVM_CHAIN_IDS: Record<SupportedEVMChain, number> = {
|
|
48
|
+
ethereum: 1,
|
|
49
|
+
base: 8453,
|
|
50
|
+
arbitrum: 42161,
|
|
51
|
+
optimism: 10,
|
|
52
|
+
polygon: 137,
|
|
53
|
+
sepolia: 11155111,
|
|
54
|
+
'base-sepolia': 84532,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Entry point addresses for ERC-4337 v0.7
|
|
59
|
+
*/
|
|
60
|
+
export const ENTRY_POINT_V07 = '0x0000000071727De22E5E9d8BAf0edAc6f37da032'
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Popular bundler endpoints
|
|
64
|
+
*/
|
|
65
|
+
export const BUNDLER_ENDPOINTS = {
|
|
66
|
+
pimlico: {
|
|
67
|
+
mainnet: 'https://api.pimlico.io/v2/{chain}/rpc',
|
|
68
|
+
testnet: 'https://api.pimlico.io/v2/{chain}/rpc',
|
|
69
|
+
},
|
|
70
|
+
stackup: {
|
|
71
|
+
mainnet: 'https://api.stackup.sh/v1/node/{apiKey}',
|
|
72
|
+
testnet: 'https://api.stackup.sh/v1/node/{apiKey}',
|
|
73
|
+
},
|
|
74
|
+
biconomy: {
|
|
75
|
+
mainnet: 'https://bundler.biconomy.io/api/v2/{chainId}/{apiKey}',
|
|
76
|
+
testnet: 'https://bundler.biconomy.io/api/v2/{chainId}/{apiKey}',
|
|
77
|
+
},
|
|
78
|
+
alchemy: {
|
|
79
|
+
mainnet: 'https://alchemy.com/account-abstraction/v3/{chain}/rpc',
|
|
80
|
+
testnet: 'https://alchemy.com/account-abstraction/v3/{chain}/rpc',
|
|
81
|
+
},
|
|
82
|
+
} as const
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* ERC-4337 Relayer configuration
|
|
86
|
+
*/
|
|
87
|
+
export interface ERC4337RelayerConfig {
|
|
88
|
+
/** Bundler RPC endpoint (full URL or provider name) */
|
|
89
|
+
bundlerUrl?: string
|
|
90
|
+
/** Bundler provider (pimlico, stackup, biconomy, alchemy) */
|
|
91
|
+
bundlerProvider?: 'pimlico' | 'stackup' | 'biconomy' | 'alchemy'
|
|
92
|
+
/** API key for bundler */
|
|
93
|
+
apiKey: string
|
|
94
|
+
/** Target chain */
|
|
95
|
+
chain: SupportedEVMChain
|
|
96
|
+
/** Paymaster URL (for gas sponsorship) */
|
|
97
|
+
paymasterUrl?: string
|
|
98
|
+
/** Entry point version (default: v0.7) */
|
|
99
|
+
entryPointVersion?: 'v0.6' | 'v0.7'
|
|
100
|
+
/** Maximum gas price multiplier (default: 1.2) */
|
|
101
|
+
gasPriceMultiplier?: number
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* User Operation structure (ERC-4337 v0.7)
|
|
106
|
+
*/
|
|
107
|
+
export interface UserOperation {
|
|
108
|
+
sender: string
|
|
109
|
+
nonce: bigint
|
|
110
|
+
factory?: string
|
|
111
|
+
factoryData?: string
|
|
112
|
+
callData: string
|
|
113
|
+
callGasLimit: bigint
|
|
114
|
+
verificationGasLimit: bigint
|
|
115
|
+
preVerificationGas: bigint
|
|
116
|
+
maxFeePerGas: bigint
|
|
117
|
+
maxPriorityFeePerGas: bigint
|
|
118
|
+
paymaster?: string
|
|
119
|
+
paymasterVerificationGasLimit?: bigint
|
|
120
|
+
paymasterPostOpGasLimit?: bigint
|
|
121
|
+
paymasterData?: string
|
|
122
|
+
signature: string
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Request to relay a transaction
|
|
127
|
+
*/
|
|
128
|
+
export interface RelayTransactionRequest {
|
|
129
|
+
/** Target contract address */
|
|
130
|
+
to: string
|
|
131
|
+
/** Call data */
|
|
132
|
+
data: string
|
|
133
|
+
/** Value to send (in wei) */
|
|
134
|
+
value?: bigint
|
|
135
|
+
/** Signer for the UserOperation */
|
|
136
|
+
signer: {
|
|
137
|
+
address: string
|
|
138
|
+
signMessage: (message: Uint8Array | string) => Promise<string>
|
|
139
|
+
}
|
|
140
|
+
/** Maximum fee willing to pay (optional, for fee limits) */
|
|
141
|
+
maxFee?: bigint
|
|
142
|
+
/** Wait for transaction confirmation */
|
|
143
|
+
waitForConfirmation?: boolean
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Result of relaying a transaction
|
|
148
|
+
*/
|
|
149
|
+
export interface RelayTransactionResult {
|
|
150
|
+
/** Whether the relay was successful */
|
|
151
|
+
success: boolean
|
|
152
|
+
/** User operation hash */
|
|
153
|
+
userOpHash?: string
|
|
154
|
+
/** Transaction hash (if confirmed) */
|
|
155
|
+
transactionHash?: string
|
|
156
|
+
/** Gas used */
|
|
157
|
+
gasUsed?: bigint
|
|
158
|
+
/** Actual fee paid */
|
|
159
|
+
actualFee?: bigint
|
|
160
|
+
/** Error if failed */
|
|
161
|
+
error?: Error
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Bundler RPC response types
|
|
166
|
+
*/
|
|
167
|
+
interface BundlerEstimateResponse {
|
|
168
|
+
preVerificationGas: string
|
|
169
|
+
verificationGasLimit: string
|
|
170
|
+
callGasLimit: string
|
|
171
|
+
paymasterVerificationGasLimit?: string
|
|
172
|
+
paymasterPostOpGasLimit?: string
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
interface BundlerSendResponse {
|
|
176
|
+
result: string // userOpHash
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
interface BundlerReceiptResponse {
|
|
180
|
+
result: {
|
|
181
|
+
userOpHash: string
|
|
182
|
+
entryPoint: string
|
|
183
|
+
sender: string
|
|
184
|
+
nonce: string
|
|
185
|
+
success: boolean
|
|
186
|
+
actualGasUsed: string
|
|
187
|
+
actualGasCost: string
|
|
188
|
+
receipt: {
|
|
189
|
+
transactionHash: string
|
|
190
|
+
blockNumber: string
|
|
191
|
+
gasUsed: string
|
|
192
|
+
}
|
|
193
|
+
} | null
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Error codes for ERC-4337 Relayer
|
|
198
|
+
*/
|
|
199
|
+
export enum ERC4337RelayerErrorCode {
|
|
200
|
+
BUNDLER_ERROR = 'BUNDLER_ERROR',
|
|
201
|
+
PAYMASTER_ERROR = 'PAYMASTER_ERROR',
|
|
202
|
+
SIGNATURE_ERROR = 'SIGNATURE_ERROR',
|
|
203
|
+
GAS_ESTIMATION_ERROR = 'GAS_ESTIMATION_ERROR',
|
|
204
|
+
TIMEOUT = 'TIMEOUT',
|
|
205
|
+
INVALID_CHAIN = 'INVALID_CHAIN',
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* ERC-4337 Relayer error
|
|
210
|
+
*/
|
|
211
|
+
export class ERC4337RelayerError extends Error {
|
|
212
|
+
code: ERC4337RelayerErrorCode
|
|
213
|
+
details?: unknown
|
|
214
|
+
|
|
215
|
+
constructor(message: string, code: ERC4337RelayerErrorCode, details?: unknown) {
|
|
216
|
+
super(message)
|
|
217
|
+
this.name = 'ERC4337RelayerError'
|
|
218
|
+
this.code = code
|
|
219
|
+
this.details = details
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* ERC-4337 Relayer for EVM gas abstraction
|
|
225
|
+
*
|
|
226
|
+
* Submits transactions via ERC-4337 bundlers with paymaster sponsorship.
|
|
227
|
+
*/
|
|
228
|
+
export class ERC4337Relayer {
|
|
229
|
+
private readonly bundlerUrl: string
|
|
230
|
+
private readonly paymasterUrl: string | null
|
|
231
|
+
private readonly chainId: number
|
|
232
|
+
private readonly entryPoint: string
|
|
233
|
+
private readonly gasPriceMultiplier: number
|
|
234
|
+
|
|
235
|
+
constructor(config: ERC4337RelayerConfig) {
|
|
236
|
+
// Validate chain
|
|
237
|
+
if (!EVM_CHAIN_IDS[config.chain]) {
|
|
238
|
+
throw new ERC4337RelayerError(
|
|
239
|
+
`Unsupported chain: ${config.chain}`,
|
|
240
|
+
ERC4337RelayerErrorCode.INVALID_CHAIN
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
this.chainId = EVM_CHAIN_IDS[config.chain]
|
|
245
|
+
this.gasPriceMultiplier = config.gasPriceMultiplier ?? 1.2
|
|
246
|
+
|
|
247
|
+
// Set entry point
|
|
248
|
+
this.entryPoint = ENTRY_POINT_V07
|
|
249
|
+
|
|
250
|
+
// Build bundler URL
|
|
251
|
+
if (config.bundlerUrl) {
|
|
252
|
+
this.bundlerUrl = config.bundlerUrl
|
|
253
|
+
} else if (config.bundlerProvider) {
|
|
254
|
+
const endpoints = BUNDLER_ENDPOINTS[config.bundlerProvider]
|
|
255
|
+
const isTestnet = config.chain.includes('sepolia')
|
|
256
|
+
const template = isTestnet ? endpoints.testnet : endpoints.mainnet
|
|
257
|
+
|
|
258
|
+
this.bundlerUrl = template
|
|
259
|
+
.replace('{chain}', config.chain)
|
|
260
|
+
.replace('{chainId}', this.chainId.toString())
|
|
261
|
+
.replace('{apiKey}', config.apiKey)
|
|
262
|
+
|
|
263
|
+
// Add API key as query param for Pimlico
|
|
264
|
+
if (config.bundlerProvider === 'pimlico') {
|
|
265
|
+
this.bundlerUrl += `?apikey=${config.apiKey}`
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
throw new ERC4337RelayerError(
|
|
269
|
+
'Either bundlerUrl or bundlerProvider must be specified',
|
|
270
|
+
ERC4337RelayerErrorCode.BUNDLER_ERROR
|
|
271
|
+
)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Set paymaster URL (defaults to bundler URL for integrated services)
|
|
275
|
+
this.paymasterUrl = config.paymasterUrl ?? this.bundlerUrl
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Relay a transaction via ERC-4337
|
|
280
|
+
*
|
|
281
|
+
* @param request - Transaction to relay
|
|
282
|
+
* @returns Relay result
|
|
283
|
+
*/
|
|
284
|
+
async relayTransaction(request: RelayTransactionRequest): Promise<RelayTransactionResult> {
|
|
285
|
+
try {
|
|
286
|
+
// 1. Build the UserOperation
|
|
287
|
+
const userOp = await this.buildUserOperation(request)
|
|
288
|
+
|
|
289
|
+
// 2. Sign the UserOperation
|
|
290
|
+
const signedUserOp = await this.signUserOperation(userOp, request.signer)
|
|
291
|
+
|
|
292
|
+
// 3. Submit to bundler
|
|
293
|
+
const userOpHash = await this.submitUserOperation(signedUserOp)
|
|
294
|
+
|
|
295
|
+
// 4. Wait for confirmation if requested
|
|
296
|
+
if (request.waitForConfirmation) {
|
|
297
|
+
const receipt = await this.waitForReceipt(userOpHash)
|
|
298
|
+
|
|
299
|
+
if (!receipt) {
|
|
300
|
+
return {
|
|
301
|
+
success: false,
|
|
302
|
+
userOpHash,
|
|
303
|
+
error: new ERC4337RelayerError(
|
|
304
|
+
'Transaction timed out waiting for confirmation',
|
|
305
|
+
ERC4337RelayerErrorCode.TIMEOUT
|
|
306
|
+
),
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
success: receipt.success,
|
|
312
|
+
userOpHash,
|
|
313
|
+
transactionHash: receipt.receipt.transactionHash,
|
|
314
|
+
gasUsed: BigInt(receipt.actualGasUsed),
|
|
315
|
+
actualFee: BigInt(receipt.actualGasCost),
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
success: true,
|
|
321
|
+
userOpHash,
|
|
322
|
+
}
|
|
323
|
+
} catch (error) {
|
|
324
|
+
if (error instanceof ERC4337RelayerError) {
|
|
325
|
+
return { success: false, error }
|
|
326
|
+
}
|
|
327
|
+
return {
|
|
328
|
+
success: false,
|
|
329
|
+
error: new ERC4337RelayerError(
|
|
330
|
+
error instanceof Error ? error.message : 'Unknown error',
|
|
331
|
+
ERC4337RelayerErrorCode.BUNDLER_ERROR,
|
|
332
|
+
error
|
|
333
|
+
),
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Build a UserOperation for the transaction
|
|
340
|
+
*/
|
|
341
|
+
private async buildUserOperation(request: RelayTransactionRequest): Promise<UserOperation> {
|
|
342
|
+
// Get gas prices from bundler
|
|
343
|
+
const gasPrices = await this.getGasPrices()
|
|
344
|
+
|
|
345
|
+
// Build call data for the target call
|
|
346
|
+
const callData = this.encodeExecuteCall(request.to, request.value ?? 0n, request.data)
|
|
347
|
+
|
|
348
|
+
// Get nonce from entry point (simplified - in production, query contract)
|
|
349
|
+
const nonce = await this.getNonce(request.signer.address)
|
|
350
|
+
|
|
351
|
+
// Estimate gas limits
|
|
352
|
+
const gasEstimate = await this.estimateGas({
|
|
353
|
+
sender: request.signer.address,
|
|
354
|
+
nonce,
|
|
355
|
+
callData,
|
|
356
|
+
maxFeePerGas: gasPrices.maxFeePerGas,
|
|
357
|
+
maxPriorityFeePerGas: gasPrices.maxPriorityFeePerGas,
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
// Get paymaster data if sponsoring
|
|
361
|
+
const paymasterData = await this.getPaymasterData({
|
|
362
|
+
sender: request.signer.address,
|
|
363
|
+
nonce,
|
|
364
|
+
callData,
|
|
365
|
+
...gasEstimate,
|
|
366
|
+
maxFeePerGas: gasPrices.maxFeePerGas,
|
|
367
|
+
maxPriorityFeePerGas: gasPrices.maxPriorityFeePerGas,
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
sender: request.signer.address,
|
|
372
|
+
nonce,
|
|
373
|
+
callData,
|
|
374
|
+
callGasLimit: gasEstimate.callGasLimit,
|
|
375
|
+
verificationGasLimit: gasEstimate.verificationGasLimit,
|
|
376
|
+
preVerificationGas: gasEstimate.preVerificationGas,
|
|
377
|
+
maxFeePerGas: gasPrices.maxFeePerGas,
|
|
378
|
+
maxPriorityFeePerGas: gasPrices.maxPriorityFeePerGas,
|
|
379
|
+
paymaster: paymasterData.paymaster,
|
|
380
|
+
paymasterVerificationGasLimit: paymasterData.paymasterVerificationGasLimit,
|
|
381
|
+
paymasterPostOpGasLimit: paymasterData.paymasterPostOpGasLimit,
|
|
382
|
+
paymasterData: paymasterData.paymasterData,
|
|
383
|
+
signature: '0x', // Will be filled in during signing
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Sign a UserOperation
|
|
389
|
+
*/
|
|
390
|
+
private async signUserOperation(
|
|
391
|
+
userOp: UserOperation,
|
|
392
|
+
signer: RelayTransactionRequest['signer']
|
|
393
|
+
): Promise<UserOperation> {
|
|
394
|
+
// Compute the UserOperation hash
|
|
395
|
+
const userOpHash = this.getUserOpHash(userOp)
|
|
396
|
+
|
|
397
|
+
// Sign the hash
|
|
398
|
+
const signature = await signer.signMessage(userOpHash)
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
...userOp,
|
|
402
|
+
signature,
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Submit UserOperation to bundler
|
|
408
|
+
*/
|
|
409
|
+
private async submitUserOperation(userOp: UserOperation): Promise<string> {
|
|
410
|
+
const response = await fetch(this.bundlerUrl, {
|
|
411
|
+
method: 'POST',
|
|
412
|
+
headers: {
|
|
413
|
+
'Content-Type': 'application/json',
|
|
414
|
+
},
|
|
415
|
+
body: JSON.stringify({
|
|
416
|
+
jsonrpc: '2.0',
|
|
417
|
+
id: 1,
|
|
418
|
+
method: 'eth_sendUserOperation',
|
|
419
|
+
params: [this.serializeUserOp(userOp), this.entryPoint],
|
|
420
|
+
}),
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
if (!response.ok) {
|
|
424
|
+
throw new ERC4337RelayerError(
|
|
425
|
+
`Bundler request failed: ${response.status}`,
|
|
426
|
+
ERC4337RelayerErrorCode.BUNDLER_ERROR
|
|
427
|
+
)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const data = (await response.json()) as BundlerSendResponse | { error: { message: string } }
|
|
431
|
+
|
|
432
|
+
if ('error' in data) {
|
|
433
|
+
throw new ERC4337RelayerError(
|
|
434
|
+
data.error.message,
|
|
435
|
+
ERC4337RelayerErrorCode.BUNDLER_ERROR
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return data.result
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Wait for UserOperation receipt
|
|
444
|
+
*/
|
|
445
|
+
private async waitForReceipt(
|
|
446
|
+
userOpHash: string,
|
|
447
|
+
timeoutMs: number = 60000
|
|
448
|
+
): Promise<BundlerReceiptResponse['result']> {
|
|
449
|
+
const startTime = Date.now()
|
|
450
|
+
const pollInterval = 2000
|
|
451
|
+
|
|
452
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
453
|
+
const response = await fetch(this.bundlerUrl, {
|
|
454
|
+
method: 'POST',
|
|
455
|
+
headers: {
|
|
456
|
+
'Content-Type': 'application/json',
|
|
457
|
+
},
|
|
458
|
+
body: JSON.stringify({
|
|
459
|
+
jsonrpc: '2.0',
|
|
460
|
+
id: 1,
|
|
461
|
+
method: 'eth_getUserOperationReceipt',
|
|
462
|
+
params: [userOpHash],
|
|
463
|
+
}),
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
if (response.ok) {
|
|
467
|
+
const data = (await response.json()) as BundlerReceiptResponse
|
|
468
|
+
if (data.result) {
|
|
469
|
+
return data.result
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval))
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return null
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Get current gas prices from bundler
|
|
481
|
+
*/
|
|
482
|
+
private async getGasPrices(): Promise<{
|
|
483
|
+
maxFeePerGas: bigint
|
|
484
|
+
maxPriorityFeePerGas: bigint
|
|
485
|
+
}> {
|
|
486
|
+
const response = await fetch(this.bundlerUrl, {
|
|
487
|
+
method: 'POST',
|
|
488
|
+
headers: { 'Content-Type': 'application/json' },
|
|
489
|
+
body: JSON.stringify({
|
|
490
|
+
jsonrpc: '2.0',
|
|
491
|
+
id: 1,
|
|
492
|
+
method: 'pimlico_getUserOperationGasPrice',
|
|
493
|
+
params: [],
|
|
494
|
+
}),
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
if (response.ok) {
|
|
498
|
+
const data = await response.json()
|
|
499
|
+
if (data.result?.standard) {
|
|
500
|
+
return {
|
|
501
|
+
maxFeePerGas: BigInt(data.result.standard.maxFeePerGas),
|
|
502
|
+
maxPriorityFeePerGas: BigInt(data.result.standard.maxPriorityFeePerGas),
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Fallback: use reasonable defaults
|
|
508
|
+
return {
|
|
509
|
+
maxFeePerGas: BigInt(30e9), // 30 gwei
|
|
510
|
+
maxPriorityFeePerGas: BigInt(1e9), // 1 gwei
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Estimate gas for UserOperation
|
|
516
|
+
*/
|
|
517
|
+
private async estimateGas(partialUserOp: Partial<UserOperation>): Promise<{
|
|
518
|
+
preVerificationGas: bigint
|
|
519
|
+
verificationGasLimit: bigint
|
|
520
|
+
callGasLimit: bigint
|
|
521
|
+
}> {
|
|
522
|
+
const response = await fetch(this.bundlerUrl, {
|
|
523
|
+
method: 'POST',
|
|
524
|
+
headers: { 'Content-Type': 'application/json' },
|
|
525
|
+
body: JSON.stringify({
|
|
526
|
+
jsonrpc: '2.0',
|
|
527
|
+
id: 1,
|
|
528
|
+
method: 'eth_estimateUserOperationGas',
|
|
529
|
+
params: [
|
|
530
|
+
{
|
|
531
|
+
sender: partialUserOp.sender,
|
|
532
|
+
nonce: `0x${(partialUserOp.nonce ?? 0n).toString(16)}`,
|
|
533
|
+
callData: partialUserOp.callData,
|
|
534
|
+
signature: '0x' + 'ff'.repeat(65), // Dummy signature for estimation
|
|
535
|
+
},
|
|
536
|
+
this.entryPoint,
|
|
537
|
+
],
|
|
538
|
+
}),
|
|
539
|
+
})
|
|
540
|
+
|
|
541
|
+
if (!response.ok) {
|
|
542
|
+
throw new ERC4337RelayerError(
|
|
543
|
+
'Gas estimation failed',
|
|
544
|
+
ERC4337RelayerErrorCode.GAS_ESTIMATION_ERROR
|
|
545
|
+
)
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const data = await response.json() as { result: BundlerEstimateResponse } | { error: { message: string } }
|
|
549
|
+
|
|
550
|
+
if ('error' in data) {
|
|
551
|
+
throw new ERC4337RelayerError(
|
|
552
|
+
data.error.message,
|
|
553
|
+
ERC4337RelayerErrorCode.GAS_ESTIMATION_ERROR
|
|
554
|
+
)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Apply gas multiplier for safety margin
|
|
558
|
+
const multiplier = this.gasPriceMultiplier
|
|
559
|
+
|
|
560
|
+
return {
|
|
561
|
+
preVerificationGas: BigInt(Math.ceil(Number(BigInt(data.result.preVerificationGas)) * multiplier)),
|
|
562
|
+
verificationGasLimit: BigInt(Math.ceil(Number(BigInt(data.result.verificationGasLimit)) * multiplier)),
|
|
563
|
+
callGasLimit: BigInt(Math.ceil(Number(BigInt(data.result.callGasLimit)) * multiplier)),
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Get paymaster data for sponsorship
|
|
569
|
+
*/
|
|
570
|
+
private async getPaymasterData(partialUserOp: Partial<UserOperation>): Promise<{
|
|
571
|
+
paymaster?: string
|
|
572
|
+
paymasterVerificationGasLimit?: bigint
|
|
573
|
+
paymasterPostOpGasLimit?: bigint
|
|
574
|
+
paymasterData?: string
|
|
575
|
+
}> {
|
|
576
|
+
if (!this.paymasterUrl) {
|
|
577
|
+
return {}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const response = await fetch(this.paymasterUrl, {
|
|
581
|
+
method: 'POST',
|
|
582
|
+
headers: { 'Content-Type': 'application/json' },
|
|
583
|
+
body: JSON.stringify({
|
|
584
|
+
jsonrpc: '2.0',
|
|
585
|
+
id: 1,
|
|
586
|
+
method: 'pm_sponsorUserOperation',
|
|
587
|
+
params: [
|
|
588
|
+
{
|
|
589
|
+
sender: partialUserOp.sender,
|
|
590
|
+
nonce: `0x${(partialUserOp.nonce ?? 0n).toString(16)}`,
|
|
591
|
+
callData: partialUserOp.callData,
|
|
592
|
+
callGasLimit: `0x${(partialUserOp.callGasLimit ?? 0n).toString(16)}`,
|
|
593
|
+
verificationGasLimit: `0x${(partialUserOp.verificationGasLimit ?? 0n).toString(16)}`,
|
|
594
|
+
preVerificationGas: `0x${(partialUserOp.preVerificationGas ?? 0n).toString(16)}`,
|
|
595
|
+
maxFeePerGas: `0x${(partialUserOp.maxFeePerGas ?? 0n).toString(16)}`,
|
|
596
|
+
maxPriorityFeePerGas: `0x${(partialUserOp.maxPriorityFeePerGas ?? 0n).toString(16)}`,
|
|
597
|
+
signature: '0x' + 'ff'.repeat(65),
|
|
598
|
+
},
|
|
599
|
+
this.entryPoint,
|
|
600
|
+
{ sponsorshipPolicyId: 'sp_sip_privacy' }, // Sponsorship policy
|
|
601
|
+
],
|
|
602
|
+
}),
|
|
603
|
+
})
|
|
604
|
+
|
|
605
|
+
if (!response.ok) {
|
|
606
|
+
// Paymaster not available, user will pay gas
|
|
607
|
+
return {}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const data = await response.json()
|
|
611
|
+
|
|
612
|
+
if (data.result) {
|
|
613
|
+
return {
|
|
614
|
+
paymaster: data.result.paymaster,
|
|
615
|
+
paymasterVerificationGasLimit: data.result.paymasterVerificationGasLimit
|
|
616
|
+
? BigInt(data.result.paymasterVerificationGasLimit)
|
|
617
|
+
: undefined,
|
|
618
|
+
paymasterPostOpGasLimit: data.result.paymasterPostOpGasLimit
|
|
619
|
+
? BigInt(data.result.paymasterPostOpGasLimit)
|
|
620
|
+
: undefined,
|
|
621
|
+
paymasterData: data.result.paymasterData,
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return {}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Get nonce for sender
|
|
630
|
+
*/
|
|
631
|
+
private async getNonce(sender: string): Promise<bigint> {
|
|
632
|
+
// In production, this would query the EntryPoint contract
|
|
633
|
+
// For now, return 0 (first operation) or query bundler
|
|
634
|
+
const response = await fetch(this.bundlerUrl, {
|
|
635
|
+
method: 'POST',
|
|
636
|
+
headers: { 'Content-Type': 'application/json' },
|
|
637
|
+
body: JSON.stringify({
|
|
638
|
+
jsonrpc: '2.0',
|
|
639
|
+
id: 1,
|
|
640
|
+
method: 'eth_call',
|
|
641
|
+
params: [
|
|
642
|
+
{
|
|
643
|
+
to: this.entryPoint,
|
|
644
|
+
data: `0x35567e1a${sender.slice(2).padStart(64, '0')}${'0'.repeat(64)}`, // getNonce(address,uint192)
|
|
645
|
+
},
|
|
646
|
+
'latest',
|
|
647
|
+
],
|
|
648
|
+
}),
|
|
649
|
+
})
|
|
650
|
+
|
|
651
|
+
if (response.ok) {
|
|
652
|
+
const data = await response.json()
|
|
653
|
+
if (data.result && data.result !== '0x') {
|
|
654
|
+
return BigInt(data.result)
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
return 0n
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Encode execute call for smart account
|
|
663
|
+
*/
|
|
664
|
+
private encodeExecuteCall(to: string, value: bigint, data: string): string {
|
|
665
|
+
// Standard execute function signature
|
|
666
|
+
// execute(address dest, uint256 value, bytes calldata func)
|
|
667
|
+
const selector = '0xb61d27f6'
|
|
668
|
+
const encodedTo = to.slice(2).padStart(64, '0')
|
|
669
|
+
const encodedValue = value.toString(16).padStart(64, '0')
|
|
670
|
+
const dataOffset = (32 * 3).toString(16).padStart(64, '0')
|
|
671
|
+
const dataLength = ((data.length - 2) / 2).toString(16).padStart(64, '0')
|
|
672
|
+
const dataContent = data.slice(2).padEnd(Math.ceil((data.length - 2) / 64) * 64, '0')
|
|
673
|
+
|
|
674
|
+
return `${selector}${encodedTo}${encodedValue}${dataOffset}${dataLength}${dataContent}`
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Compute UserOperation hash
|
|
679
|
+
*/
|
|
680
|
+
private getUserOpHash(userOp: UserOperation): string {
|
|
681
|
+
// Simplified hash computation
|
|
682
|
+
// In production, use proper EIP-712 typed data hashing
|
|
683
|
+
const packed = this.packUserOp(userOp)
|
|
684
|
+
// For now, return a placeholder - real implementation would use keccak256
|
|
685
|
+
return packed
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Pack UserOperation for hashing
|
|
690
|
+
*/
|
|
691
|
+
private packUserOp(userOp: UserOperation): string {
|
|
692
|
+
return JSON.stringify({
|
|
693
|
+
sender: userOp.sender,
|
|
694
|
+
nonce: userOp.nonce.toString(),
|
|
695
|
+
callData: userOp.callData,
|
|
696
|
+
callGasLimit: userOp.callGasLimit.toString(),
|
|
697
|
+
verificationGasLimit: userOp.verificationGasLimit.toString(),
|
|
698
|
+
preVerificationGas: userOp.preVerificationGas.toString(),
|
|
699
|
+
maxFeePerGas: userOp.maxFeePerGas.toString(),
|
|
700
|
+
maxPriorityFeePerGas: userOp.maxPriorityFeePerGas.toString(),
|
|
701
|
+
paymaster: userOp.paymaster,
|
|
702
|
+
paymasterData: userOp.paymasterData,
|
|
703
|
+
chainId: this.chainId,
|
|
704
|
+
entryPoint: this.entryPoint,
|
|
705
|
+
})
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Serialize UserOperation for RPC
|
|
710
|
+
*/
|
|
711
|
+
private serializeUserOp(userOp: UserOperation): Record<string, string | undefined> {
|
|
712
|
+
return {
|
|
713
|
+
sender: userOp.sender,
|
|
714
|
+
nonce: `0x${userOp.nonce.toString(16)}`,
|
|
715
|
+
factory: userOp.factory,
|
|
716
|
+
factoryData: userOp.factoryData,
|
|
717
|
+
callData: userOp.callData,
|
|
718
|
+
callGasLimit: `0x${userOp.callGasLimit.toString(16)}`,
|
|
719
|
+
verificationGasLimit: `0x${userOp.verificationGasLimit.toString(16)}`,
|
|
720
|
+
preVerificationGas: `0x${userOp.preVerificationGas.toString(16)}`,
|
|
721
|
+
maxFeePerGas: `0x${userOp.maxFeePerGas.toString(16)}`,
|
|
722
|
+
maxPriorityFeePerGas: `0x${userOp.maxPriorityFeePerGas.toString(16)}`,
|
|
723
|
+
paymaster: userOp.paymaster,
|
|
724
|
+
paymasterVerificationGasLimit: userOp.paymasterVerificationGasLimit
|
|
725
|
+
? `0x${userOp.paymasterVerificationGasLimit.toString(16)}`
|
|
726
|
+
: undefined,
|
|
727
|
+
paymasterPostOpGasLimit: userOp.paymasterPostOpGasLimit
|
|
728
|
+
? `0x${userOp.paymasterPostOpGasLimit.toString(16)}`
|
|
729
|
+
: undefined,
|
|
730
|
+
paymasterData: userOp.paymasterData,
|
|
731
|
+
signature: userOp.signature,
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Check if relayer is available
|
|
737
|
+
*/
|
|
738
|
+
async isAvailable(): Promise<boolean> {
|
|
739
|
+
try {
|
|
740
|
+
const response = await fetch(this.bundlerUrl, {
|
|
741
|
+
method: 'POST',
|
|
742
|
+
headers: { 'Content-Type': 'application/json' },
|
|
743
|
+
body: JSON.stringify({
|
|
744
|
+
jsonrpc: '2.0',
|
|
745
|
+
id: 1,
|
|
746
|
+
method: 'eth_supportedEntryPoints',
|
|
747
|
+
params: [],
|
|
748
|
+
}),
|
|
749
|
+
})
|
|
750
|
+
|
|
751
|
+
if (!response.ok) return false
|
|
752
|
+
|
|
753
|
+
const data = await response.json()
|
|
754
|
+
return Array.isArray(data.result) && data.result.length > 0
|
|
755
|
+
} catch {
|
|
756
|
+
return false
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* Get supported entry points
|
|
762
|
+
*/
|
|
763
|
+
async getSupportedEntryPoints(): Promise<string[]> {
|
|
764
|
+
try {
|
|
765
|
+
const response = await fetch(this.bundlerUrl, {
|
|
766
|
+
method: 'POST',
|
|
767
|
+
headers: { 'Content-Type': 'application/json' },
|
|
768
|
+
body: JSON.stringify({
|
|
769
|
+
jsonrpc: '2.0',
|
|
770
|
+
id: 1,
|
|
771
|
+
method: 'eth_supportedEntryPoints',
|
|
772
|
+
params: [],
|
|
773
|
+
}),
|
|
774
|
+
})
|
|
775
|
+
|
|
776
|
+
if (response.ok) {
|
|
777
|
+
const data = await response.json()
|
|
778
|
+
return data.result ?? []
|
|
779
|
+
}
|
|
780
|
+
} catch {
|
|
781
|
+
// Ignore
|
|
782
|
+
}
|
|
783
|
+
return []
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Create a Pimlico-backed ERC-4337 relayer
|
|
789
|
+
*
|
|
790
|
+
* @param config - Relayer configuration
|
|
791
|
+
* @returns Configured relayer
|
|
792
|
+
*/
|
|
793
|
+
export function createPimlicoRelayer(
|
|
794
|
+
config: Omit<ERC4337RelayerConfig, 'bundlerProvider'>
|
|
795
|
+
): ERC4337Relayer {
|
|
796
|
+
return new ERC4337Relayer({
|
|
797
|
+
...config,
|
|
798
|
+
bundlerProvider: 'pimlico',
|
|
799
|
+
})
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Create a Stackup-backed ERC-4337 relayer
|
|
804
|
+
*
|
|
805
|
+
* @param config - Relayer configuration
|
|
806
|
+
* @returns Configured relayer
|
|
807
|
+
*/
|
|
808
|
+
export function createStackupRelayer(
|
|
809
|
+
config: Omit<ERC4337RelayerConfig, 'bundlerProvider'>
|
|
810
|
+
): ERC4337Relayer {
|
|
811
|
+
return new ERC4337Relayer({
|
|
812
|
+
...config,
|
|
813
|
+
bundlerProvider: 'stackup',
|
|
814
|
+
})
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Create a Biconomy-backed ERC-4337 relayer
|
|
819
|
+
*
|
|
820
|
+
* @param config - Relayer configuration
|
|
821
|
+
* @returns Configured relayer
|
|
822
|
+
*/
|
|
823
|
+
export function createBiconomyRelayer(
|
|
824
|
+
config: Omit<ERC4337RelayerConfig, 'bundlerProvider'>
|
|
825
|
+
): ERC4337Relayer {
|
|
826
|
+
return new ERC4337Relayer({
|
|
827
|
+
...config,
|
|
828
|
+
bundlerProvider: 'biconomy',
|
|
829
|
+
})
|
|
830
|
+
}
|