I was wondering about this exact behaviour the other day but didn't have time to investigate it. The short answer is that Javascript itself doesn't allow for function overloading, so when the contract object is being built from the ABI, functions of the same name get overwritten.
...
The following 'homosignet' (?) contract contains three overloaded functions of the same name, the same JS argument type typeof "0x123" == "string"
but with different ABI argument types, string
, bytes
, address
contract homosignet {
function f(string s) constant returns (string){ return "was string";}
function f(bytes b) constant returns (string){ return "was bytes";}
function f(address a) constant returns (string){return "was address";}
}
We can see in Remix that the three are unique ABI functions having unique function signatures of:
fc68521a f(address)
d45754f8 f(bytes)
91e145ef f(string)
And that all three functions return the expected strings of "was string", "was bytes" and "was address".
However, if I hook the contract using web3.js and call the functions of the resulting JS object from the console, as in the following testcode and case:
<html> <head><script src="web3.js"></script>
<script>
web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
var homosignetContract = web3.eth.contract([{"constant":true,"inputs":[{"name":"s","type":"string"}],"name":"f","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"b","type":"bytes"}],"name":"f","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"}]);
k = homosignetContract.at("0x3b3d3de7a3271f0f6bea0b6b99e6f26a64dd3617");
</script></head></html>
> k.f(123)
"was string"
> k.f(0x123)
"was string"
> k.f("0x123")
"was string"
> k.f("test")
"was string"
The return values are all the same and in this case, all arguments are being parsed as type string
, even the number literal.
This makes sense because Javascript does not allow function overloading and when you look at the contract object, indeed only one k.f()
exists.
So it seems that calling overloaded functions requires a low level call with pre-encoded call object :
web3.eth.call(callObject [, defaultBlock] [, callback])
Which looks something like this when calling f(bytes b)
. Then you need to decode the raw return data.
web3.eth.call({to:"0x7db0be9069df82feac77f810b26a6d8011b89a8c", data:"d45754f80000000000000000000000000000000000000000000000000000000000000123"})
You can create the contract instance using only the ABI, which is the only required field when you create the contract. web3.eth.Contract(JSON.parse(abi))
. You don't have the contract address yet, since you're deploying it, so don't need to be provided. The third parameter, options, define the global options for that contract; the values provided are used as fallback for calls and transactions.
To answer to your second point, the redundancy is because the options when you create the contract are there as default settings and fallback, while you can still specify your own in any call/transaction.
let contract = new web3.eth.Contract(JSON.parse(abi)));
await contract.deploy({
data: "0x" + bin,
arguments: contractArgs
})
.send({
from: MY_ADDRESS,
gasPrice: '1000', gas: 2310334
});
so your code could be simplified in this way.
Best Answer
That's because they are not the same thing.
When you execute the
getCode(...)
function, you get the deployed bytecode of a specific address, as the docs says. The bytecode on the blockchain is the result of the execution of the compiled bytecode of your contract, which includes initialization code.About the contract you provide, which is verified:
await web3.eth.getCode(0xdac17f958d2ee523a2206206994597c13d831ec7)
Returns the bytecode of the deployed contract.
This is the
input
of the transaction that creates the contract, as you can see here in theInput Data
field. This bytecode includes initialization code and will result in the bytecode deployed on the blockchain.Let's see an example about a non verified contract.
With:
web3.eth.getCode("0x004c8981FdDA3219d4F1319a50b2EfC9F52D36B3")
What you will get is the bytecode of the contract on the blockchain:
https://rinkeby.etherscan.io/address/0x004c8981FdDA3219d4F1319a50b2EfC9F52D36B3#code
Which is not the same as the input of the transaction that creates the contract, which is the
contract creation code
.https://rinkeby.etherscan.io/tx/0xea40ca2f6be3f146b7a4f80c97319e6658f5cc1118330f4b4bf76a7df10c6f0f
Probably here is explained better: