Tracking Contracts Across Shardeum and Multiple Chains
This tutorial will guide you on how to keep track of the same contracts across multiple chains and Shardeum. Check this...
This tutorial will guide you on how to keep track of the same contracts across multiple chains and Shardeum. Check this...
Having contracts deployed at the same address cross-chain makes it very easy to find contracts for:
-users who want to quickly find the same contracts on other chains
-developers maintaining the same contracts across different chains
Simple methods:
-a new wallet (nonce = 0 for all chains)
-a wallet used for deploying contracts with the same cross chain nonce values
CAUTION
Using the same wallet again will require you to keep track of nonce values cross chain.
Alchemy recommends using a Hardhat nonce query script to quickly check a wallet’s nonce values cross-chain:
Create a Hardhat task to query the nonce
Smart contracts deployment addresses can be created with either opcodes:
CREATE
or
CREATE2
CREATE deployment addresses depend on the senders:
-address
-nonce
CREATE2 deployment addresses depend on:
-sender address (CREATE2 Factory is the sender in this context to execute the CREATE2 opcode)
-salt
-byteCode [creationCode since constructor input arguments can change the byteCode value]
For more info on the CREATE2 opcode:
Yes (for some contracts).
Do the following steps:
WARNING
Calling SELFDESTRUCT will delete all data in a contract! Be very careful using it. Goerli supports SELFDESTRUCT while Shardeum currently does not. SELFDESTRUCT might also be deprecated with EIP-4758: https://eips.ethereum.org/EIPS/eip-4758.
CAUTION
Remember this CREATE2 Factory address and the salt you used for the contract byteCode deployed.
Remember this CREATE2 Factory address and the salt you used for the contract byteCode deployed.
WARNING
If you skip this step 3, you will see the following error on the Shardeum explorer:
create collison
Doing this will kill a contract at an address, then bring it back to life at the same address.
Examples:
Contract deployed with CREATE that did a SELFDESTRUCT
This factory is useful for:
-saving gas:
-factory can be used for other contracts since it takes raw creationCode
-constructors cannot be payable (callvalue() [msg.value in assembly] set to 0, saves gas)
-constructors cannot have arguments (memory variables not used saves gas)
-creationCode == runtimeCode, since there are no constructor arguments changing the byteCode
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
error notOwner();
contract ContractToDeployMetamask { //Bytecode matches creation code with no constructor input arguments. Bytecode with tutorial address 0xc1202e7d42655F23097476f6D48006fE56d38d4f:
//0x60a060405234801561001057600080fd5b5073c1202e7d42655f23097476f6d48006fe56d38d4f73ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff16815250506080516102c96100806000396000818160bb0152818160df015261016401526102c96000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806321cf146c14610051578063b4a99a4e1461006f578063f81bfa3f1461008d578063f965e32e14610097575b600080fd5b6100596100b3565b60405161006691906101be565b60405180910390f35b6100776100b9565b604051610084919061021a565b60405180910390f35b6100956100dd565b005b6100b160048036038101906100ac9190610266565b61019b565b005b60005481565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610162576040517f251c9d6300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16ff5b8060008190555050565b6000819050919050565b6101b8816101a5565b82525050565b60006020820190506101d360008301846101af565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610204826101d9565b9050919050565b610214816101f9565b82525050565b600060208201905061022f600083018461020b565b92915050565b600080fd5b610243816101a5565b811461024e57600080fd5b50565b6000813590506102608161023a565b92915050565b60006020828403121561027c5761027b610235565b5b600061028a84828501610251565b9150509291505056fea2646970667358221220f13ed2f135ac0e33e3f372e325eaa526fa74eefc16e73495164e7b2549f0974464736f6c63430008110033
uint public storageSlot0;
address public immutable Owner;
constructor() { // Put your address as Owner instead of msg.sender because msg.sender will be another contract with CREATE2 salt deploy.
Owner = 0xc1202e7d42655F23097476f6D48006fE56d38d4f; // Metamask address for tutorial.
} // Do not do tx.origin because someone can front run you during contract creation!
function changeValue(uint updateStorageSlot) public {
storageSlot0 = updateStorageSlot;
}
function killThisContract() public { //After a contract is killed at an address, you can deploy another contract at that address.
if(msg.sender != Owner) { revert notOwner(); }
selfdestruct(payable(Owner)); //Deletes all data from this contract address. Address inside selfdestruct call gets all ether in the contract self destructing.
}
}
contract Create2Factory {
event create2Event(address deployedAddressEvent);
function generalDeployCreate2(uint _salt, bytes memory creationCode) public { // BYTECODE NEEDS TO HAVE "0X" PREFIX TO BUILD RLP!
address deployedAddress; // Salt example: 0x0000000000000000000000000000000000000000000000000000000000000000
assembly { // Another salt example: 0x123456789abcdef0000000000000000000000000000000000000000000000000
deployedAddress := create2(0, add(creationCode, 0x20), mload(creationCode), _salt) //callvalue() is the same as msg.value in assembly. callvalue() == 0 to save gas since the constructor and this create2 function are not payable.
if iszero(extcodesize(deployedAddress)) { //Note: constructor is not payable and takes no input variables (hardcoded in) to keep the creation code simple for this example.
revert(0, 0)
}
}
emit create2Event(deployedAddress);
}
function generalPrecomputeAddress(uint _salt, bytes memory creationCode) public view returns (address) { // BYTECODE NEEDS TO HAVE "0X" PREFIX TO BUILD RLP!
bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(creationCode)));
return address(uint160(uint(hash)));
}
}
This factory is useful for:
-building creationCode directly inside CREATE2 Factory functions with constructor argument values
-keeping contract deployment simple without needing to deploy other contracts with general byteCode
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
error notOwner();
contract ContractToDeploy {
uint public storageSlot0;
address public immutable Owner;
constructor(address inputAddress) payable {
Owner = inputAddress; //Need to put address as input instead of msg.sender because msg.sender will be another contract with CREATE2 salt deploy.
}
function changeValue(uint updateStorageSlot) public {
storageSlot0 = updateStorageSlot;
}
function killThisContract() public { //After a contract is killed at an address, you can deploy another contract at that address.
if(msg.sender != Owner) { revert notOwner(); }
selfdestruct(payable(Owner)); //Deletes all data from this contract.
}
}
contract Create2Factory { //Modified from: "Create2 | Solidity 0.8" by Smart Contract Programmer https://www.youtube.com/watch?v=883-koWrsO4&ab_channel=SmartContractProgrammer
event create2Event(address deployedAddressEvent);
function create2DeployContract(bytes32 _salt, address ownerAddress) public payable { // Salt example: 0x0000000000000000000000000000000000000000000000000000000000000000
ContractToDeploy deployedAddress = new ContractToDeploy {value: msg.value, salt: _salt}(ownerAddress); // Another salt example: 0x123456789abcdef0000000000000000000000000000000000000000000000000
emit create2Event(address(deployedAddress));
}
function precomputeAddress(uint _salt, address ownerAddress) public view returns (address) {
bytes memory creationCodeValue = abi.encodePacked(type(ContractToDeploy).creationCode, abi.encode(ownerAddress));
bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(creationCodeValue)));
return address(uint160(uint(hash)));
}
}