truffle
truffle copied to clipboard
Add feature to deploy a contract at a specific address
I am developing a contract, which can be easily developed using Truffle Suite. But I am having a problem when I am writing unit tests. Here is the pseudo-code of the function.
function(bytes calldata signature) external {
bytes32 hash = keccak256(
abi.encodePacked(
a,
b,
c,
address(this)
)
);
address recovered = ECDSA.recover(hash, signature);
}
But then I run into a problem, I can't know the address of the contract in advance when I generate the signature, the address of the contract will be different for different people, on different computers when I run the unit test.
I think we need such a feature to facilitate writing unit tests.
For example.
MyContract.fixedAddress(myAddress);
The address we get when we call address(this)
inside the contract is the address we configured. It is also convenient to use Java code or other languages to generate this signature. 🤔
@leiiiooo thanks for submitting this issue. Are you looking for a way to find out the deployment address of the contract in advance or are you looking to control a deployment address? Please specify more precisely what you intend to be able to do.
Thank you for your reply. I am looking for a way to control a deployment address because as shown in the code above, I need to know the deployment address of this contract in advance because I need to calculate this signature in java code and I need to run this part of the logic check in unit tests, so it would be better to provide a mock method, for example using Truffle. mockContractAddress(myAddress)
. That way this contract address is known and controllable even if I allow as many unit tests as I want.
Hey @leiiiooo, you can determine the address at which a contract will be deployed if you know the address and nonce of the account that is sending the contract creation transaction. See https://ethereum.stackexchange.com/a/761/44640 for how to do this
As an alternative, as of Ganache 7.0.3 you can use a new RPC method, evm_setAccountCode
, to set the contract code at a specific address. See https://github.com/trufflesuite/ganache/releases/tag/v7.0.3#user-content-7.0.3-new-features for details.
For now, we'll leave this feature request issue open in order to gauge community interest in the idea.
Let us know if you have any other questions.
@davidmurdoch thank you very much for your reply, I think this proposal (https://github.com/trufflesuite/ganache/pull/2337) is very interesting and necessary to make it very easy to write some unit test code. I will try to use Ganache v7.0.3. Thanks again for your reply.
Here is my test code, I am trying to understand the meaning of Contract.at()
function.
As you can see, I have insert the code to mockAddress
, but when I call the Hello.at(mockAddress)
function, it throws exception Error: Cannot create instance of Hello; no code at address 0xe2d3A739EFFCd3A99387d015E260eEFAc72EBea1
. Feel really strange. Any idea, where am I wrong?
const Hello = artifacts.require("hello/Hello");
contract("Hello test code", async function () {
it("Contract.at", async function () {
const mockAddress = "0xe2d3A739EFFCd3A99387d015E260eEFAc72EBea1";
const code =
"0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea264697066735822122017a2ce05533e864d9273119636089adc9ee39f4de7f45ce1cf3d069a136fe25d64736f6c634300080e0033";
const setStatus = await provider.request({
method: "evm_setAccountCode",
params: [mockAddress, code],
});
assert.strictEqual(setStatus, true);
const getCode = await provider.request({
method: "eth_getCode",
params: [mockAddress],
});
assert.strictEqual(code, getCode);
let mockContract = await Hello.at(mockAddress);
});
});
Error: Cannot create instance of Hello; no code at address 0xe2d3A739EFFCd3A99387d015E260eEFAc72EBea1
at Object.checkCode (node_modules/truffle/build/webpack:/packages/contract/lib/utils/index.js:272:1)
at Function.at (node_modules/truffle/build/webpack:/packages/contract/lib/contract/constructorMethods.js:73:1)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at Context.<anonymous> (test/merchant/Hello.test.js:21:24)
It looks like the Contract.at() function, is not very easy to use. I think it would be more convenient if the new function could be extended. Contract.new([params],{targetAddress: "0xaddress"})
Hi @leiiiooo, I don't think Contract.at
is where the problem is. You can use web3.eth.getCode
to verify that evm_setAccountCode
succeeded before calling Contract.at
.
Could you try this and let me know if it works? Thanks!
await web3.currentProvider.send({
method: "evm_setAccountCode",
params: [addr, bytecode]
})
web3
is available in the test context with the correct provider.
@cliffoo Hi, here is the code for your mentioned function. In the first step, I can read the code and print it, second step, I try to call evm_setAccountCode
after that I try to call getCode
again, seems nothing happened. Always 0x
it("byte code test", async function () {
let helloInstance: HelloInstance = await Hello.new();
let address: string = helloInstance.address;
let addressCode = await web3.eth.getCode(address);
console.log("addressCode: --- " + addressCode);
let newAddress = "0xddfabcdc4d8ffc6d5beaf154f18b778f892a0740";
await (web3.currentProvider as any).send({
method: "evm_setAccountCode",
params: [newAddress, addressCode],
});
let newAddressCode = await web3.eth.getCode(newAddress);
console.log("newAddressCode: --- " + newAddressCode);
});
Contract: Hello test code
addressCode: --- 0x608060405234801561001057600080fd5b50600436106100415760003560e01c8063594f4d4a14610046578063b954e8a814610076578063fd5afcc114610092575b600080fd5b610060600480360381019061005b9190610134565b6100c2565b60405161006d919061017c565b60405180910390f35b610090600480360381019061008b9190610134565b6100c9565b005b6100ac60048036038101906100a79190610134565b6100cc565b6040516100b9919061017c565b60405180910390f35b6000919050565b50565b600060019050919050565b600080fd5b60007fffffffffffffffff00000000000000000000000000000000000000000000000082169050919050565b610111816100dc565b811461011c57600080fd5b50565b60008135905061012e81610108565b92915050565b60006020828403121561014a576101496100d7565b5b60006101588482850161011f565b91505092915050565b60008115159050919050565b61017681610161565b82525050565b6000602082019050610191600083018461016d565b9291505056fea2646970667358221220d77f282d34daca82dc75a8eeb088d3d83938a7e2bd8bdadc419ea177d026788f64736f6c634300080e0033
1) byte code test
newAddressCode: --- 0x
2) byte code test
> No events were emitted
What version of ganache are you using?
Here are my deps. 🤔
"devDependencies": {
"@openzeppelin/test-helpers": "0.5.15",
"@typechain/truffle-v5": "8.0.0",
"@types/bn.js": "5.1.0",
"@types/chai": "4.3.1",
"@types/mocha": "9.1.1",
"@types/node": "^17.0.40",
"@types/shelljs": "0.8.11",
"chai": "4.3.6",
"ganache": "7.0.5",
"mocha": "10.0.0",
"shelljs": "0.8.5",
"solidity-coverage": "0.8.0-rc.1",
"ts-node": "10.8.1",
"typechain": "8.0.0",
"typescript": "4.7.3"
},
"dependencies": {
"@openzeppelin/contracts": "4.6.0",
"solc": "0.8.14",
"truffle": "5.5.15",
"truffle-flattener": "^1.6.0"
}
Can you update Ganache to the latest version, 7.2.0, and try again?
Sad, the same.
> Everything is up to date, there is nothing to compile.
Contract: Hello test code
addressCode: --- 0x608060405234801561001057600080fd5b50600436106100415760003560e01c8063594f4d4a14610046578063b954e8a814610076578063fd5afcc114610092575b600080fd5b610060600480360381019061005b9190610134565b6100c2565b60405161006d919061017c565b60405180910390f35b610090600480360381019061008b9190610134565b6100c9565b005b6100ac60048036038101906100a79190610134565b6100cc565b6040516100b9919061017c565b60405180910390f35b6000919050565b50565b600060019050919050565b600080fd5b60007fffffffffffffffff00000000000000000000000000000000000000000000000082169050919050565b610111816100dc565b811461011c57600080fd5b50565b60008135905061012e81610108565b92915050565b60006020828403121561014a576101496100d7565b5b60006101588482850161011f565b91505092915050565b60008115159050919050565b61017681610161565b82525050565b6000602082019050610191600083018461016d565b9291505056fea2646970667358221220d77f282d34daca82dc75a8eeb088d3d83938a7e2bd8bdadc419ea177d026788f64736f6c634300080e0033
1) byte code test
newAddressCode: --- 0x
2) byte code test
> No events were emitted
0 passing (521ms)
2 failing
1) Contract: Hello test code
byte code test:
Uncaught TypeError: callback is not a function
at /Users/user/leiiiooo/project/android/company/R2-D2/node_modules/truffle/build/webpack:/packages/provider/wrapper.js:119:1
at XMLHttpRequest.request.onreadystatechange (node_modules/truffle/build/webpack:/node_modules/web3/node_modules/web3-providers-http/lib/index.js:98:1)
at XMLHttpRequestEventTarget.dispatchEvent (node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:1)
at XMLHttpRequest.exports.modules.996763.XMLHttpRequest._setReadyState (node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:208:1)
at XMLHttpRequest.exports.modules.996763.XMLHttpRequest._onHttpResponseEnd (node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:318:1)
at IncomingMessage.<anonymous> (node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:289:48)
at IncomingMessage.emit (node:events:402:35)
at endReadableNT (node:internal/streams/readable:1343:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21)
2) Contract: Hello test code
byte code test:
Error: done() called multiple times in test <Contract: Hello test code byte code test> of file /Users/user/leiiiooo/project/android/company/R2-D2/test/MerchantWallet.test.js
at processTicksAndRejections (node:internal/process/task_queues:96:5)
Hey @leiiiooo, so you need to provide a callback to web3.currentProvider.send
as the second param.
Here's a working example that wraps it and returns a promise. Also as David mentioned, make sure you're running the latest version of Ganache. Give it a try!
Yep, your codes are acceptable. 🤔 MAY be the root cause is the plugin solidity-coverage
. It does not support the newest version of Ganache. In my code, when I call evm_setAccountCode
, it will give me undefined
. @cgewecke Do you have any good ideas about this? 🤔
Closing for Issue Maintenance.