diff --git a/.gitignore b/.gitignore index f13e4cb..63b4ed8 100644 --- a/.gitignore +++ b/.gitignore @@ -14,8 +14,9 @@ coverage/ coverage.json yarn-error.log typechain-types +.aptos # Certora Formal Verification related files .certora_internal .certora_recent_jobs.json -.zip-output-url.txt \ No newline at end of file +.zip-output-url.txt diff --git a/README.md b/README.md index 15213c6..8e1fc75 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ The application uses several environment variables, which should be set in a `.e - `EVM_PRECOMPILE_CONTRACT`: The address of the EVM precompile contract. - `MOVE_FRAMEWORK`: The address of the Move framework. -## Steps for `evm-to-move.js` +## Steps for `mevm-to-aptosvm.js` 1. Create a Gnosis Safe contract using the EVM wallet. 2. Create a multisig account using the Move wallet, and set the Gnosis Safe contract created in step 1 as one of the owners. @@ -59,10 +59,10 @@ The application uses several environment variables, which should be set in a `.e 6. Execute the transaction created in step 4 using the EVM wallet. 7. Check whether the multisig account has voted successfully. -## Steps for `move-to-evm.js` +## Steps for `aptosvm-to-mevm.js` 1. Create a NumberRegistry.sol contract using the EVM wallet. -2. Call the setNumber function of the NumberRegistry contract using the Move wallet. +2. Call the setNumber function of the NumberRegistry contract using the Move wallet or using the Move Contract(you must compile the and publish the move package as the demo contract in move-contract). 3. Check whether the number is set successfully. ## Note diff --git a/aptosvm-to-mevm.js b/aptosvm-to-mevm.js index 2d6de01..85fd450 100644 --- a/aptosvm-to-mevm.js +++ b/aptosvm-to-mevm.js @@ -8,11 +8,15 @@ const client = new AptosClient(process.env.MOVEMENT_RPC_ENDPOINT); let pk = process.env.MOVE_PRIVATE_KEY; let owner = new AptosAccount(new HexString(pk).toUint8Array()) +const fs = require("fs"); +const path = require("path"); + const web3Provider = process.env.EVM_RPC_ENDPOINT const provider = getDefaultProvider(web3Provider) const wallet = new ethers.Wallet(process.env.ETHEREUM_PRIVATE_KEY, provider); const account = wallet.connect(provider); const data = require("./NumberRegistry.json") +const moveModule = "./move-contract"; async function deployNumberRegistry() { const factory = new ContractFactory(data.abi, data.bytecode, account); @@ -22,6 +26,15 @@ async function deployNumberRegistry() { return contract } +async function deployMovePackage() { + const moduleData = fs.readFileSync(path.join(moveModule, "build", "CallEVMDemo", "bytecode_modules", "demo.mv")); + const packageMetadata = fs.readFileSync(path.join(moveModule, "build", "CallEVMDemo", "package-metadata.bcs")); + let txnHash = await client.publishPackage(owner, new HexString(packageMetadata.toString("hex")).toUint8Array(), [ + new TxnBuilderTypes.Module(new HexString(moduleData.toString("hex")).toUint8Array()), + ]); + await client.waitForTransaction(txnHash, { checkSuccess: true }); +} + // Function to submit a transaction async function submitTx(rawTxn) { const bcsTxn = await client.signTransaction(owner, rawTxn); @@ -42,8 +55,24 @@ async function getNonce(addr) { } } +async function setNumberByMoveContract(contract) { + let interface = new ethers.utils.Interface(data.abi); + + // 1. Encodes the EVM function + let calldata = interface.encodeFunctionData("setNumber", [200]); + + // 2. Generates the AptosVM transaction that interacts with the EVM contract + let txn = await client.generateTransaction(owner.address(), { + function: `${owner.address()}::demo::call_evm`, + type_arguments: [], + arguments: [new HexString(contract.address).toUint8Array(), new HexString(calldata).toUint8Array(), BCS.bcsSerializeU256(0)], + }); + + console.log(`setting number tx ${await submitTx(txn)}`); +} + // Function to set the number -async function setNumber(contract) { +async function setNumberByWallet(contract) { let interface = new ethers.utils.Interface(data.abi); // 1. Encodes the EVM function @@ -63,13 +92,18 @@ async function setNumber(contract) { } async function run() { - await getNonce(owner.address()) let contract = await deployNumberRegistry(); let number = await contract.number(); console.log(`number before setting ${number}`) - await setNumber(contract) + await setNumberByWallet(contract) + number = await contract.number(); + console.log(`number after setting by wallet ${number}`) + + await deployMovePackage(); // only need to deploy once + await setNumberByMoveContract(contract); number = await contract.number(); - console.log(`number after setting ${number}`) + console.log(`number after setting by contract ${number}`) + } run().then() diff --git a/move-contract/Move.toml b/move-contract/Move.toml new file mode 100644 index 0000000..4b80b5f --- /dev/null +++ b/move-contract/Move.toml @@ -0,0 +1,27 @@ +[package] +name = "CallEVMDemo" +version = "1.0.0" + + +[addresses] +call_evm_demo = "_" + +#[dependencies.AptosFramework] +#git = 'https://github.com/aptos-labs/aptos-core' +#rev = 'devnet' +#subdir = 'aptos-move/framework/aptos-framework' +# +#[dependencies.AptosStdlib] +#git = 'https://github.com/aptos-labs/aptos-core' +#rev = 'devnet' +#subdir = 'aptos-move/framework/aptos-stdlib' + +[dependencies.AptosFramework] +git = 'https://github.com/shaokun11/move-me2' +rev = 'acc184f9af236ac318b8235ea61fc6669a8a3b79' +subdir = 'aptos-move/framework/aptos-framework' +# +#[dependencies.AptosStdlib] +#git = 'https://github.com/shaokun11/move-me2' +#rev = 'acf53c7de6982aee4747b1ff47908326da52487c' +#subdir = 'aptos-move/framework/aptos-stdlib' \ No newline at end of file diff --git a/move-contract/sources/demo.move b/move-contract/sources/demo.move new file mode 100644 index 0000000..49f60df --- /dev/null +++ b/move-contract/sources/demo.move @@ -0,0 +1,19 @@ +module call_evm_demo::demo { + use aptos_framework::evm::{MoveContractCap, register_move_contract, call_evm_from_move, get_nonce, get_evm_address}; + + struct ModuleCap has key { + cap: MoveContractCap + } + + fun init_module(signer: &signer) { + move_to(signer, ModuleCap { + cap: register_move_contract(signer) + }); + } + + public entry fun call_evm(to: vector, calldata: vector, value_bytes: vector) acquires ModuleCap { + let cap = borrow_global_mut(@call_evm_demo); + let nonce = get_nonce(get_evm_address(@call_evm_demo)); + call_evm_from_move(&cap.cap, nonce, to, calldata, value_bytes, 1); + } +}