Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to deploy contract in w3vm? #133

Closed
temuera opened this issue Apr 16, 2024 · 17 comments
Closed

how to deploy contract in w3vm? #133

temuera opened this issue Apr 16, 2024 · 17 comments

Comments

@temuera
Copy link

temuera commented Apr 16, 2024

hi all~

can anyone give a example code for deploy contract with bytecode in w3vm?

I attempted to set the account state with bytecode, but it didn't run.

@temuera
Copy link
Author

temuera commented Apr 16, 2024

tried to use w3.Message with InputData to deploy the contract.

Simple functions can run normally, such as:

w3.MustNewFunc(`test(uint256 _in)`, "uint256")

call a Complex functions failed.

However, forked a exists deployed contract can call complex functions with same args.

@temuera
Copy link
Author

temuera commented Apr 16, 2024

Understood. The default chain ID for the VM is 1.

@temuera temuera closed this as completed Apr 16, 2024
@lmittmann
Copy link
Owner

lmittmann commented Apr 17, 2024

@temuera did you manage to deploy a contract? There is an open issue explaining how this can be done using a workaround: #112

Note, that you can also set the w3.Message Func and Args attribute instead of constructing the input data manually using Func.EncodeArgs.

Understood. The default chain ID for the VM is 1.

You can set a custom chain config using w3vm.WithChainConfig.

@temuera
Copy link
Author

temuera commented Apr 17, 2024

@lmittmann

Yeah, It works.

Just construct an account with bytecode using state, or go with tx message.

w3 is a helperfull project with great structure, But it's also brought along some inconveniences for me.

1,It's missing onSuccess and onFaild callbacks for call functions.
Even though I can work around these problems by writing more code, it's just not convenient.

2,It's not compatible with the BSC version of go-eth library.

This one's particularly annoying. If you don't merge the PR, I'll have to keep using my locally modified version with replace.

Also, I haven't found a way yet to modify the blockNumber within the same VM context.

As I continue to use it, I might come across more issues and post in this thread.

@temuera
Copy link
Author

temuera commented Apr 17, 2024

I might have some weird use cases. but communication isn't easy 4 me (I don't speak English).

Currently, I'm working on a scam token detection tool. Some scam tokens set a time bomb, where you can buy and sell tokens at BN1, but you can't sell after BN+n. That's why I'm looking for a way to switch blockNumber within the same context.

I might add a SetBlockNumber function to w3vm.VM and submit a PR.

/LOL

@lmittmann
Copy link
Owner

I might add a SetBlockNumber function to w3vm.VM and submit a PR.

you can set the block number when building the VM using w3vm.WithHeader or w3vm.WithBlockContext`

@lmittmann
Copy link
Owner

1,It's missing onSuccess and onFaild callbacks for call functions.
Even though I can work around these problems by writing more code, it's just not convenient.

see https://github.com/lmittmann/w3?tab=readme-ov-file#error-handling

@temuera
Copy link
Author

temuera commented Apr 17, 2024

VM

What I mean is, after executing one transaction (TX), then modifying the block number, and finally executing the next transaction. All these operations are carried out within the same context.

@zzispp
Copy link

zzispp commented Feb 8, 2025

Hello, I would like to ask if you have solved the problem of modifying BlockNumber on the same VM at present?

@lmittmann
Copy link
Owner

So far a VM has a fixed BlockContext, that is set during construction. Can you tell me about your use case that would require updating it?

@zzispp
Copy link

zzispp commented Feb 9, 2025

` latestBlock := BlockInfo{
number: header.Number,
header: header,
}

		nextBlock := BlockInfo{
			number:    new(big.Int).Add(header.Number, big.NewInt(1)),
			timestamp: header.Time + 12, 
			baseFee:   calculateNextBlockBaseFee(header),
		}

		
		blockCtx := &vm.BlockContext{
			CanTransfer: core.CanTransfer,
			Transfer:    core.Transfer,
			GetHash:     func(u uint64) common.Hash { return common.Hash{} },
			BlockNumber: nextBlock.number,
			Time:        nextBlock.timestamp,
			BaseFee:     nextBlock.baseFee,
			Coinbase:    common.HexToAddress("0xDecafC0FFEe15BAD000000000000000000000000"),
			Difficulty:  new(big.Int),
			GasLimit:    params.MaxGasLimit,
			Random:      &common.Hash{},
		}

		bscChainConfig := params.MainnetChainConfig
		bscChainConfig.ChainID = big.NewInt(97)
		
		newVM, err := w3vm.New(
			w3vm.WithChainConfig(bscChainConfig),
			w3vm.WithBlockContext(blockCtx),
			w3vm.WithHeader(latestBlock.header),
			w3vm.WithFetcher(w3vm.NewRPCFetcher(w3Client, latestBlock.number)),
			w3vm.WithNoBaseFee()
		)
		if err != nil {
			continue
		}`

I don't know if I have a problem writing this, but what I need is to use the state of the current block to simulate transaction execution for the next block, because I'm developing Fraud Token Detection Tools. I was looking for a way to switch blockNumbers in the same context.

@lmittmann
Copy link
Owner

That is possible and you don't need to change the blockCtx for that. In general I would recommend not to set the blockCtx manual, but instead simply use the option WithHeader, which derives the blockCtx from the header automatically. See the example https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-TraceCalls on how to simulate (and trace) a tx in the corresponding block.

Do you want to simulate in a specific block, or for a pending block?

@zzispp
Copy link

zzispp commented Feb 10, 2025

I want to simulate it in the pending block

@zzispp
Copy link

zzispp commented Feb 10, 2025

txs := make(chan *types.Transaction) sub, err := gethClient.SubscribeFullPendingTransactions(ctx, txs) if err != nil { log.Fatalf("BSC 订阅待处理交易失败: %v", err) }
After getting the pending transaction from the SubscribeFullPendingTransactions function, I fork the latest block to get the block status DB, and then execute the transaction I specified and the pending transaction of the memory pool to see if I can buy or try to sell

@zzispp
Copy link

zzispp commented Feb 10, 2025

`while let Ok(event) = new_block_receive.recv().await {
let latest_block = match event {
NewBlockEvent::NewBlock { latest_block } => latest_block,
};

            let latest_block_number = Some(
                BlockId::Number(BlockNumber::Number(latest_block.number))
            );

            // initialize an empty cache db
            let cache_db = CacheDB::new(EmptyDB::default());

            // setup the backend
            let fork_factory = ForkFactory::new_sandbox_factory(
                client.clone(),
                cache_db,
                latest_block_number
            );

            let fork_db = fork_factory.new_sandbox_fork();
            
            // update fork_db
            let mut oracle_guard = oracle.lock().await;
            oracle_guard.update_fork_db(fork_db);
            drop(oracle_guard);
        } // end of while loop`     `// ** Simulate a sell transactions to get the current amount out of weth

