-
Notifications
You must be signed in to change notification settings - Fork 1
/
simulation.ts
144 lines (136 loc) · 5.41 KB
/
simulation.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
require('dotenv').config()
import axios from 'axios'
import fs from 'fs'
import { ethers } from 'ethers'
import Ganache from 'ganache-core'
import { exit } from 'process'
import { HandlerAnalyzer, StepHandler } from '../src/analyzer'
import { CallHandler, StorageHandler } from '../src/handlers'
import { SafeInfoProvider } from '../src/info'
import { GanacheCoreConnector, GanacheV7Connector } from '../src/connectors'
import { Simulator } from '../src/simulator'
import { MultisigTransaction } from "../src/types"
import { decodeLog } from '../src/decoders/events'
import { decodeFunctionData } from '../src/decoders/functions'
import { decodeSafeStorageChange } from '../src/decoders/storages'
import { loadEventSignatures, loadFunctionSignatures } from '../src/decoders/4byte'
const loadSafeTx = async(): Promise<MultisigTransaction> => {
const safeTxFile = process.env.SAFE_TX_FILE
if (safeTxFile) {
return JSON.parse(fs.readFileSync(safeTxFile, "utf-8"))
}
const safeTxHash = process.env.SAFE_TX_HASH
if (safeTxHash) {
const serviceUrl = process.env.SERVICE_URL
if (!serviceUrl) throw Error("Require service URL to load tx by hash")
const resp = await axios.get<MultisigTransaction>(`${serviceUrl}/api/v1/multisig-transactions/${safeTxHash}`)
return resp.data
}
throw Error("Missing Safe tx information")
}
const tryDefault = async <T>(generator: (() => T | Promise<T>), fallback: T | Promise<T>): Promise<T> => {
try {
return await generator()
} catch (e) {
return fallback
}
}
async function run(): Promise<void> {
const verbose: boolean = process.env.VERBOSE === "true"
const nodeUrl = process.env.NODE_URL
const network = process.env.NETWORK!!
const options: any = { dbPath: "/", fork: nodeUrl || network, gasLimit: 100_000_000, gasPrice: "0x0", vmErrorsOnRPCResponse: false, logging: { quiet: !verbose, verbose: verbose, debug: verbose } }
const ganache = Ganache.provider(options)
const connector = new GanacheCoreConnector(ganache)
const simulator = new Simulator(connector, console.log)
const provider = new ethers.providers.Web3Provider(connector as any)
const infoProvider = new SafeInfoProvider(provider, console.log)
const safeTx = await loadSafeTx()
console.log(safeTx)
const safeInfo = await infoProvider.loadInfo(safeTx.safe)
console.log("Safe Information", safeInfo)
const callHandler = new CallHandler()
const storageHandler = new StorageHandler()
const handlers: StepHandler[] = [
callHandler,
storageHandler
]
const analyzer = new HandlerAnalyzer(handlers)
const txHash = await simulator.simulateMultiSigTransaction(safeInfo, safeTx, analyzer)
const txReceipt = await provider.getTransactionReceipt(txHash)
console.log(JSON.stringify(callHandler.roots, undefined, " "))
console.log("\n")
console.log(`Storage changes on Safe ${safeTx.safe}:`)
const safeStorageChanges = storageHandler.storageChanges.get(safeTx.safe)
if (safeStorageChanges) {
console.log("")
safeStorageChanges.forEach((change) => console.log(decodeSafeStorageChange(change)))
}
console.log("\n")
console.log("Other Storage Changes:")
for (const [address, changes] of storageHandler.storageChanges) {
if (address === safeTx.safe) continue
console.log("")
console.log("On", address)
changes.forEach((change) => console.log(change))
}
console.log()
console.log("\n")
console.log(`Calls from Safe ${safeTx.safe}:`)
const safeCalls = callHandler.calls.get(safeTx.safe)
if (safeCalls) {
console.log("")
let i = 1
for (const call of safeCalls) {
console.log(`Call ${i++}`)
console.log(call)
const decodedData = await tryDefault(() => decodeFunctionData(call.data, loadFunctionSignatures), [])
if (decodedData.length > 0) {
console.log("Decoded data:")
console.log(decodedData[0])
}
console.log("")
}
}
console.log("")
console.log("Other Calls:")
for (const [caller, calls] of callHandler.calls.entries()) {
if (caller === safeTx.safe) continue
console.log("")
console.log("From", caller)
let i = 1
for (const call of calls) {
console.log(`Call ${i++}`)
console.log(call)
const decodedData = await tryDefault(() => decodeFunctionData(call.data, loadFunctionSignatures), [])
if (decodedData.length > 0) {
console.log("Decoded data:")
console.log(decodedData[0])
}
console.log("")
}
}
console.log("")
console.log("Transaction Events:")
for (const log of txReceipt.logs) {
console.log("")
const decodedData = await tryDefault(() => decodeLog(log, loadEventSignatures), Promise.resolve([]))
if (decodedData.length === 0) {
console.log("Unknown event:")
console.log(log)
} else {
console.log("Decoded event:")
console.log(`Emitted by ${log.address}`)
console.log(decodedData[0])
}
}
console.log("\n")
console.log("Transaction Status:", txReceipt.status !== 0 ? "Success" : "Failed")
console.log("Done")
}
run()
.catch((e) => {
console.error(e)
exit(1)
})
.then(() => { exit(0) })