viem 2.57.4 → 2.57.6
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/CHANGELOG.md +34 -0
- package/_cjs/actions/public/waitForTransactionReceipt.js +5 -2
- package/_cjs/actions/public/waitForTransactionReceipt.js.map +1 -1
- package/_cjs/actions/wallet/waitForCallsStatus.js +25 -2
- package/_cjs/actions/wallet/waitForCallsStatus.js.map +1 -1
- package/_cjs/chains/definitions/arcTestnet.js +11 -8
- package/_cjs/chains/definitions/arcTestnet.js.map +1 -1
- package/_cjs/chains/definitions/qmsTestnet.js +30 -0
- package/_cjs/chains/definitions/qmsTestnet.js.map +1 -0
- package/_cjs/chains/definitions/tempo.js +1 -1
- package/_cjs/chains/definitions/tempoModerato.js +2 -2
- package/_cjs/chains/index.js +7 -5
- package/_cjs/chains/index.js.map +1 -1
- package/_cjs/clients/transports/ipc.js +19 -10
- package/_cjs/clients/transports/ipc.js.map +1 -1
- package/_cjs/clients/transports/webSocket.js +19 -10
- package/_cjs/clients/transports/webSocket.js.map +1 -1
- package/_cjs/errors/version.js +1 -1
- package/_cjs/tempo/Abis.js +297 -1
- package/_cjs/tempo/Abis.js.map +1 -1
- package/_cjs/tempo/Account.js +40 -40
- package/_cjs/tempo/Account.js.map +1 -1
- package/_cjs/tempo/Addresses.js +2 -1
- package/_cjs/tempo/Addresses.js.map +1 -1
- package/_cjs/tempo/Decorator.js +9 -8
- package/_cjs/tempo/Decorator.js.map +1 -1
- package/_cjs/tempo/ExecutionError.js +95 -4
- package/_cjs/tempo/ExecutionError.js.map +1 -1
- package/_cjs/tempo/Formatters.js +8 -7
- package/_cjs/tempo/Formatters.js.map +1 -1
- package/_cjs/tempo/KeyAuthorizationManager.js +25 -16
- package/_cjs/tempo/KeyAuthorizationManager.js.map +1 -1
- package/_cjs/tempo/Relay.js +9 -4
- package/_cjs/tempo/Relay.js.map +1 -1
- package/_cjs/tempo/Transaction.js +9 -9
- package/_cjs/tempo/Transaction.js.map +1 -1
- package/_cjs/tempo/Transport.js +21 -11
- package/_cjs/tempo/Transport.js.map +1 -1
- package/_cjs/tempo/{multisig → accounts}/Config.js +8 -8
- package/_cjs/tempo/{multisig → accounts}/Config.js.map +1 -1
- package/_cjs/tempo/{multisig → accounts}/Operation.js +10 -10
- package/_cjs/tempo/{multisig → accounts}/Operation.js.map +1 -1
- package/_cjs/tempo/{multisig → accounts}/Signature.js +2 -2
- package/_cjs/tempo/accounts/Signature.js.map +1 -0
- package/_cjs/tempo/actions/accessKey.js +442 -47
- package/_cjs/tempo/actions/accessKey.js.map +1 -1
- package/_cjs/tempo/actions/{multisig.js → accounts.js} +16 -20
- package/_cjs/tempo/actions/accounts.js.map +1 -0
- package/_cjs/tempo/actions/amm.js +90 -4
- package/_cjs/tempo/actions/amm.js.map +1 -1
- package/_cjs/tempo/actions/channel.js +183 -7
- package/_cjs/tempo/actions/channel.js.map +1 -1
- package/_cjs/tempo/actions/dex.js +507 -14
- package/_cjs/tempo/actions/dex.js.map +1 -1
- package/_cjs/tempo/actions/earn/deployment.js +4 -3
- package/_cjs/tempo/actions/earn/deployment.js.map +1 -1
- package/_cjs/tempo/actions/earn.js +22 -21
- package/_cjs/tempo/actions/earn.js.map +1 -1
- package/_cjs/tempo/actions/fee.js +85 -5
- package/_cjs/tempo/actions/fee.js.map +1 -1
- package/_cjs/tempo/actions/index.js +2 -2
- package/_cjs/tempo/actions/index.js.map +1 -1
- package/_cjs/tempo/actions/nonce.js +54 -1
- package/_cjs/tempo/actions/nonce.js.map +1 -1
- package/_cjs/tempo/actions/policy.js +35 -6
- package/_cjs/tempo/actions/policy.js.map +1 -1
- package/_cjs/tempo/actions/propAmm.js +18 -8
- package/_cjs/tempo/actions/propAmm.js.map +1 -1
- package/_cjs/tempo/actions/receivePolicy.js +7 -6
- package/_cjs/tempo/actions/receivePolicy.js.map +1 -1
- package/_cjs/tempo/actions/reward.js +7 -6
- package/_cjs/tempo/actions/reward.js.map +1 -1
- package/_cjs/tempo/actions/token.js +433 -22
- package/_cjs/tempo/actions/token.js.map +1 -1
- package/_cjs/tempo/actions/validator.js +12 -11
- package/_cjs/tempo/actions/validator.js.map +1 -1
- package/_cjs/tempo/actions/virtualAddress.js +3 -2
- package/_cjs/tempo/actions/virtualAddress.js.map +1 -1
- package/_cjs/tempo/actions/zone.js +12 -11
- package/_cjs/tempo/actions/zone.js.map +1 -1
- package/_cjs/tempo/chainConfig.js +37 -37
- package/_cjs/tempo/chainConfig.js.map +1 -1
- package/_cjs/tempo/errors.js +57 -1
- package/_cjs/tempo/errors.js.map +1 -1
- package/_cjs/tempo/index.js +3 -3
- package/_cjs/tempo/index.js.map +1 -1
- package/_cjs/tempo/internal/errors.js +46 -0
- package/_cjs/tempo/internal/errors.js.map +1 -0
- package/_cjs/tempo/internal/relay/{multisig.js → accounts.js} +90 -93
- package/_cjs/tempo/internal/relay/accounts.js.map +1 -0
- package/_cjs/tempo/internal/relay/keyAuthorization.js +296 -0
- package/_cjs/tempo/internal/relay/keyAuthorization.js.map +1 -0
- package/_cjs/tempo/internal/relay/plugin.js +17 -7
- package/_cjs/tempo/internal/relay/plugin.js.map +1 -1
- package/_cjs/tempo/internal/resolveError.js +128 -0
- package/_cjs/tempo/internal/resolveError.js.map +1 -0
- package/_cjs/tempo/internal/utils.js +1 -0
- package/_cjs/tempo/internal/utils.js.map +1 -1
- package/_cjs/utils/hash/normalizeSignature.js +1 -1
- package/_cjs/utils/hash/normalizeSignature.js.map +1 -1
- package/_cjs/utils/rpc/http.js +66 -31
- package/_cjs/utils/rpc/http.js.map +1 -1
- package/_cjs/utils/rpc/socket.js +1 -1
- package/_cjs/utils/rpc/socket.js.map +1 -1
- package/_esm/actions/public/waitForTransactionReceipt.js +5 -2
- package/_esm/actions/public/waitForTransactionReceipt.js.map +1 -1
- package/_esm/actions/wallet/waitForCallsStatus.js +39 -2
- package/_esm/actions/wallet/waitForCallsStatus.js.map +1 -1
- package/_esm/chains/definitions/arcTestnet.js +11 -8
- package/_esm/chains/definitions/arcTestnet.js.map +1 -1
- package/_esm/chains/definitions/qmsTestnet.js +27 -0
- package/_esm/chains/definitions/qmsTestnet.js.map +1 -0
- package/_esm/chains/definitions/tempo.js +1 -1
- package/_esm/chains/definitions/tempoModerato.js +2 -2
- package/_esm/chains/index.js +1 -0
- package/_esm/chains/index.js.map +1 -1
- package/_esm/clients/transports/ipc.js +21 -10
- package/_esm/clients/transports/ipc.js.map +1 -1
- package/_esm/clients/transports/webSocket.js +21 -10
- package/_esm/clients/transports/webSocket.js.map +1 -1
- package/_esm/errors/version.js +1 -1
- package/_esm/tempo/Abis.js +297 -0
- package/_esm/tempo/Abis.js.map +1 -1
- package/_esm/tempo/Account.js +42 -42
- package/_esm/tempo/Account.js.map +1 -1
- package/_esm/tempo/Addresses.js +1 -0
- package/_esm/tempo/Addresses.js.map +1 -1
- package/_esm/tempo/Client.js +3 -3
- package/_esm/tempo/Decorator.js +9 -8
- package/_esm/tempo/Decorator.js.map +1 -1
- package/_esm/tempo/ExecutionError.js +96 -3
- package/_esm/tempo/ExecutionError.js.map +1 -1
- package/_esm/tempo/Formatters.js +11 -10
- package/_esm/tempo/Formatters.js.map +1 -1
- package/_esm/tempo/KeyAuthorizationManager.js +58 -16
- package/_esm/tempo/KeyAuthorizationManager.js.map +1 -1
- package/_esm/tempo/Relay.js +49 -8
- package/_esm/tempo/Relay.js.map +1 -1
- package/_esm/tempo/Transaction.js +11 -11
- package/_esm/tempo/Transaction.js.map +1 -1
- package/_esm/tempo/Transport.js +22 -12
- package/_esm/tempo/Transport.js.map +1 -1
- package/_esm/tempo/{multisig → accounts}/Config.js +14 -14
- package/_esm/tempo/{multisig → accounts}/Config.js.map +1 -1
- package/_esm/tempo/{multisig → accounts}/Operation.js +17 -17
- package/_esm/tempo/{multisig → accounts}/Operation.js.map +1 -1
- package/_esm/tempo/accounts/Signature.js +9 -0
- package/_esm/tempo/accounts/Signature.js.map +1 -0
- package/_esm/tempo/actions/accessKey.js +499 -48
- package/_esm/tempo/actions/accessKey.js.map +1 -1
- package/_esm/tempo/actions/{multisig.js → accounts.js} +34 -38
- package/_esm/tempo/actions/accounts.js.map +1 -0
- package/_esm/tempo/actions/amm.js +95 -4
- package/_esm/tempo/actions/amm.js.map +1 -1
- package/_esm/tempo/actions/channel.js +191 -7
- package/_esm/tempo/actions/channel.js.map +1 -1
- package/_esm/tempo/actions/dex.js +537 -14
- package/_esm/tempo/actions/dex.js.map +1 -1
- package/_esm/tempo/actions/earn/deployment.js +4 -3
- package/_esm/tempo/actions/earn/deployment.js.map +1 -1
- package/_esm/tempo/actions/earn.js +22 -21
- package/_esm/tempo/actions/earn.js.map +1 -1
- package/_esm/tempo/actions/fee.js +90 -6
- package/_esm/tempo/actions/fee.js.map +1 -1
- package/_esm/tempo/actions/index.js +1 -1
- package/_esm/tempo/actions/index.js.map +1 -1
- package/_esm/tempo/actions/nonce.js +57 -1
- package/_esm/tempo/actions/nonce.js.map +1 -1
- package/_esm/tempo/actions/policy.js +36 -6
- package/_esm/tempo/actions/policy.js.map +1 -1
- package/_esm/tempo/actions/propAmm.js +18 -8
- package/_esm/tempo/actions/propAmm.js.map +1 -1
- package/_esm/tempo/actions/receivePolicy.js +7 -6
- package/_esm/tempo/actions/receivePolicy.js.map +1 -1
- package/_esm/tempo/actions/reward.js +7 -6
- package/_esm/tempo/actions/reward.js.map +1 -1
- package/_esm/tempo/actions/token.js +461 -22
- package/_esm/tempo/actions/token.js.map +1 -1
- package/_esm/tempo/actions/validator.js +12 -11
- package/_esm/tempo/actions/validator.js.map +1 -1
- package/_esm/tempo/actions/virtualAddress.js +3 -2
- package/_esm/tempo/actions/virtualAddress.js.map +1 -1
- package/_esm/tempo/actions/zone.js +12 -11
- package/_esm/tempo/actions/zone.js.map +1 -1
- package/_esm/tempo/chainConfig.js +38 -38
- package/_esm/tempo/chainConfig.js.map +1 -1
- package/_esm/tempo/errors.js +63 -0
- package/_esm/tempo/errors.js.map +1 -1
- package/_esm/tempo/index.js +2 -1
- package/_esm/tempo/index.js.map +1 -1
- package/_esm/tempo/internal/errors.js +51 -0
- package/_esm/tempo/internal/errors.js.map +1 -0
- package/_esm/tempo/internal/relay/{multisig.js → accounts.js} +93 -98
- package/_esm/tempo/internal/relay/accounts.js.map +1 -0
- package/_esm/tempo/internal/relay/feePayer.js +1 -1
- package/_esm/tempo/internal/relay/feePayer.js.map +1 -1
- package/_esm/tempo/internal/relay/keyAuthorization.js +334 -0
- package/_esm/tempo/internal/relay/keyAuthorization.js.map +1 -0
- package/_esm/tempo/internal/relay/plugin.js +15 -6
- package/_esm/tempo/internal/relay/plugin.js.map +1 -1
- package/_esm/tempo/internal/resolveError.js +130 -0
- package/_esm/tempo/internal/resolveError.js.map +1 -0
- package/_esm/tempo/internal/utils.js +2 -0
- package/_esm/tempo/internal/utils.js.map +1 -1
- package/_esm/utils/hash/normalizeSignature.js +3 -2
- package/_esm/utils/hash/normalizeSignature.js.map +1 -1
- package/_esm/utils/rpc/http.js +74 -31
- package/_esm/utils/rpc/http.js.map +1 -1
- package/_esm/utils/rpc/socket.js +3 -1
- package/_esm/utils/rpc/socket.js.map +1 -1
- package/_types/actions/public/waitForTransactionReceipt.d.ts.map +1 -1
- package/_types/actions/wallet/waitForCallsStatus.d.ts.map +1 -1
- package/_types/chains/definitions/arcTestnet.d.ts +5 -5
- package/_types/chains/definitions/arcTestnet.d.ts.map +1 -1
- package/_types/chains/definitions/qmsTestnet.d.ts +50 -0
- package/_types/chains/definitions/qmsTestnet.d.ts.map +1 -0
- package/_types/chains/definitions/tempo.d.ts +2 -2
- package/_types/chains/definitions/tempoModerato.d.ts +4 -4
- package/_types/chains/index.d.ts +1 -0
- package/_types/chains/index.d.ts.map +1 -1
- package/_types/clients/transports/ipc.d.ts.map +1 -1
- package/_types/clients/transports/webSocket.d.ts.map +1 -1
- package/_types/errors/version.d.ts +1 -1
- package/_types/tempo/Abis.d.ts +807 -0
- package/_types/tempo/Abis.d.ts.map +1 -1
- package/_types/tempo/Account.d.ts +28 -28
- package/_types/tempo/Account.d.ts.map +1 -1
- package/_types/tempo/Addresses.d.ts +1 -0
- package/_types/tempo/Addresses.d.ts.map +1 -1
- package/_types/tempo/Client.d.ts +3 -3
- package/_types/tempo/Decorator.d.ts +64 -38
- package/_types/tempo/Decorator.d.ts.map +1 -1
- package/_types/tempo/ExecutionError.d.ts +3 -1
- package/_types/tempo/ExecutionError.d.ts.map +1 -1
- package/_types/tempo/Formatters.d.ts.map +1 -1
- package/_types/tempo/KeyAuthorizationManager.d.ts +45 -2
- package/_types/tempo/KeyAuthorizationManager.d.ts.map +1 -1
- package/_types/tempo/Relay.d.ts +62 -12
- package/_types/tempo/Relay.d.ts.map +1 -1
- package/_types/tempo/Transaction.d.ts +14 -11
- package/_types/tempo/Transaction.d.ts.map +1 -1
- package/_types/tempo/Transport.d.ts +20 -7
- package/_types/tempo/Transport.d.ts.map +1 -1
- package/_types/tempo/{multisig → accounts}/Config.d.ts +7 -7
- package/_types/tempo/accounts/Config.d.ts.map +1 -0
- package/_types/tempo/{multisig → accounts}/Operation.d.ts +7 -7
- package/_types/tempo/accounts/Operation.d.ts.map +1 -0
- package/_types/tempo/{multisig → accounts}/Signature.d.ts +1 -1
- package/_types/tempo/accounts/Signature.d.ts.map +1 -0
- package/_types/tempo/actions/accessKey.d.ts +214 -25
- package/_types/tempo/actions/accessKey.d.ts.map +1 -1
- package/_types/tempo/actions/{multisig.d.ts → accounts.d.ts} +31 -29
- package/_types/tempo/actions/accounts.d.ts.map +1 -0
- package/_types/tempo/actions/amm.d.ts +39 -0
- package/_types/tempo/actions/amm.d.ts.map +1 -1
- package/_types/tempo/actions/channel.d.ts +77 -7
- package/_types/tempo/actions/channel.d.ts.map +1 -1
- package/_types/tempo/actions/dex.d.ts +222 -16
- package/_types/tempo/actions/dex.d.ts.map +1 -1
- package/_types/tempo/actions/earn/deployment.d.ts +7 -6
- package/_types/tempo/actions/earn/deployment.d.ts.map +1 -1
- package/_types/tempo/actions/earn.d.ts +15 -14
- package/_types/tempo/actions/earn.d.ts.map +1 -1
- package/_types/tempo/actions/fee.d.ts +32 -6
- package/_types/tempo/actions/fee.d.ts.map +1 -1
- package/_types/tempo/actions/index.d.ts +1 -1
- package/_types/tempo/actions/index.d.ts.map +1 -1
- package/_types/tempo/actions/nonce.d.ts +35 -0
- package/_types/tempo/actions/nonce.d.ts.map +1 -1
- package/_types/tempo/actions/policy.d.ts +29 -8
- package/_types/tempo/actions/policy.d.ts.map +1 -1
- package/_types/tempo/actions/propAmm.d.ts +9 -1
- package/_types/tempo/actions/propAmm.d.ts.map +1 -1
- package/_types/tempo/actions/receivePolicy.d.ts +10 -6
- package/_types/tempo/actions/receivePolicy.d.ts.map +1 -1
- package/_types/tempo/actions/reward.d.ts +7 -3
- package/_types/tempo/actions/reward.d.ts.map +1 -1
- package/_types/tempo/actions/token.d.ts +200 -32
- package/_types/tempo/actions/token.d.ts.map +1 -1
- package/_types/tempo/actions/validator.d.ts +17 -5
- package/_types/tempo/actions/validator.d.ts.map +1 -1
- package/_types/tempo/actions/virtualAddress.d.ts +4 -3
- package/_types/tempo/actions/virtualAddress.d.ts.map +1 -1
- package/_types/tempo/actions/zone.d.ts +10 -9
- package/_types/tempo/actions/zone.d.ts.map +1 -1
- package/_types/tempo/errors.d.ts +63 -1
- package/_types/tempo/errors.d.ts.map +1 -1
- package/_types/tempo/index.d.ts +3 -2
- package/_types/tempo/index.d.ts.map +1 -1
- package/_types/tempo/internal/errors.d.ts +59 -0
- package/_types/tempo/internal/errors.d.ts.map +1 -0
- package/_types/tempo/internal/relay/accounts.d.ts +15 -0
- package/_types/tempo/internal/relay/accounts.d.ts.map +1 -0
- package/_types/tempo/internal/relay/keyAuthorization.d.ts +25 -0
- package/_types/tempo/internal/relay/keyAuthorization.d.ts.map +1 -0
- package/_types/tempo/internal/relay/plugin.d.ts +6 -3
- package/_types/tempo/internal/relay/plugin.d.ts.map +1 -1
- package/_types/tempo/internal/resolveError.d.ts +8 -0
- package/_types/tempo/internal/resolveError.d.ts.map +1 -0
- package/_types/tempo/internal/utils.d.ts.map +1 -1
- package/_types/utils/hash/normalizeSignature.d.ts.map +1 -1
- package/_types/utils/rpc/http.d.ts.map +1 -1
- package/_types/utils/rpc/socket.d.ts.map +1 -1
- package/actions/public/waitForTransactionReceipt.ts +8 -2
- package/actions/wallet/waitForCallsStatus.ts +41 -2
- package/chains/definitions/arcTestnet.ts +11 -8
- package/chains/definitions/qmsTestnet.ts +27 -0
- package/chains/definitions/tempo.ts +1 -1
- package/chains/definitions/tempoModerato.ts +2 -2
- package/chains/index.ts +1 -0
- package/clients/transports/ipc.ts +38 -29
- package/clients/transports/webSocket.ts +42 -33
- package/errors/version.ts +1 -1
- package/package.json +2 -2
- package/tempo/Abis.ts +298 -0
- package/tempo/Account.ts +95 -84
- package/tempo/Addresses.ts +1 -0
- package/tempo/Client.ts +3 -3
- package/tempo/Decorator.ts +91 -60
- package/tempo/ExecutionError.ts +102 -4
- package/tempo/Formatters.ts +14 -13
- package/tempo/KeyAuthorizationManager.ts +71 -19
- package/tempo/Relay.ts +70 -14
- package/tempo/Transaction.ts +31 -23
- package/tempo/Transport.ts +49 -21
- package/tempo/{multisig → accounts}/Config.ts +21 -21
- package/tempo/{multisig → accounts}/Operation.ts +26 -26
- package/tempo/{multisig → accounts}/Signature.ts +5 -3
- package/tempo/actions/accessKey.ts +675 -104
- package/tempo/actions/{multisig.ts → accounts.ts} +54 -59
- package/tempo/actions/amm.ts +126 -4
- package/tempo/actions/channel.ts +248 -14
- package/tempo/actions/dex.ts +599 -33
- package/tempo/actions/earn/deployment.ts +11 -9
- package/tempo/actions/earn.ts +40 -35
- package/tempo/actions/fee.ts +111 -9
- package/tempo/actions/index.ts +1 -1
- package/tempo/actions/nonce.ts +86 -1
- package/tempo/actions/policy.ts +66 -14
- package/tempo/actions/propAmm.ts +36 -9
- package/tempo/actions/receivePolicy.ts +26 -15
- package/tempo/actions/reward.ts +17 -9
- package/tempo/actions/token.ts +519 -54
- package/tempo/actions/validator.ts +40 -16
- package/tempo/actions/virtualAddress.ts +7 -5
- package/tempo/actions/zone.ts +25 -20
- package/tempo/chainConfig.ts +52 -52
- package/tempo/errors.ts +114 -1
- package/tempo/index.ts +5 -4
- package/tempo/internal/errors.ts +110 -0
- package/tempo/internal/relay/{multisig.ts → accounts.ts} +118 -118
- package/tempo/internal/relay/feePayer.ts +1 -1
- package/tempo/internal/relay/keyAuthorization.ts +415 -0
- package/tempo/internal/relay/plugin.ts +20 -7
- package/tempo/internal/resolveError.ts +169 -0
- package/tempo/internal/utils.ts +2 -0
- package/utils/hash/normalizeSignature.ts +3 -2
- package/utils/rpc/http.ts +76 -30
- package/utils/rpc/socket.ts +3 -1
- package/_cjs/tempo/actions/multisig.js.map +0 -1
- package/_cjs/tempo/internal/relay/multisig.js.map +0 -1
- package/_cjs/tempo/multisig/Signature.js.map +0 -1
- package/_esm/tempo/actions/multisig.js.map +0 -1
- package/_esm/tempo/internal/relay/multisig.js.map +0 -1
- package/_esm/tempo/multisig/Signature.js +0 -9
- package/_esm/tempo/multisig/Signature.js.map +0 -1
- package/_types/tempo/actions/multisig.d.ts.map +0 -1
- package/_types/tempo/internal/relay/multisig.d.ts +0 -4
- package/_types/tempo/internal/relay/multisig.d.ts.map +0 -1
- package/_types/tempo/multisig/Config.d.ts.map +0 -1
- package/_types/tempo/multisig/Operation.d.ts.map +0 -1
- package/_types/tempo/multisig/Signature.d.ts.map +0 -1
|
@@ -419,7 +419,7 @@ async function handleRawTransaction(options: handleRawTransaction.Options) {
|
|
|
419
419
|
})
|
|
420
420
|
|
|
421
421
|
const transaction = Transaction.deserialize(serialized)
|
|
422
|
-
// Prefer sender recovered from raw envelope;
|
|
422
|
+
// Prefer sender recovered from raw envelope; configurable account finalize path supplies fallback sender.
|
|
423
423
|
const sender = transaction.from ?? options.sender
|
|
424
424
|
|
|
425
425
|
// Sponsorship only applies after sender has signed original transaction.
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import type { Address } from 'abitype'
|
|
2
|
+
import * as RpcResponse from 'ox/RpcResponse'
|
|
3
|
+
import {
|
|
4
|
+
AccountConfig,
|
|
5
|
+
AccountOperation,
|
|
6
|
+
KeyAuthorization,
|
|
7
|
+
SignatureEnvelope,
|
|
8
|
+
} from 'ox/tempo'
|
|
9
|
+
import { getBlockNumber } from '../../../actions/public/getBlockNumber.js'
|
|
10
|
+
import type { Client } from '../../../clients/createClient.js'
|
|
11
|
+
import { BaseError } from '../../../errors/base.js'
|
|
12
|
+
import { isAddressEqual } from '../../../utils/address/isAddressEqual.js'
|
|
13
|
+
import * as Actions from '../../actions/index.js'
|
|
14
|
+
import type * as Relay from '../../Relay.js'
|
|
15
|
+
import * as Store from '../../Store.js'
|
|
16
|
+
import { selectApprovals, validateConfig } from './accounts.js'
|
|
17
|
+
import * as Plugin from './plugin.js'
|
|
18
|
+
|
|
19
|
+
/** Creates middleware that stores pending key authorizations and attaches them to fills. @internal */
|
|
20
|
+
export function create(
|
|
21
|
+
options: Relay.keyAuthorization.Options = {},
|
|
22
|
+
): Relay.keyAuthorization.ReturnType {
|
|
23
|
+
const { store = Store.memory() } = options
|
|
24
|
+
if (!store.compareAndSet)
|
|
25
|
+
throw new RpcResponse.InvalidParamsError({
|
|
26
|
+
message:
|
|
27
|
+
'Key authorization storage requires a store with atomic `compareAndSet`.',
|
|
28
|
+
})
|
|
29
|
+
return Plugin.keyAuthorization({
|
|
30
|
+
async handleRequest(context, next) {
|
|
31
|
+
const { request } = context
|
|
32
|
+
|
|
33
|
+
if (request.method === 'relay_saveKeyAuthorization') {
|
|
34
|
+
const keyAuthorization = parse(request.params?.[0])
|
|
35
|
+
const chainId = Number(keyAuthorization.chainId)
|
|
36
|
+
if (
|
|
37
|
+
context.options.chainId !== undefined &&
|
|
38
|
+
context.options.chainId !== chainId
|
|
39
|
+
)
|
|
40
|
+
throw new RpcResponse.InvalidParamsError({
|
|
41
|
+
message: 'Conflicting chain ids.',
|
|
42
|
+
})
|
|
43
|
+
const client = context.getClient(chainId)
|
|
44
|
+
const account = await check(client, keyAuthorization)
|
|
45
|
+
if (!account) return null
|
|
46
|
+
// Drop an unusable pending authorization so it cannot block this one.
|
|
47
|
+
await read(client, store, {
|
|
48
|
+
account,
|
|
49
|
+
accessKey: keyAuthorization.address,
|
|
50
|
+
chainId,
|
|
51
|
+
})
|
|
52
|
+
if (!(await write(store, { account, keyAuthorization })))
|
|
53
|
+
throw new RpcResponse.InvalidParamsError({
|
|
54
|
+
message:
|
|
55
|
+
'A pending key authorization for this access key expires later.',
|
|
56
|
+
})
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (request.method === 'account_approveKeyAuthorization') {
|
|
61
|
+
await next()
|
|
62
|
+
const operation = AccountOperation.fromRpc(
|
|
63
|
+
context.result as AccountOperation.Rpc,
|
|
64
|
+
) as AccountOperation.KeyAuthorizationOperation
|
|
65
|
+
if (operation.status !== 'success') return
|
|
66
|
+
const keyAuthorization = KeyAuthorization.deserialize(
|
|
67
|
+
operation.keyAuthorization,
|
|
68
|
+
) as KeyAuthorization.Signed
|
|
69
|
+
// Re-check completed operations: quorum may land after expiry, and a
|
|
70
|
+
// stored success may predate a config rotation. The approval itself
|
|
71
|
+
// succeeded, so an authorization that fails these checks, or loses to a
|
|
72
|
+
// newer pending one, is skipped rather than reported. Operational
|
|
73
|
+
// failures propagate so callers know storage did not complete.
|
|
74
|
+
const chainId = Number(keyAuthorization.chainId)
|
|
75
|
+
const client = context.getClient(chainId)
|
|
76
|
+
const account = await check(client, keyAuthorization).catch((error) => {
|
|
77
|
+
if (error instanceof RpcResponse.InvalidParamsError) return undefined
|
|
78
|
+
throw error
|
|
79
|
+
})
|
|
80
|
+
if (!account) return
|
|
81
|
+
await read(client, store, {
|
|
82
|
+
account,
|
|
83
|
+
accessKey: keyAuthorization.address,
|
|
84
|
+
chainId,
|
|
85
|
+
})
|
|
86
|
+
await write(store, { account, keyAuthorization })
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (request.method !== 'eth_fillTransaction') return next()
|
|
91
|
+
|
|
92
|
+
const parameters = request.params?.[0] as Record<string, unknown>
|
|
93
|
+
const { from: account, keyId: accessKey } = parameters
|
|
94
|
+
if (
|
|
95
|
+
parameters.keyAuthorization ||
|
|
96
|
+
typeof account !== 'string' ||
|
|
97
|
+
typeof accessKey !== 'string'
|
|
98
|
+
)
|
|
99
|
+
return next()
|
|
100
|
+
|
|
101
|
+
const pending = await read(context.client, store, {
|
|
102
|
+
account: account as Address,
|
|
103
|
+
accessKey: accessKey as Address,
|
|
104
|
+
chainId: context.options.chainId!,
|
|
105
|
+
})
|
|
106
|
+
if (!pending) return next()
|
|
107
|
+
const metadata = await Actions.accessKey.getMetadata(context.client, {
|
|
108
|
+
account: account as Address,
|
|
109
|
+
accessKey: accessKey as Address,
|
|
110
|
+
})
|
|
111
|
+
// The key already landed onchain, so the authorization is spent.
|
|
112
|
+
if (isActive(metadata, accessKey as Address)) {
|
|
113
|
+
await remove(store, pending)
|
|
114
|
+
return next()
|
|
115
|
+
}
|
|
116
|
+
const keyAuthorization = KeyAuthorization.toRpc(pending.keyAuthorization)
|
|
117
|
+
|
|
118
|
+
// Attach before forwarding so gas estimation and later plugins see the authorization.
|
|
119
|
+
context.request = {
|
|
120
|
+
...request,
|
|
121
|
+
params: [{ ...parameters, keyAuthorization }],
|
|
122
|
+
}
|
|
123
|
+
return next()
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Parses a signed key authorization from RPC parameters. */
|
|
129
|
+
function parse(value: unknown) {
|
|
130
|
+
const keyAuthorization = (() => {
|
|
131
|
+
try {
|
|
132
|
+
if (typeof value === 'string')
|
|
133
|
+
return KeyAuthorization.deserialize(value as `0x${string}`)
|
|
134
|
+
return KeyAuthorization.fromRpc(value as KeyAuthorization.Rpc)
|
|
135
|
+
} catch {
|
|
136
|
+
return undefined
|
|
137
|
+
}
|
|
138
|
+
})()
|
|
139
|
+
if (!keyAuthorization?.signature)
|
|
140
|
+
throw new RpcResponse.InvalidParamsError({
|
|
141
|
+
message: 'Expected a signed key authorization.',
|
|
142
|
+
})
|
|
143
|
+
return keyAuthorization as KeyAuthorization.Signed
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Validates an authorization and returns the account it authorizes, or `undefined`
|
|
148
|
+
* when the access key is already active onchain.
|
|
149
|
+
*/
|
|
150
|
+
async function check(
|
|
151
|
+
client: Client,
|
|
152
|
+
keyAuthorization: KeyAuthorization.Signed,
|
|
153
|
+
): Promise<Address | undefined> {
|
|
154
|
+
if (isExpired(keyAuthorization))
|
|
155
|
+
throw new RpcResponse.InvalidParamsError({
|
|
156
|
+
message: 'Key authorization has expired.',
|
|
157
|
+
})
|
|
158
|
+
const account = await verify(client, keyAuthorization)
|
|
159
|
+
if (await isBurned(client, { account, keyAuthorization }))
|
|
160
|
+
throw new RpcResponse.InvalidParamsError({
|
|
161
|
+
message: 'Key authorization witness has been burned.',
|
|
162
|
+
})
|
|
163
|
+
// Authorizations for keys that are already active would only be removed on the next fill.
|
|
164
|
+
const metadata = await Actions.accessKey.getMetadata(client, {
|
|
165
|
+
account,
|
|
166
|
+
accessKey: keyAuthorization.address,
|
|
167
|
+
})
|
|
168
|
+
if (isActive(metadata, keyAuthorization.address)) return undefined
|
|
169
|
+
return account
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Verifies the authorization signature and returns the account it authorizes. */
|
|
173
|
+
async function verify(
|
|
174
|
+
client: Client,
|
|
175
|
+
keyAuthorization: KeyAuthorization.Signed,
|
|
176
|
+
): Promise<Address> {
|
|
177
|
+
const { signature, ...unsigned } = keyAuthorization
|
|
178
|
+
const payload = KeyAuthorization.getSignPayload(unsigned)
|
|
179
|
+
|
|
180
|
+
if (signature.type === 'configurable') {
|
|
181
|
+
if (
|
|
182
|
+
!keyAuthorization.account ||
|
|
183
|
+
!isAddressEqual(keyAuthorization.account, signature.account)
|
|
184
|
+
)
|
|
185
|
+
throw new RpcResponse.InvalidParamsError({
|
|
186
|
+
message:
|
|
187
|
+
'Configurable account key authorization account does not match its signature.',
|
|
188
|
+
})
|
|
189
|
+
const config = AccountConfig.from(signature.config)
|
|
190
|
+
await validateConfig({
|
|
191
|
+
account: signature.account,
|
|
192
|
+
blockNumber: await getBlockNumber(client, { cacheTime: 0 }),
|
|
193
|
+
client: client as never,
|
|
194
|
+
config,
|
|
195
|
+
})
|
|
196
|
+
const approvals = await selectApprovals({
|
|
197
|
+
account: signature.account,
|
|
198
|
+
approvals: signature.signatures.map((signature) =>
|
|
199
|
+
SignatureEnvelope.serialize(signature),
|
|
200
|
+
),
|
|
201
|
+
config,
|
|
202
|
+
hash: AccountConfig.getSignPayload({
|
|
203
|
+
account: signature.account,
|
|
204
|
+
config,
|
|
205
|
+
payload,
|
|
206
|
+
}),
|
|
207
|
+
})
|
|
208
|
+
if (approvals.weight < approvals.threshold)
|
|
209
|
+
throw new RpcResponse.InvalidParamsError({
|
|
210
|
+
message:
|
|
211
|
+
'Configurable account key authorization has not reached quorum.',
|
|
212
|
+
})
|
|
213
|
+
return signature.account
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const signer = (() => {
|
|
217
|
+
try {
|
|
218
|
+
const signer = SignatureEnvelope.extractAddress({ payload, signature })
|
|
219
|
+
if (SignatureEnvelope.verify(signature, { address: signer, payload }))
|
|
220
|
+
return signer
|
|
221
|
+
} catch {}
|
|
222
|
+
throw new RpcResponse.InvalidParamsError({
|
|
223
|
+
message: 'Invalid key authorization signature.',
|
|
224
|
+
})
|
|
225
|
+
})()
|
|
226
|
+
|
|
227
|
+
// Admin-signed authorizations can only be submitted by the admin key itself,
|
|
228
|
+
// so only authorizations signed by the account can be attached for the new key.
|
|
229
|
+
if (
|
|
230
|
+
keyAuthorization.account &&
|
|
231
|
+
!isAddressEqual(keyAuthorization.account, signer)
|
|
232
|
+
)
|
|
233
|
+
throw new RpcResponse.InvalidParamsError({
|
|
234
|
+
message: 'Key authorization must be signed by its account.',
|
|
235
|
+
})
|
|
236
|
+
return signer
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Reads the pending authorization for an access key. Entries that are malformed,
|
|
241
|
+
* expired, or whose witness has been burned are removed.
|
|
242
|
+
*/
|
|
243
|
+
async function read(
|
|
244
|
+
client: Client,
|
|
245
|
+
store: Store.Atomic,
|
|
246
|
+
options: { account: Address; accessKey: Address; chainId: number },
|
|
247
|
+
) {
|
|
248
|
+
const { account } = options
|
|
249
|
+
const key = getKey(options)
|
|
250
|
+
const value = await store.getItem(key)
|
|
251
|
+
if (!value) return undefined
|
|
252
|
+
const keyAuthorization = (() => {
|
|
253
|
+
try {
|
|
254
|
+
return KeyAuthorization.deserialize(
|
|
255
|
+
value as `0x${string}`,
|
|
256
|
+
) as KeyAuthorization.Signed
|
|
257
|
+
} catch {
|
|
258
|
+
return undefined
|
|
259
|
+
}
|
|
260
|
+
})()
|
|
261
|
+
if (
|
|
262
|
+
keyAuthorization &&
|
|
263
|
+
!isExpired(keyAuthorization) &&
|
|
264
|
+
(await isVerified(client, keyAuthorization)) &&
|
|
265
|
+
!(await isBurned(client, { account, keyAuthorization }))
|
|
266
|
+
)
|
|
267
|
+
return { key, keyAuthorization, value }
|
|
268
|
+
await remove(store, { key, value })
|
|
269
|
+
return undefined
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Removes a stored authorization only while it still holds the value that was read,
|
|
274
|
+
* so a concurrently saved replacement survives. The empty value expires immediately.
|
|
275
|
+
* @internal
|
|
276
|
+
*/
|
|
277
|
+
export async function remove(
|
|
278
|
+
store: Store.Atomic,
|
|
279
|
+
options: { key: string; value: string },
|
|
280
|
+
) {
|
|
281
|
+
const { key, value } = options
|
|
282
|
+
await store.compareAndSet(key, value, '', { expiresAt: Date.now() })
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Returns whether the authorization still verifies, such as after a configurable
|
|
287
|
+
* account rotates its config. Operational failures propagate.
|
|
288
|
+
*/
|
|
289
|
+
async function isVerified(
|
|
290
|
+
client: Client,
|
|
291
|
+
keyAuthorization: KeyAuthorization.Signed,
|
|
292
|
+
) {
|
|
293
|
+
try {
|
|
294
|
+
await verify(client, keyAuthorization)
|
|
295
|
+
return true
|
|
296
|
+
} catch (error) {
|
|
297
|
+
if (error instanceof RpcResponse.InvalidParamsError) return false
|
|
298
|
+
throw error
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** Returns whether the authorization's witness has been burned onchain. */
|
|
303
|
+
async function isBurned(
|
|
304
|
+
client: Client,
|
|
305
|
+
options: { account: Address; keyAuthorization: KeyAuthorization.Signed },
|
|
306
|
+
) {
|
|
307
|
+
const { account, keyAuthorization } = options
|
|
308
|
+
if (!keyAuthorization.witness) return false
|
|
309
|
+
return Actions.accessKey.isWitnessBurned(client, {
|
|
310
|
+
account,
|
|
311
|
+
witness: keyAuthorization.witness,
|
|
312
|
+
})
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Stores an authorization unless a pending authorization for the same key expires
|
|
317
|
+
* later, so a replayed older authorization cannot replace a newer one.
|
|
318
|
+
* @internal
|
|
319
|
+
*/
|
|
320
|
+
export async function write(
|
|
321
|
+
store: Store.Atomic,
|
|
322
|
+
options: {
|
|
323
|
+
account: Address
|
|
324
|
+
keyAuthorization: KeyAuthorization.Signed
|
|
325
|
+
},
|
|
326
|
+
): Promise<boolean> {
|
|
327
|
+
const { account, keyAuthorization } = options
|
|
328
|
+
const key = getKey({
|
|
329
|
+
account,
|
|
330
|
+
accessKey: keyAuthorization.address,
|
|
331
|
+
chainId: Number(keyAuthorization.chainId),
|
|
332
|
+
})
|
|
333
|
+
const value = KeyAuthorization.serialize(keyAuthorization)
|
|
334
|
+
const expiresAt = Math.min(
|
|
335
|
+
Date.now() + maxTtl,
|
|
336
|
+
getExpiry(keyAuthorization) * 1_000,
|
|
337
|
+
)
|
|
338
|
+
for (let attempt = 0; attempt < maxWriteAttempts; attempt++) {
|
|
339
|
+
const current = (await store.getItem(key)) ?? null
|
|
340
|
+
const existing = (() => {
|
|
341
|
+
if (!current) return undefined
|
|
342
|
+
try {
|
|
343
|
+
return KeyAuthorization.deserialize(current as `0x${string}`)
|
|
344
|
+
} catch {
|
|
345
|
+
return undefined
|
|
346
|
+
}
|
|
347
|
+
})()
|
|
348
|
+
if (existing && current!.toLowerCase() === value.toLowerCase()) return true
|
|
349
|
+
if (existing && getExpiry(existing) >= getExpiry(keyAuthorization))
|
|
350
|
+
return false
|
|
351
|
+
// A lost compare-and-set means another writer replaced the value, so compare again.
|
|
352
|
+
if (await store.compareAndSet(key, current, value, { expiresAt }))
|
|
353
|
+
return true
|
|
354
|
+
}
|
|
355
|
+
throw new StoreConflictError()
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** Maximum compare-and-set attempts before a write is abandoned. */
|
|
359
|
+
const maxWriteAttempts = 32
|
|
360
|
+
|
|
361
|
+
/** Maximum time a pending key authorization is kept. */
|
|
362
|
+
const maxTtl = 30 * 24 * 60 * 60 * 1_000
|
|
363
|
+
|
|
364
|
+
/** Returns the authorization expiry in seconds, treating no expiry as unbounded. */
|
|
365
|
+
function getExpiry(keyAuthorization: KeyAuthorization.KeyAuthorization) {
|
|
366
|
+
const { expiry } = keyAuthorization
|
|
367
|
+
return expiry === undefined || expiry === null
|
|
368
|
+
? Number.POSITIVE_INFINITY
|
|
369
|
+
: Number(expiry)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/** Returns whether the access key is active onchain. */
|
|
373
|
+
function isActive(
|
|
374
|
+
metadata: Actions.accessKey.getMetadata.ReturnValue,
|
|
375
|
+
accessKey: Address,
|
|
376
|
+
) {
|
|
377
|
+
return (
|
|
378
|
+
isAddressEqual(metadata.address, accessKey) &&
|
|
379
|
+
!metadata.isRevoked &&
|
|
380
|
+
metadata.expiry > BigInt(Math.floor(Date.now() / 1000))
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function isExpired(keyAuthorization: KeyAuthorization.KeyAuthorization) {
|
|
385
|
+
const { expiry } = keyAuthorization
|
|
386
|
+
return (
|
|
387
|
+
expiry !== undefined &&
|
|
388
|
+
expiry !== null &&
|
|
389
|
+
BigInt(expiry) <= BigInt(Math.floor(Date.now() / 1000))
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function getKey(options: {
|
|
394
|
+
account: Address
|
|
395
|
+
accessKey: Address
|
|
396
|
+
chainId: number
|
|
397
|
+
}) {
|
|
398
|
+
const { account, accessKey, chainId } = options
|
|
399
|
+
return [
|
|
400
|
+
'keyAuthorization',
|
|
401
|
+
chainId,
|
|
402
|
+
account.toLowerCase(),
|
|
403
|
+
accessKey.toLowerCase(),
|
|
404
|
+
].join(':')
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/** Thrown when a pending key authorization cannot be written after repeated conflicts. */
|
|
408
|
+
class StoreConflictError extends BaseError {
|
|
409
|
+
constructor() {
|
|
410
|
+
super(
|
|
411
|
+
'Pending key authorization could not be written after repeated conflicts.',
|
|
412
|
+
{ name: 'Relay.KeyAuthorization.StoreConflictError' },
|
|
413
|
+
)
|
|
414
|
+
}
|
|
415
|
+
}
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import type * as Relay from '../../Relay.js'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const accountsPlugins = new WeakSet<Relay.Plugin>()
|
|
4
|
+
const keyAuthorizationPlugins = new WeakSet<Relay.Plugin>()
|
|
4
5
|
|
|
5
|
-
/** Registers the built-in
|
|
6
|
-
export function
|
|
7
|
-
|
|
8
|
-
return plugin as Relay.
|
|
6
|
+
/** Registers the built-in accounts plugin for transport capability detection. */
|
|
7
|
+
export function accounts(plugin: Relay.Plugin): Relay.accounts.ReturnType {
|
|
8
|
+
accountsPlugins.add(plugin)
|
|
9
|
+
return plugin as Relay.accounts.ReturnType
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
export function
|
|
12
|
-
return
|
|
12
|
+
export function isAccounts(plugin: Relay.Plugin): boolean {
|
|
13
|
+
return accountsPlugins.has(plugin)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Registers the built-in key authorization plugin for transport capability detection. */
|
|
17
|
+
export function keyAuthorization(
|
|
18
|
+
plugin: Relay.Plugin,
|
|
19
|
+
): Relay.keyAuthorization.ReturnType {
|
|
20
|
+
keyAuthorizationPlugins.add(plugin)
|
|
21
|
+
return plugin as Relay.keyAuthorization.ReturnType
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isKeyAuthorization(plugin: Relay.Plugin): boolean {
|
|
25
|
+
return keyAuthorizationPlugins.has(plugin)
|
|
13
26
|
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type { Abi, Address } from 'abitype'
|
|
2
|
+
import { parseAccount } from '../../accounts/utils/parseAccount.js'
|
|
3
|
+
import { call } from '../../actions/public/call.js'
|
|
4
|
+
import { multicall } from '../../actions/public/multicall.js'
|
|
5
|
+
import type { Client } from '../../clients/createClient.js'
|
|
6
|
+
import { BaseError } from '../../errors/base.js'
|
|
7
|
+
import { TransactionReceiptRevertedError } from '../../errors/transaction.js'
|
|
8
|
+
import type { ContractFunctionParameters } from '../../types/contract.js'
|
|
9
|
+
import { encodeFunctionData } from '../../utils/abi/encodeFunctionData.js'
|
|
10
|
+
import { formatAbiItem } from '../../utils/abi/formatAbiItem.js'
|
|
11
|
+
import { toFunctionSelector } from '../../utils/hash/toFunctionSelector.js'
|
|
12
|
+
import { errorResolvers as accessKeyResolvers } from '../actions/accessKey.js'
|
|
13
|
+
import { errorResolvers as ammResolvers } from '../actions/amm.js'
|
|
14
|
+
import { errorResolvers as channelResolvers } from '../actions/channel.js'
|
|
15
|
+
import { errorResolvers as dexResolvers } from '../actions/dex.js'
|
|
16
|
+
import { errorResolvers as feeResolvers } from '../actions/fee.js'
|
|
17
|
+
import { errorResolvers as nonceResolvers } from '../actions/nonce.js'
|
|
18
|
+
import { errorResolvers as policyResolvers } from '../actions/policy.js'
|
|
19
|
+
import { errorResolvers as tokenResolvers } from '../actions/token.js'
|
|
20
|
+
import * as ExecutionError from '../ExecutionError.js'
|
|
21
|
+
import { TempoExecutionError } from '../errors.js'
|
|
22
|
+
import type { Context, Hints, Resolvers } from './errors.js'
|
|
23
|
+
|
|
24
|
+
/** Node messages that carry no revert data, mapped to the error they describe. */
|
|
25
|
+
const messages = [
|
|
26
|
+
[/Access key expired: expiry \d+ <= min allowed \d+/, 'ExpiryInPast()'],
|
|
27
|
+
] as const
|
|
28
|
+
|
|
29
|
+
export async function toExecutionError(
|
|
30
|
+
cause: unknown,
|
|
31
|
+
options: {
|
|
32
|
+
client: Client
|
|
33
|
+
hints?: Hints | undefined
|
|
34
|
+
parameters: Record<string, any>
|
|
35
|
+
},
|
|
36
|
+
) {
|
|
37
|
+
if (!(cause instanceof BaseError)) return cause
|
|
38
|
+
const { client, parameters } = options
|
|
39
|
+
const source = (await replay(cause, client, parameters)) ?? cause
|
|
40
|
+
const details = source.details ?? ''
|
|
41
|
+
let error: ExecutionError.ExecutionError = ExecutionError.from(source)
|
|
42
|
+
if (error.errorName === 'unknown') {
|
|
43
|
+
const signature = messages.find(([pattern]) => pattern.test(details))?.[1]
|
|
44
|
+
if (!signature) return cause
|
|
45
|
+
error = ExecutionError.from(toFunctionSelector(signature))
|
|
46
|
+
}
|
|
47
|
+
if (error.errorName === 'unknown') return cause
|
|
48
|
+
|
|
49
|
+
const signature = formatAbiItem(
|
|
50
|
+
error.abiItem,
|
|
51
|
+
) as keyof typeof ExecutionError.messages
|
|
52
|
+
// Solidity `Error(string)` and `Panic(uint256)` are not Tempo errors.
|
|
53
|
+
if (!(signature in ExecutionError.messages)) return cause
|
|
54
|
+
const hints = [
|
|
55
|
+
...(options.hints?.[error.errorName] ?? []),
|
|
56
|
+
...(ExecutionError.hints[signature] ?? []),
|
|
57
|
+
]
|
|
58
|
+
const base = { cause, error, hints } as TempoExecutionError.Parameters
|
|
59
|
+
|
|
60
|
+
const account_ = parameters.account ?? client.account
|
|
61
|
+
const account = account_ ? parseAccount(account_) : undefined
|
|
62
|
+
const context: Context = {
|
|
63
|
+
base,
|
|
64
|
+
call: getCall(parameters),
|
|
65
|
+
client,
|
|
66
|
+
details,
|
|
67
|
+
parameters,
|
|
68
|
+
read: (contracts) =>
|
|
69
|
+
multicall(client, {
|
|
70
|
+
allowFailure: true,
|
|
71
|
+
contracts: contracts as readonly ContractFunctionParameters[],
|
|
72
|
+
deployless: true,
|
|
73
|
+
}).catch(() => []) as never,
|
|
74
|
+
signer: account && {
|
|
75
|
+
address: account.address,
|
|
76
|
+
accessKey:
|
|
77
|
+
account.source === 'accessKey' && 'accessKeyAddress' in account
|
|
78
|
+
? (account.accessKeyAddress as Address)
|
|
79
|
+
: undefined,
|
|
80
|
+
},
|
|
81
|
+
validation: !hasRevertData(source),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const resolvers of [
|
|
85
|
+
accessKeyResolvers,
|
|
86
|
+
ammResolvers,
|
|
87
|
+
channelResolvers,
|
|
88
|
+
dexResolvers,
|
|
89
|
+
feeResolvers,
|
|
90
|
+
nonceResolvers,
|
|
91
|
+
policyResolvers,
|
|
92
|
+
tokenResolvers,
|
|
93
|
+
] as readonly Resolvers[]) {
|
|
94
|
+
const result = await resolvers[signature]?.(context)
|
|
95
|
+
if (result) return result
|
|
96
|
+
}
|
|
97
|
+
return new TempoExecutionError(base)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Replays a call whose receipt reverted, to recover its revert data. */
|
|
101
|
+
async function replay(
|
|
102
|
+
cause: BaseError,
|
|
103
|
+
client: Client,
|
|
104
|
+
parameters: Record<string, any>,
|
|
105
|
+
) {
|
|
106
|
+
const reverted = cause.walk(
|
|
107
|
+
(error) => error instanceof TransactionReceiptRevertedError,
|
|
108
|
+
) as TransactionReceiptRevertedError | null
|
|
109
|
+
const to = parameters.to ?? parameters.address
|
|
110
|
+
const data =
|
|
111
|
+
parameters.data ??
|
|
112
|
+
(parameters.abi && parameters.functionName
|
|
113
|
+
? encodeFunctionData({
|
|
114
|
+
abi: parameters.abi,
|
|
115
|
+
args: parameters.args,
|
|
116
|
+
functionName: parameters.functionName,
|
|
117
|
+
})
|
|
118
|
+
: undefined)
|
|
119
|
+
if (!reverted || !to || !data) return undefined
|
|
120
|
+
// ponytail: replays on the parent block, so earlier transactions in the same block are not applied.
|
|
121
|
+
return call(client, {
|
|
122
|
+
account: parameters.account ?? client.account,
|
|
123
|
+
blockNumber: reverted.receipt.blockNumber - 1n,
|
|
124
|
+
data,
|
|
125
|
+
to,
|
|
126
|
+
value: parameters.value,
|
|
127
|
+
}).then(
|
|
128
|
+
() => undefined,
|
|
129
|
+
(error) => (error instanceof BaseError ? error : undefined),
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Whether an error carries revert data, which only execution reverts have. */
|
|
134
|
+
function hasRevertData(error: BaseError) {
|
|
135
|
+
return !!error.walk((error) => {
|
|
136
|
+
const { data, raw } = error as { data?: unknown; raw?: unknown }
|
|
137
|
+
const hex =
|
|
138
|
+
typeof data === 'object' && data !== null
|
|
139
|
+
? (data as { data?: unknown }).data
|
|
140
|
+
: (data ?? raw)
|
|
141
|
+
return typeof hex === 'string' && hex.length >= 10
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function getCall(parameters: Record<string, any>): Context['call'] {
|
|
146
|
+
const {
|
|
147
|
+
abi,
|
|
148
|
+
address,
|
|
149
|
+
args = [],
|
|
150
|
+
functionName,
|
|
151
|
+
} = parameters as {
|
|
152
|
+
abi?: Abi
|
|
153
|
+
address?: Address
|
|
154
|
+
args?: readonly unknown[]
|
|
155
|
+
functionName?: string
|
|
156
|
+
}
|
|
157
|
+
if (!address || !functionName) return undefined
|
|
158
|
+
const item = abi?.find(
|
|
159
|
+
(item) => item.type === 'function' && item.name === functionName,
|
|
160
|
+
)
|
|
161
|
+
const inputs = item?.type === 'function' ? item.inputs : []
|
|
162
|
+
return {
|
|
163
|
+
address,
|
|
164
|
+
args,
|
|
165
|
+
functionName,
|
|
166
|
+
arg: (name) =>
|
|
167
|
+
args[inputs.findIndex((input) => input.name === name)] as never,
|
|
168
|
+
}
|
|
169
|
+
}
|
package/tempo/internal/utils.ts
CHANGED
|
@@ -279,6 +279,8 @@ export function defineCall<
|
|
|
279
279
|
} {
|
|
280
280
|
return {
|
|
281
281
|
...(call as any),
|
|
282
|
+
// Precompile errors bubble up across contracts (e.g. `AccountKeychain` errors from a TIP-20 `transfer`).
|
|
283
|
+
abi: [...call.abi, ...Abis.coreErrors],
|
|
282
284
|
data: encodeFunctionData(call as never),
|
|
283
285
|
to: call.address,
|
|
284
286
|
} as const
|
|
@@ -46,8 +46,9 @@ export function normalizeSignature(
|
|
|
46
46
|
|
|
47
47
|
// Ignore spaces
|
|
48
48
|
if (char === ' ') {
|
|
49
|
-
//
|
|
50
|
-
|
|
49
|
+
// A space directly after a `(` or `,` separator is leading whitespace.
|
|
50
|
+
// Otherwise, it ends the type, so we want to deactivate.
|
|
51
|
+
if (!current.endsWith(',') && !current.endsWith('(')) {
|
|
51
52
|
current = ''
|
|
52
53
|
active = false
|
|
53
54
|
}
|