How solidity make function signature with tuple(nested abi)? - solidity

struct Test {
uint ui;
string s;
}
function test(Test t) public {
emit Log(t.ui, t.s);
}
I have some knowledge about ABI. I made this contract with experimental ABIEncoderV2 option. In conclusion, this function's signature is 0x6056f4cc, I found this value in opcode. I tried some case test(uint256,string), test(tuple(uint256,string)), test(tuple), test(tuple[uint256,string])) with sha3... but no one make correct signature. How solidity make function signature with tuple?

You're close with the first one. The actual encoding is done from test((uint256,string)).
bytes4(keccak256("test((uint256,string))"): 6056f4cc

Related

Invalid implicit conversion from uint256[10] memory to uint256[] memory requested

So apparently we can not use dynamic arrays while using memory data location. But the following code gives me error:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract A {
uint256[] public numbers;
constructor(uint256[] memory _numbers) {
for(uint256 i=0; i<_numbers.length; i++) {
numbers.push(_numbers[i]);
}
}
function get() public view returns (uint256[] memory) {
return numbers;
}
}
contract Manager {
function makeA() public returns(uint256) {
uint256[10] memory numbers;
// push is not supported for memory data location of array
numbers[0] = 10;
A a = new A(numbers); //Error: Invalid implicit conversion from uint256[10] memory to uint256[] memory requested
return a.numbers(0);
}
}
I solved it using this syntax of declaring static array:
uint256[] memory numbers = new uint256[](5);
Although it solved the issue but I am still confused behind the concept of why the later works? My assumption is that solidity differs the type between uint256[] and uint256[10]. Correct me if I am wrong, also an explanation of this behavior will be helpful.
The error says cannot convert static array to dynamic array implicitly.
In Java, whenever you pass Integer wrapper to int or int to Integer, you don't have to explicitly convert it. the compiler does it for you. In the same way, some conversion happens at the compiler level known as implicit conventions.
In EVM, when you passed numbers[10] that says that you are declaring a static type array. In the constructor, you are pushing it into a dynamic array. compiler by default cannot convert it into unit[10] to unit[]. this is what the error says.
when you declare that with help of a new keyword it is one way to declare a dynamic array. so, the dynamic array is passed and values will be inserted into the dynamic array. so, no conversion is required.
if you feel, the answer needs to be updated please update it.
The difference is as follows:
Firstly, a static array will have its memory slots allocated when defined.
Yet dynamic arrays will require a new allocation to be issued every time you push a new element into it.
This might not seem like a big thing in a normal OOP language, but Solidity requires gas per instruction, and that's where that difference can't be ignored.

Lock transfer of erc721 NFT

Hi all I am building a blockchain-based game around an NFT project and am looking to understand if it's possible to implement the following.
Have a method on the NFT contract that when called can locks the transfer of all minted NFT's for a period. A bit like a game of tag than when your tag the contract all the (NFT / players) cant (move /transfer)
I assume I would need overide the transfer method then do a boolean check.
Something like
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
if(!isLocked){
safeTransferFrom(from, to, tokenId, "");
}
}
Will this work as I expect and is there any issues with this and would override the transfer method especially around security etc.
Sorry for such a broad question
Thanks
From the context, it seems like you're using the OpenZeppelin implementation. If that's the case, you can override their _beforeTokenTransfer() function, which is effectively called from all functions performing token transfer (as there is multiple of them).
pragma solidity ^0.8;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
contract MyCollection is ERC721 {
constructor() ERC721("MyCollection", "MyC") {
_mint(msg.sender, 1);
}
// needs to be unlocked for the `_mint()` function in constructor
bool locked = false;
function setLocked(bool _locked) external {
locked = _locked;
}
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal override {
require(!locked, "Cannot transfer - currently locked");
}
}
If you're using other or custom implementation, or chose to only override the publicly visible functions, make sure that there is no other public or external function allowing the transfer without the lock mechanism.
Also few notes to your current code:
You probably won't need the virtual keyword. It allows the function to be further overridden. Unless you're expecting to override this function as well, you can safely remove the keyword.
You're calling the parent function with the same name but different argument set. If you wanted to call the parent function with the same name and the same argument set (assuming it's defined), you'd need to use the super keyword (same as in Java): super.safeTransferFrom(from, to, tokenId);

Send array of structs to a different contract

It is mentioned here that ABIEncoderV2 should make it possible to pass structs from contract to contract. Solidity latest ABI spec mentions that it is possible to have tuple array as a input. Can you please say how array of structs / tuple[] should be sent in below example code from TupleArrayFactory to TupleArray?
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract TupleArrayFactory {
TupleArray newTuple;
function createTuple() public {
newTuple = new TupleArray("trait0", "display0", 0);
// newTuple.pushAttribute([["trait1","display2",2]]);
newTuple.pushAttribute(["trait1","display2",2]);
// TypeError: Unable to deduce common type for array elements.
}
}
contract TupleArray {
struct Attribute {
string trait_type;
string display_type;
uint8 value;
}
Attribute[] public attributes;
Attribute[] public tempAttributeArr;
constructor(string memory trait_type, string memory display_type, uint8 value) payable {
Attribute memory Attribute0 = Attribute(trait_type, display_type, value);
tempAttributeArr.push(Attribute0);
pushAttribute(tempAttributeArr);
//pushAttribute([trait_type, display_type, value]);
// TypeError: Unable to deduce common type for array elements.
}
function pushAttribute(Attribute[] memory _attr) public payable {
attributes.push(_attr[0]);
}
}
Here's a working version of your example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract TupleArrayFactory {
TupleArray newTuple;
function createTuple() public {
newTuple = new TupleArray("trait0", "display0", 0);
TupleArray.Attribute[] memory attributes = new TupleArray.Attribute[](1);
attributes[0] = TupleArray.Attribute("trait1", "display2", 2);
newTuple.pushAttribute(attributes);
}
}
contract TupleArray {
struct Attribute {
string trait_type;
string display_type;
uint8 value;
}
Attribute[] public attributes;
constructor(string memory trait_type, string memory display_type, uint8 value) payable {
Attribute[] memory tempAttributeArr = new Attribute[](1);
tempAttributeArr[0] = Attribute(trait_type, display_type, value);
pushAttribute(tempAttributeArr);
}
function pushAttribute(Attribute[] memory _attr) public payable {
attributes.push(_attr[0]);
}
}
Some remarks:
pushAttribute([["trait1","display2",2]]) won't work because an array is not implicitly convertible to a struct. You have to invoke the struct constructor.
Even then pushAttribute([TupleArray.Attribute("trait1","display2",2)]) won't work because Solidity currently does not have dynamic array literals. If you want to pass a dynamic array into a function you have to create a variable for it.
Your pushAttribute() takes an array but ignores all but the first element. So why make it an array at all? I'm assuming it's for the sake of the example but if not, you should make the function just accept a single struct.
Putting tempAttributeArr in storage works but is not necessary. You can put it in memory, which is cheaper.
Solidity latest ABI spec mentions that it is possible to have tuple array as a input.
The docs you are linking to only describe how tuples (used for example to return named function arguments or return values) are formatted in the JSON describing the ABI.
Maybe you're referring to some other section on that page? In general, structs are encoded as tuples the ABI, which might be the source of confusion there. But this simply means that if you are encoding the parameters yourself (e.g. when you use off-chain code to issue a transaction), you should encode structs as tuples. If you are just calling external functions on chain, you do not have to do that. In fact you can't because structs and tuples are not convertible to each other. You have to use an actual struct when a function expects a struct and the struct type must be the exact one specified in function signature.

Why does Solidity suggest me to implement a receive ether function when I have a fallback function?

The recent change in Solidity changed the fallback function format from just function() to fallback(), which is pretty nice for beginners to understand what is going on, but I have a question about a suggestion that the compiler gives me when I implement such a fallback.
For example, a piece of code from my project:
pragma solidity ^0.6.1;
contract payment{
mapping(address => uint) _balance;
fallback() payable external {
_balance[msg.sender] += msg.value;
}
}
Everything goes fine, but the compiler suggests that:
Warning: This contract has a payable fallback function, but no receive ether function.
Consider adding a receive ether function.
What does it mean by a receive ether function? I tried looking it up and many examples I could find is just another fallback function.
I am using version 0.6.1+commit.e6f7d5a4
As a complement to the accepted answer, here's how you should define the unnamed fallback and receive functions to solve this error:
contract MyContract {
fallback() external payable {
// custom function code
}
receive() external payable {
// custom function code
}
}
According to solidity version 0.6.0, we have a breaking change.
The unnamed function commonly referred to as “fallback function” was split up into a new fallback function that is defined using the fallback keyword and a receive ether function defined using the receive keyword.
If present, the receive ether function is called whenever the call data is empty (whether or not ether is received). This function is implicitly payable.
The new fallback function is called when no other function matches (if the receive ether function does not exist then this includes calls with empty call data). You can make this function payable or not. If it is not payable then transactions not matching any other function which send value will revert. You should only need to implement the new fallback function if you are following an upgrade or proxy pattern.
https://solidity.readthedocs.io/en/v0.6.7/060-breaking-changes.html#semantic-and-syntactic-changes

Can you detect if a msg.sender is a smart contract?

If i have a function in solidity being called, is there a way that the function can know if the msg.sender is a smart contract?
Yes you can:
function isContract(address _address) returns (bool isContract){
uint32 size;
assembly {
size := extcodesize(_address)
}
return (size > 0);//Warning: will return false if the call is made from the constructor of a smart contract
}
I don't think there is a safe way to detect if msg.sender is a smartcontract since this can be hacked by doing the calls from the constructor of a smart contract.
Etherenaut's 14th level "Gatekeeper Two" is about this issue. See explanation in:
Ethernaut Lvl 14 Gatekeeper 2 Walkthrough: How contracts initialize (and how to do bitwise operations)
extcodesize(sender) should return 0, if extcodesize is a subroutine inside the sender contract’s original constructor function