@feelyourprotocol/vm 8141.0.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/LICENSE +373 -0
- package/README.md +583 -0
- package/dist/cjs/bloom/index.d.ts +29 -0
- package/dist/cjs/bloom/index.d.ts.map +1 -0
- package/dist/cjs/bloom/index.js +76 -0
- package/dist/cjs/bloom/index.js.map +1 -0
- package/dist/cjs/buildBlock.d.ts +118 -0
- package/dist/cjs/buildBlock.d.ts.map +1 -0
- package/dist/cjs/buildBlock.js +363 -0
- package/dist/cjs/buildBlock.js.map +1 -0
- package/dist/cjs/constructors.d.ts +9 -0
- package/dist/cjs/constructors.d.ts.map +1 -0
- package/dist/cjs/constructors.js +75 -0
- package/dist/cjs/constructors.js.map +1 -0
- package/dist/cjs/emitEVMProfile.d.ts +9 -0
- package/dist/cjs/emitEVMProfile.d.ts.map +1 -0
- package/dist/cjs/emitEVMProfile.js +130 -0
- package/dist/cjs/emitEVMProfile.js.map +1 -0
- package/dist/cjs/index.d.ts +11 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +36 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/params.d.ts +3 -0
- package/dist/cjs/params.d.ts.map +1 -0
- package/dist/cjs/params.js +105 -0
- package/dist/cjs/params.js.map +1 -0
- package/dist/cjs/requests.d.ts +11 -0
- package/dist/cjs/requests.d.ts.map +1 -0
- package/dist/cjs/requests.js +208 -0
- package/dist/cjs/requests.js.map +1 -0
- package/dist/cjs/runBlock.d.ts +35 -0
- package/dist/cjs/runBlock.d.ts.map +1 -0
- package/dist/cjs/runBlock.js +797 -0
- package/dist/cjs/runBlock.js.map +1 -0
- package/dist/cjs/runFrameTx.d.ts +18 -0
- package/dist/cjs/runFrameTx.d.ts.map +1 -0
- package/dist/cjs/runFrameTx.js +313 -0
- package/dist/cjs/runFrameTx.js.map +1 -0
- package/dist/cjs/runTx.d.ts +18 -0
- package/dist/cjs/runTx.d.ts.map +1 -0
- package/dist/cjs/runTx.js +900 -0
- package/dist/cjs/runTx.js.map +1 -0
- package/dist/cjs/types.d.ts +452 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/vm.d.ts +75 -0
- package/dist/cjs/vm.d.ts.map +1 -0
- package/dist/cjs/vm.js +111 -0
- package/dist/cjs/vm.js.map +1 -0
- package/dist/esm/bloom/index.d.ts +29 -0
- package/dist/esm/bloom/index.d.ts.map +1 -0
- package/dist/esm/bloom/index.js +72 -0
- package/dist/esm/bloom/index.js.map +1 -0
- package/dist/esm/buildBlock.d.ts +118 -0
- package/dist/esm/buildBlock.d.ts.map +1 -0
- package/dist/esm/buildBlock.js +358 -0
- package/dist/esm/buildBlock.js.map +1 -0
- package/dist/esm/constructors.d.ts +9 -0
- package/dist/esm/constructors.d.ts.map +1 -0
- package/dist/esm/constructors.js +72 -0
- package/dist/esm/constructors.js.map +1 -0
- package/dist/esm/emitEVMProfile.d.ts +9 -0
- package/dist/esm/emitEVMProfile.d.ts.map +1 -0
- package/dist/esm/emitEVMProfile.js +127 -0
- package/dist/esm/emitEVMProfile.js.map +1 -0
- package/dist/esm/index.d.ts +11 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +11 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/params.d.ts +3 -0
- package/dist/esm/params.d.ts.map +1 -0
- package/dist/esm/params.js +102 -0
- package/dist/esm/params.js.map +1 -0
- package/dist/esm/requests.d.ts +11 -0
- package/dist/esm/requests.d.ts.map +1 -0
- package/dist/esm/requests.js +204 -0
- package/dist/esm/requests.js.map +1 -0
- package/dist/esm/runBlock.d.ts +35 -0
- package/dist/esm/runBlock.d.ts.map +1 -0
- package/dist/esm/runBlock.js +790 -0
- package/dist/esm/runBlock.js.map +1 -0
- package/dist/esm/runFrameTx.d.ts +18 -0
- package/dist/esm/runFrameTx.d.ts.map +1 -0
- package/dist/esm/runFrameTx.js +310 -0
- package/dist/esm/runFrameTx.js.map +1 -0
- package/dist/esm/runTx.d.ts +18 -0
- package/dist/esm/runTx.d.ts.map +1 -0
- package/dist/esm/runTx.js +896 -0
- package/dist/esm/runTx.js.map +1 -0
- package/dist/esm/types.d.ts +452 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/vm.d.ts +75 -0
- package/dist/esm/vm.d.ts.map +1 -0
- package/dist/esm/vm.js +107 -0
- package/dist/esm/vm.js.map +1 -0
- package/dist/tsconfig.prod.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.prod.esm.tsbuildinfo +1 -0
- package/package.json +117 -0
- package/src/bloom/index.ts +83 -0
- package/src/buildBlock.ts +470 -0
- package/src/constructors.ts +91 -0
- package/src/emitEVMProfile.ts +151 -0
- package/src/index.ts +10 -0
- package/src/params.ts +104 -0
- package/src/requests.ts +293 -0
- package/src/runBlock.ts +1022 -0
- package/src/runFrameTx.ts +411 -0
- package/src/runTx.ts +1203 -0
- package/src/types.ts +511 -0
- package/src/vm.ts +147 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EIP-8141 Frame Transaction Execution
|
|
3
|
+
*
|
|
4
|
+
* Implements the frame execution loop and default code for EOA accounts.
|
|
5
|
+
* The EVM holds a FrameExecutionContext = { tx, state } during execution.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { ENTRY_POINT_ADDRESS, FRAME_MODE } from '@feelyourprotocol/evm'
|
|
9
|
+
import type { ExecResult, FrameExecutionState, FrameResult, ParsedFrame } from '@feelyourprotocol/evm'
|
|
10
|
+
import { RLP } from '@feelyourprotocol/rlp'
|
|
11
|
+
import type { FrameBytes, FrameEIP8141Tx } from '@feelyourprotocol/tx'
|
|
12
|
+
import {
|
|
13
|
+
Account,
|
|
14
|
+
Address,
|
|
15
|
+
BIGINT_0,
|
|
16
|
+
BIGINT_1,
|
|
17
|
+
EthereumJSErrorWithoutCode,
|
|
18
|
+
bytesToBigInt,
|
|
19
|
+
bytesToHex,
|
|
20
|
+
concatBytes,
|
|
21
|
+
ecrecover,
|
|
22
|
+
equalsBytes,
|
|
23
|
+
hexToBytes,
|
|
24
|
+
publicToAddress,
|
|
25
|
+
} from '@feelyourprotocol/util'
|
|
26
|
+
import { keccak_256 } from '@noble/hashes/sha3.js'
|
|
27
|
+
|
|
28
|
+
import type { Block } from '@feelyourprotocol/block'
|
|
29
|
+
import type { RunTxOpts, RunTxResult } from './types.ts'
|
|
30
|
+
import type { VM } from './vm.ts'
|
|
31
|
+
|
|
32
|
+
const ECRECOVER_GAS = BigInt(3000)
|
|
33
|
+
const DEFAULT_CODE_OVERHEAD = BigInt(100)
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parse raw FrameBytes from the tx into ParsedFrame objects.
|
|
37
|
+
*/
|
|
38
|
+
function parseFrames(tx: FrameEIP8141Tx): ParsedFrame[] {
|
|
39
|
+
return tx.frames.map((f: FrameBytes) => {
|
|
40
|
+
const modeVal = f[0].length === 0 ? 0 : Number(bytesToBigInt(f[0]))
|
|
41
|
+
return {
|
|
42
|
+
mode: modeVal & 0xff,
|
|
43
|
+
target: f[1].length === 0 ? null : new Address(f[1]),
|
|
44
|
+
gasLimit: f[2].length === 0 ? BIGINT_0 : bytesToBigInt(f[2]),
|
|
45
|
+
data: f[3],
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Execute an EIP-8141 frame transaction through the frame execution loop.
|
|
52
|
+
*
|
|
53
|
+
* Gas cost deduction and nonce increment are handled by APPROVE (or the default
|
|
54
|
+
* code equivalent), not by the VM upfront.
|
|
55
|
+
*/
|
|
56
|
+
export async function runFrameTransaction(
|
|
57
|
+
vm: VM,
|
|
58
|
+
tx: FrameEIP8141Tx,
|
|
59
|
+
gasPrice: bigint,
|
|
60
|
+
blobGasPrice: bigint,
|
|
61
|
+
intrinsicGas: bigint,
|
|
62
|
+
block: Block | undefined,
|
|
63
|
+
_opts: RunTxOpts,
|
|
64
|
+
): Promise<RunTxResult> {
|
|
65
|
+
const evm = vm.evm
|
|
66
|
+
const caller = tx.getSenderAddress()
|
|
67
|
+
const parsedFrames = parseFrames(tx)
|
|
68
|
+
|
|
69
|
+
const blobCount = tx.blobVersionedHashes.length
|
|
70
|
+
const blobGasPerBlob = blobCount > 0 ? vm.common.param('blobGasPerBlob') : BIGINT_0
|
|
71
|
+
const totalBlobGas = blobGasPerBlob * BigInt(blobCount)
|
|
72
|
+
const totalGasCost = tx.gasLimit * gasPrice
|
|
73
|
+
const totalBlobGasCost = totalBlobGas * blobGasPrice
|
|
74
|
+
|
|
75
|
+
const state: FrameExecutionState = {
|
|
76
|
+
parsedFrames,
|
|
77
|
+
currentFrameIndex: 0,
|
|
78
|
+
senderApproved: false,
|
|
79
|
+
payerApproved: false,
|
|
80
|
+
approveCalledInCurrentFrame: false,
|
|
81
|
+
frameResults: [],
|
|
82
|
+
totalGasCost,
|
|
83
|
+
totalBlobGasCost,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
evm.frameExecutionContext = { tx, state }
|
|
87
|
+
|
|
88
|
+
let totalFrameGasUsed = BIGINT_0
|
|
89
|
+
const entryPoint = new Address(hexToBytes(ENTRY_POINT_ADDRESS))
|
|
90
|
+
const allLogs: any[] = []
|
|
91
|
+
let txInvalid = false
|
|
92
|
+
let invalidReason = ''
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
for (let i = 0; i < parsedFrames.length; i++) {
|
|
96
|
+
state.currentFrameIndex = i
|
|
97
|
+
state.approveCalledInCurrentFrame = false
|
|
98
|
+
const frame = parsedFrames[i]
|
|
99
|
+
|
|
100
|
+
if (frame.mode === FRAME_MODE.SENDER && !state.senderApproved) {
|
|
101
|
+
txInvalid = true
|
|
102
|
+
invalidReason = 'SENDER frame executed before sender_approved'
|
|
103
|
+
break
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const frameTarget = frame.target ?? caller
|
|
107
|
+
const frameCaller = frame.mode === FRAME_MODE.SENDER ? caller : entryPoint
|
|
108
|
+
|
|
109
|
+
const code = await vm.stateManager.getCode(frameTarget)
|
|
110
|
+
const hasCode = code.length > 0
|
|
111
|
+
|
|
112
|
+
let frameResult: FrameResult
|
|
113
|
+
|
|
114
|
+
if (!hasCode) {
|
|
115
|
+
frameResult = await runDefaultCode(
|
|
116
|
+
vm,
|
|
117
|
+
tx,
|
|
118
|
+
state,
|
|
119
|
+
frame,
|
|
120
|
+
frameCaller,
|
|
121
|
+
frameTarget,
|
|
122
|
+
gasPrice,
|
|
123
|
+
block,
|
|
124
|
+
)
|
|
125
|
+
} else {
|
|
126
|
+
const result = await evm.runCall({
|
|
127
|
+
block,
|
|
128
|
+
gasPrice,
|
|
129
|
+
caller: frameCaller,
|
|
130
|
+
gasLimit: frame.gasLimit,
|
|
131
|
+
to: frameTarget,
|
|
132
|
+
value: BIGINT_0,
|
|
133
|
+
data: frame.data,
|
|
134
|
+
origin: frameCaller,
|
|
135
|
+
blobVersionedHashes: tx.blobVersionedHashes.map(bytesToHex),
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
const reverted = result.execResult.exceptionError !== undefined
|
|
139
|
+
frameResult = {
|
|
140
|
+
status: reverted ? 0 : 1,
|
|
141
|
+
gasUsed: result.execResult.executionGasUsed,
|
|
142
|
+
returnValue: result.execResult.returnValue,
|
|
143
|
+
}
|
|
144
|
+
if (result.execResult.logs) {
|
|
145
|
+
allLogs.push(...result.execResult.logs)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
state.frameResults.push(frameResult)
|
|
150
|
+
totalFrameGasUsed += frameResult.gasUsed
|
|
151
|
+
|
|
152
|
+
if (frame.mode === FRAME_MODE.VERIFY && !state.approveCalledInCurrentFrame) {
|
|
153
|
+
txInvalid = true
|
|
154
|
+
invalidReason = 'VERIFY frame did not call APPROVE'
|
|
155
|
+
break
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!txInvalid && !state.payerApproved) {
|
|
160
|
+
txInvalid = true
|
|
161
|
+
invalidReason = 'payer_approved not set after executing all frames'
|
|
162
|
+
}
|
|
163
|
+
} finally {
|
|
164
|
+
evm.frameExecutionContext = undefined
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (txInvalid) {
|
|
168
|
+
throw EthereumJSErrorWithoutCode(`EIP-8141 frame transaction invalid: ${invalidReason}`)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const totalGasSpent = intrinsicGas + totalFrameGasUsed
|
|
172
|
+
|
|
173
|
+
const result: RunTxResult = {
|
|
174
|
+
execResult: {
|
|
175
|
+
executionGasUsed: totalFrameGasUsed,
|
|
176
|
+
exceptionError: undefined,
|
|
177
|
+
gas: BIGINT_0,
|
|
178
|
+
gasUsed: totalFrameGasUsed,
|
|
179
|
+
returnValue: new Uint8Array(0),
|
|
180
|
+
logs: allLogs.length > 0 ? allLogs : undefined,
|
|
181
|
+
runState: undefined as any,
|
|
182
|
+
} as ExecResult,
|
|
183
|
+
totalGasSpent,
|
|
184
|
+
amountSpent: BIGINT_0,
|
|
185
|
+
receipt: undefined as any,
|
|
186
|
+
bloom: undefined as any,
|
|
187
|
+
gasRefund: BIGINT_0,
|
|
188
|
+
blockGasSpent: totalGasSpent,
|
|
189
|
+
} as RunTxResult
|
|
190
|
+
|
|
191
|
+
return result
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Default code execution for EOA accounts (no deployed code).
|
|
196
|
+
*/
|
|
197
|
+
async function runDefaultCode(
|
|
198
|
+
vm: VM,
|
|
199
|
+
tx: FrameEIP8141Tx,
|
|
200
|
+
execState: FrameExecutionState,
|
|
201
|
+
frame: ParsedFrame,
|
|
202
|
+
_frameCaller: Address,
|
|
203
|
+
frameTarget: Address,
|
|
204
|
+
gasPrice: bigint,
|
|
205
|
+
block: Block | undefined,
|
|
206
|
+
): Promise<FrameResult> {
|
|
207
|
+
if (frame.mode === FRAME_MODE.VERIFY) {
|
|
208
|
+
return runDefaultCodeVerify(vm, tx, execState, frame, frameTarget)
|
|
209
|
+
}
|
|
210
|
+
if (frame.mode === FRAME_MODE.SENDER) {
|
|
211
|
+
return runDefaultCodeSender(vm, tx, execState, frame, frameTarget, gasPrice, block)
|
|
212
|
+
}
|
|
213
|
+
return { status: 0, gasUsed: frame.gasLimit, returnValue: new Uint8Array(0) }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Default code VERIFY mode: ECDSA signature verification + APPROVE.
|
|
218
|
+
*/
|
|
219
|
+
async function runDefaultCodeVerify(
|
|
220
|
+
vm: VM,
|
|
221
|
+
tx: FrameEIP8141Tx,
|
|
222
|
+
execState: FrameExecutionState,
|
|
223
|
+
frame: ParsedFrame,
|
|
224
|
+
frameTarget: Address,
|
|
225
|
+
): Promise<FrameResult> {
|
|
226
|
+
const gasUsed = ECRECOVER_GAS + DEFAULT_CODE_OVERHEAD
|
|
227
|
+
if (frame.gasLimit < gasUsed) {
|
|
228
|
+
return { status: 0, gasUsed: frame.gasLimit, returnValue: new Uint8Array(0) }
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (!equalsBytes(frameTarget.bytes, tx.sender.bytes)) {
|
|
232
|
+
return { status: 0, gasUsed, returnValue: new Uint8Array(0) }
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (frame.data.length < 1) {
|
|
236
|
+
return { status: 0, gasUsed, returnValue: new Uint8Array(0) }
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const firstByte = frame.data[0]
|
|
240
|
+
const scope = (firstByte >> 4) & 0xf
|
|
241
|
+
const sigType = firstByte & 0xf
|
|
242
|
+
|
|
243
|
+
if (sigType === 0x0) {
|
|
244
|
+
if (frame.data.length !== 66) {
|
|
245
|
+
return { status: 0, gasUsed, returnValue: new Uint8Array(0) }
|
|
246
|
+
}
|
|
247
|
+
const v = frame.data[1]
|
|
248
|
+
const r = frame.data.slice(2, 34)
|
|
249
|
+
const s = frame.data.slice(34, 66)
|
|
250
|
+
const dataWithoutSig = frame.data.slice(0, 1)
|
|
251
|
+
const sigHash = tx.getHashedMessageToSign()
|
|
252
|
+
const keccakFn = vm.common.customCrypto.keccak256 ?? keccak_256
|
|
253
|
+
const hash = keccakFn(concatBytes(sigHash, dataWithoutSig))
|
|
254
|
+
|
|
255
|
+
let recoveredAddress: Address
|
|
256
|
+
try {
|
|
257
|
+
const pubKey = ecrecover(hash, BigInt(v), r, s)
|
|
258
|
+
recoveredAddress = new Address(publicToAddress(pubKey))
|
|
259
|
+
} catch {
|
|
260
|
+
return { status: 0, gasUsed, returnValue: new Uint8Array(0) }
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (!equalsBytes(recoveredAddress.bytes, frameTarget.bytes)) {
|
|
264
|
+
return { status: 0, gasUsed, returnValue: new Uint8Array(0) }
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
return { status: 0, gasUsed, returnValue: new Uint8Array(0) }
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const approveResult = await executeApprove(vm, tx, execState, frameTarget, scope)
|
|
271
|
+
if (!approveResult) {
|
|
272
|
+
return { status: 0, gasUsed, returnValue: new Uint8Array(0) }
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
execState.approveCalledInCurrentFrame = true
|
|
276
|
+
return { status: 1, gasUsed, returnValue: new Uint8Array(0) }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Default code SENDER mode: decode RLP calls and execute them.
|
|
281
|
+
*/
|
|
282
|
+
async function runDefaultCodeSender(
|
|
283
|
+
vm: VM,
|
|
284
|
+
tx: FrameEIP8141Tx,
|
|
285
|
+
_execState: FrameExecutionState,
|
|
286
|
+
frame: ParsedFrame,
|
|
287
|
+
frameTarget: Address,
|
|
288
|
+
gasPrice: bigint,
|
|
289
|
+
block: Block | undefined,
|
|
290
|
+
): Promise<FrameResult> {
|
|
291
|
+
if (frame.data.length < 1) {
|
|
292
|
+
return { status: 0, gasUsed: frame.gasLimit, returnValue: new Uint8Array(0) }
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const firstNibble = (frame.data[0] >> 4) & 0xf
|
|
296
|
+
if (firstNibble !== 0x0) {
|
|
297
|
+
return { status: 0, gasUsed: frame.gasLimit, returnValue: new Uint8Array(0) }
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (!equalsBytes(frameTarget.bytes, tx.sender.bytes)) {
|
|
301
|
+
return { status: 0, gasUsed: frame.gasLimit, returnValue: new Uint8Array(0) }
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const rlpData = frame.data.slice(1)
|
|
305
|
+
let calls: any[]
|
|
306
|
+
try {
|
|
307
|
+
const decoded = RLP.decode(rlpData)
|
|
308
|
+
calls = decoded as any[]
|
|
309
|
+
} catch {
|
|
310
|
+
return { status: 0, gasUsed: frame.gasLimit, returnValue: new Uint8Array(0) }
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let remainingGas = frame.gasLimit
|
|
314
|
+
let totalGasUsed = BIGINT_0
|
|
315
|
+
const allLogs: any[] = []
|
|
316
|
+
|
|
317
|
+
for (const call of calls) {
|
|
318
|
+
if (!Array.isArray(call) || call.length < 3) {
|
|
319
|
+
return { status: 0, gasUsed: frame.gasLimit, returnValue: new Uint8Array(0) }
|
|
320
|
+
}
|
|
321
|
+
const [callTargetBytes, callValueBytes, callDataBytes] = call as [
|
|
322
|
+
Uint8Array,
|
|
323
|
+
Uint8Array,
|
|
324
|
+
Uint8Array,
|
|
325
|
+
]
|
|
326
|
+
const callTarget = new Address(callTargetBytes)
|
|
327
|
+
const callValue = callValueBytes.length === 0 ? BIGINT_0 : bytesToBigInt(callValueBytes)
|
|
328
|
+
const callData = callDataBytes ?? new Uint8Array(0)
|
|
329
|
+
|
|
330
|
+
const result = await vm.evm.runCall({
|
|
331
|
+
block,
|
|
332
|
+
gasPrice,
|
|
333
|
+
caller: tx.sender,
|
|
334
|
+
gasLimit: remainingGas,
|
|
335
|
+
to: callTarget,
|
|
336
|
+
value: callValue,
|
|
337
|
+
data: callData,
|
|
338
|
+
origin: tx.sender,
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
const callGasUsed = result.execResult.executionGasUsed
|
|
342
|
+
totalGasUsed += callGasUsed
|
|
343
|
+
remainingGas = remainingGas > callGasUsed ? remainingGas - callGasUsed : BIGINT_0
|
|
344
|
+
|
|
345
|
+
if (result.execResult.logs) {
|
|
346
|
+
allLogs.push(...result.execResult.logs)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (result.execResult.exceptionError) {
|
|
350
|
+
return { status: 0, gasUsed: frame.gasLimit, returnValue: new Uint8Array(0) }
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
status: 1,
|
|
356
|
+
gasUsed: totalGasUsed,
|
|
357
|
+
returnValue: new Uint8Array(0),
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Execute APPROVE logic directly (for default code, not via opcode).
|
|
363
|
+
* Returns true on success, false if conditions fail.
|
|
364
|
+
*/
|
|
365
|
+
async function executeApprove(
|
|
366
|
+
vm: VM,
|
|
367
|
+
tx: FrameEIP8141Tx,
|
|
368
|
+
execState: FrameExecutionState,
|
|
369
|
+
target: Address,
|
|
370
|
+
scope: number,
|
|
371
|
+
): Promise<boolean> {
|
|
372
|
+
const stateManager = vm.stateManager
|
|
373
|
+
|
|
374
|
+
if (scope === 0x0) {
|
|
375
|
+
if (execState.senderApproved) return false
|
|
376
|
+
if (!equalsBytes(target.bytes, tx.sender.bytes)) return false
|
|
377
|
+
execState.senderApproved = true
|
|
378
|
+
return true
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (scope === 0x1) {
|
|
382
|
+
if (execState.payerApproved) return false
|
|
383
|
+
if (!execState.senderApproved) return false
|
|
384
|
+
let account = await stateManager.getAccount(target)
|
|
385
|
+
if (account === undefined) account = new Account()
|
|
386
|
+
if (account.balance < execState.totalGasCost + execState.totalBlobGasCost) return false
|
|
387
|
+
account.nonce += BIGINT_1
|
|
388
|
+
account.balance -= execState.totalGasCost + execState.totalBlobGasCost
|
|
389
|
+
await vm.evm.journal.putAccount(target, account)
|
|
390
|
+
execState.payerApproved = true
|
|
391
|
+
execState.payer = target
|
|
392
|
+
return true
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (scope === 0x2) {
|
|
396
|
+
if (execState.senderApproved || execState.payerApproved) return false
|
|
397
|
+
if (!equalsBytes(target.bytes, tx.sender.bytes)) return false
|
|
398
|
+
let account = await stateManager.getAccount(target)
|
|
399
|
+
if (account === undefined) account = new Account()
|
|
400
|
+
if (account.balance < execState.totalGasCost + execState.totalBlobGasCost) return false
|
|
401
|
+
execState.senderApproved = true
|
|
402
|
+
account.nonce += BIGINT_1
|
|
403
|
+
account.balance -= execState.totalGasCost + execState.totalBlobGasCost
|
|
404
|
+
await vm.evm.journal.putAccount(target, account)
|
|
405
|
+
execState.payerApproved = true
|
|
406
|
+
execState.payer = target
|
|
407
|
+
return true
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return false
|
|
411
|
+
}
|