// ** We also use the same function here to simulate a sell after a pending tx which may have changed the state of the contract
pub fn simulate_sell(
tx: Option,
pool: Pool,
next_block: BlockInfo,
fork_db: ForkDB
) -> Result<U256, anyhow::Error> {
// setup an evm instance
let mut evm = revm::EVM::new();
evm.database(fork_db);

// setup the next block state
setup_block_state(&mut evm, &next_block);
disable_checks(&mut evm);

// if we have a pending tx simulate it
if let Some(tx) = tx.clone() {
    commit_pending_tx(&mut evm, &tx)?;
}

// ** get the token balance for the amount_in to sell
let amount_in = get_balance_of_evm(
    pool.token_1, // token1 is always a shitcoin
    get_snipe_contract_address(),
    &next_block,
    &mut evm
)?;

// ** create the call_data for the swap
let call_data = encode_swap(
    pool.token_1, // shitcoin
    pool.token_0, // weth
    pool.address,
    amount_in,
    U256::from(0u128)
);

// ** Simulate the Sell Transaction
let (reverted, logs, _) = commit_tx(
    &mut evm,
    call_data.into(),
    get_my_address(), // caller
    get_snipe_contract_address(), // transact to
    true, // apply state changes to db
    &next_block
)?;

// if the tx is reverted we return 0
// cause it will produce no logs
if reverted {
    log::warn!("Our tx is reverted, returning 0");
    return Ok(U256::zero());
}

// ** get the actual amount of weth we are going to receive from the logs
let (weth_amount, _) = get_real_amount_from_logs(
    logs,
    pool.address,
    get_swap_event(),
    get_transfer_event()
)?;

return Ok(weth_amount);

}` my approach is to refer to the rust code of a coder, which uses the current block state to simulate the transaction execution of the next block, but I want to use w3 to achieve the same function.

@lmittmann
Copy link
Owner

Your general approach looks good to me. You should use WithBlockContext OR WithHeader, as WithHeader derives the block context and overwrites the previous call to WithBlockContext.

You setup your fetcher correctly, to fetch state from the latest known block. The only thing left, is to "predict" the pending block parameters (nextHeader):

+ nextHeader := &types.Header{
+	Number: new(big.Int).Add(latestBlock.Number(), big.NewInt(1))
+	// ...
+ }

newVM, err := w3vm.New(
	w3vm.WithChainConfig(bscChainConfig),
-	w3vm.WithBlockContext(blockCtx),
-	w3vm.WithHeader(latestBlock.header),
+	w3vm.WithHeader(nextHeader),
	w3vm.WithFetcher(w3vm.NewRPCFetcher(w3Client, latestBlock.number)),
	w3vm.WithNoBaseFee()
)
if err != nil {
	continue
}

Note, you can also use w3 to subscribe to new pending transactions: https://github.com/lmittmann/w3/blob/main/example_test.go#L291-L314

@zzispp
Copy link

zzispp commented Feb 11, 2025

I understand, thank you for your reply!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants