I want to write javascript method for same
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, implementation))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
mstore(add(ptr, 0x38), shl(0x60, deployer))
mstore(add(ptr, 0x4c), salt)
mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
predicted := keccak256(add(ptr, 0x37), 0x55)
}
}
Related
i'm working at a smart-contract that has a merkle-tree function in it.
Basically i'm passing the proof from a firebase function that take in input the address and the n° of token reserved by the user, and gives as output the proof to the front-end that execute the smart-contract function.
The only thing i need to figure out is the leaf calculation, on the firebase function it is composed by encoding keccak256 the string in the array, composed by an eth address concatenated with the number of reserved tokens (0x38207d0e84edb04a57482c3769fe192617520373 + 3), in the smart contract instead, the leaf is calculated with the keccak256(abi.encodepacked(0x38207d0e84edb04a57482c3769fe1926175203733, 3), and it's not passing the verifiy...
I think these two methods of calculation are different in some part, can you help me?
I'm going to paste the code of both functions:
SOLIDITY
// 1. Address check, if the proof is valid
function isPrivateListed(
uint256 _addressReservedTokens,
bytes32[] memory _proof
) public view returns (bool) {
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, _addressReservedTokens);
return MerkleProof.verify(_proof, privateListRoot, leaf);
}
// 2. Mint function called from the front-end
function privateMint(
uint256 _mintAmount,
uint256 _addressReservedTokens,
bytes32[] calldata proof
) public payable {
require(privateOpen, "The private mint is not opened");
require(
isPrivateListed(_addressReservedTokens, proof),
"you are not in the private list"
);
uint256 supply = totalSupply();
require(supply + _mintAmount <= maxSupply, "max NFT limit exceeded");
uint256 owned = addressMintedBalance[msg.sender];
require(
owned + _mintAmount <= _addressReservedTokens,
"You have less nft reserved"
);
for (uint256 i = 1; i <= _mintAmount; i++) {
_safeMint(msg.sender, supply + i);
addressMintedBalance[msg.sender]++;
}
}
FIREBASE FUNCTION
// 1. Get parameters from the function called from client side
const walletAddress = data.walletAddress;
let whitelistAddresses = [
'0x38207d0e84edb04a57482c3769fe1926175203733',
'0x943926a8ff0000350d0b879a658fa52bcd4fca186',
];
// 2. Create a new array of `leafNodes` by hashing all indexes of the `whitelistAddresses`
// using `keccak256`. Then creates a Merkle Tree object using keccak256 as the algorithm.
const leafNodes = whitelistAddresses.map((addr) => keccak256(addr));
const merkleTree = new MerkleTree(leafNodes, keccak256, {
sortPairs: true,
});
// 3. Get root hash of the `merkleeTree` in hexadecimal format (0x)
const rootHash = merkleTree.getRoot();
const rootHashHex = merkleTree.getHexRoot();
// ***** ***** ***** CLIENT-SIDE ***** ***** ***** //
const claimingAddress = keccak256(walletAddress);
const hexProof = merkleTree.getHexProof(claimingAddress);
const verifyProof = merkleTree.verify(hexProof, claimingAddress, rootHash);
In solidity you're hashing a (20+32)-bytes element
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, _addressReservedTokens);
Since msg.sender is 20 bytes and _addressReservedTokens is 32 bytes, the output of abi.encodePacked() is something like this:
0x38207d0e84edb04a57482c3769fe1926175203730000000000000000000000000000000000000000000000000000000000000003
This is clearly different than 0x38207d0e84edb04a57482c3769fe1926175203733 used by firebase.
Also I'm not sure if the js keccak256 is interpreting that value as string or as bytes (in solidity it's bytes).
The best fix is probably changing the js code. You can write the correct whitelistAddresses using ethers, see here https://ethereum.stackexchange.com/questions/119990/how-to-mimic-abi-encodepacked-in-ethers
I have a contract like this that has a constructor like this:
constructor(
string[] memory characterNames,
string[] memory characterImageURIs,
uint256[] memory characterHp,
uint256[] memory characterAttackDmg,
string memory bossName,
string memory bossImageURI,
uint256 bossHp,
uint256 bossAttackDamage
) ERC721("Heroes", "HERO") {
for (uint256 i = 0; i < characterNames.length; i += 1) {
defaultCharacters.push(
CharacterAttributes({
characterIndex: i,
name: characterNames[i],
imageURI: characterImageURIs[i],
hp: characterHp[i],
maxHp: characterHp[i],
attackDamage: characterAttackDmg[i]
})
);
CharacterAttributes memory c = defaultCharacters[i];
console.log(
"Done initializing %s w/ HP %s, img %s",
c.name,
c.hp,
c.imageURI
);
}
bigBoss = BigBoss({
name: bossName,
imageURI: bossImageURI,
hp: bossHp,
maxHp: bossHp,
attackDamage: bossAttackDamage
});
console.log(
"Done initializing boss %s w/ HP %s, img %s",
bigBoss.name,
bigBoss.hp,
bigBoss.imageURI
);
_tokenIds.increment();
}
Question
for the characters names and HP and Attack... how should I type them is it in a JSON or in arrays or what?
for the imageURI how can I parse them to the constructor and where as I'm using Pinata IPFS to host my images?
Please if you can help me with this.
1- type for constructor arguments are already defined:
string[] = array of strings
string =string
uint (uint256) = 256 bits unsigned number ranging from 0 to 2²⁵⁶
2- constructor arguments mean, when you create the contract, you have to pass those parameters inorder to initialize the contracts. So you should already have them in hand.
you store the metadata in ipfs: an example would be like this:
{
"name": "name",
"description": "descruption",
"image": "ipfs://cid/name.jpeg"
}
So when you work in front-end, you make a request to ipfs Uri, to get the metadata:
// make a request with axios library
// this will store the above json data inside `metaData`.data
const metaData = await axios.get(tokenUri)
const data=metaData.data
From metadata, you get the imageUri as data.image
I'm practicing on an ERC_721 contract and would like to know if there's a way to store JSON data directly on the contract, take a look at my approach and give your thoughts:
The variation1.json file uploaded to IPFS:
{
"attributes": [
{
"trait_type": "Rank",
"value": "1"
}
],
"description": "A simple NFT",
"image": "Qmdasifhw89rv92enfkq128re3",
"name": "NFT#1"
}
Then I would use an object like the one below to go with the contract that references the file to the urlOfTokenURI
[
{
"file": "variation1.json", "urlOfTokenURI": https://gateway.pinata.cloud/ipfs/Qmdasifhw89rv92enfkqeqe23f"
},
{
"file": "variation2.json", "urlOfTokenURI": "https://gateway.pinata.cloud/ipfs/Qmgvfrg34vt4u785ygbmasdsa"
},
...
]
Then get a random number, retrieve a random urlOfTokenURI and mint the token to the desired address, excluding that token from the list of available tokens:
function mintNFT(address recipient, string memory tokenURI)
public onlyOwner
returns (uint256)
{
_tokenIds.increment();
//retrieve object, get random number and mint with the urlOfTokenURI
//from random number index
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
What's missing here for this to work?
Can someone guide me how to proceed or have a suggestion for another approach? I'm kind of lost.
Don't understand how to mint a random NFT and instantly revealing it to the user.
Sorry in advance if it's a stupid question. I'm learning, btw.
Appreciate the help!
If you need a real random number fn, you need to look for oracle service (e.g. Chainlink)
Yes, it is possible to generate an uri without publishing to IPFS. You can generate a base64 data uri instead. Check out Example from wizard and dragon game and their contract
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/utils/Strings.sol";
...
using Strings for uint256;
using Base64 for bytes;
...
function tokenURI(uint256 id) public view returns (string memory) {
string memory metadata = string(abi.encodePacked(
"{\"name\": \"NFT#",
id.toString(),
"\", \"description\": \"A simple NFT\", \"attributes\":",
"[{\"key\":\"trait_type\",\"value\":\"1\"}]",
"}"
));
return string(abi.encodePacked(
"data:application/json;base64,",
bytes(metadata).base64()
));
}
Base64 lib
pragma solidity ^0.8.0;
library Base64 {
string internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
function base64(bytes memory data) internal pure returns (string memory) {
if (data.length == 0) return "";
// load the table into memory
string memory table = TABLE;
// multiply by 4/3 rounded up
uint256 encodedLen = 4 * ((data.length + 2) / 3);
// add some extra buffer at the end required for the writing
string memory result = new string(encodedLen + 32);
assembly {
// set the actual output length
mstore(result, encodedLen)
// prepare the lookup table
let tablePtr := add(table, 1)
// input ptr
let dataPtr := data
let endPtr := add(dataPtr, mload(data))
// result ptr, jump over length
let resultPtr := add(result, 32)
// run over the input, 3 bytes at a time
for {} lt(dataPtr, endPtr) {}
{
dataPtr := add(dataPtr, 3)
// read 3 bytes
let input := mload(dataPtr)
// write 4 characters
mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(18, input), 0x3F)))))
resultPtr := add(resultPtr, 1)
mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(12, input), 0x3F)))))
resultPtr := add(resultPtr, 1)
mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr( 6, input), 0x3F)))))
resultPtr := add(resultPtr, 1)
mstore(resultPtr, shl(248, mload(add(tablePtr, and( input, 0x3F)))))
resultPtr := add(resultPtr, 1)
}
// padding with '='
switch mod(mload(data), 3)
case 1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) }
case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) }
}
return result;
}
}
I have a problem with adding new opcode to solidity. I'm using solc (on C++) and geth(ethereum on Go). I want to add new opcode, that takes address payable, uint256, uint256, bytes memory and returns bytes memory. So I have a problem with return value.
Some peaces of code below, I will skip some files, to make question shorter.
Solc
libsolidity/codegen/ExpressionCompiler.cpp
// ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::MyOpcode:
{
acceptAndConvert(*arguments[0], *function.parameterTypes()[0], true);
acceptAndConvert(*arguments[1], *function.parameterTypes()[1], true);
acceptAndConvert(*arguments[2], *function.parameterTypes()[2], true);
arguments[3]->accept(*this);
utils().fetchFreeMemoryPointer();
utils().packedEncode(
{arguments[3]->annotation().type},
{TypeProvider::array(DataLocation::Memory, true)}
);
utils().toSizeAfterFreeMemoryPointer();
m_context << Instruction::MYOPCODE;
}
libsolidity/analysis/GlobalContext.cpp
// inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariables()
magicVarDecl("myopcode", TypeProvider::function(strings{"address payable", "uint256", "uint256", "bytes memory"}, strings{"bytes memory"}, FunctionType::Kind::MyOpcode, false, StateMutability::Payable)),
libevmasm/Instruction.cpp
// static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::MYOPCODE, { "MYOPCODE", 0, 5, 1, true, Tier::Base } }
Geth
core/vm/jump_table.go
// func newFrontierInstructionSet() JumpTable {
CALLACTOR: {
execute: opMyOpCode,
dynamicGas: gasCallActor,
minStack: minStack(5, 1),
maxStack: maxStack(5, 1),
memorySize: memoryReturn,
writes: true,
returns: true,
},
core/vm/instructions.go
func opMyOpcode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
inoffset, insize := callContext.stack.pop(), callContext.stack.pop()
params := callContext.memory.GetPtr(int64(inoffset.Uint64()), int64(insize.Uint64()))
secondValue := callContext.stack.pop()
firstValue := callContext.stack.pop()
addr := callContext.stack.pop()
// ... Do smth with input ...
outoffset := inoffset.Uint64() + insize.Uint64()
callContext.memory.Set(outoffset, 1, []byte{0x2})
tmp := make([]byte, 1)
tmp[0] = 0x98
callContext.memory.Set(outoffset + 1, 1, tmp)
callContext.stack.push(uint256.NewInt().SetUint64(outoffset))
return tmp, nil
}
Smart contract
pragma solidity >=0.6.0; // SPDX-License-Identifier: GPL-3
contract test {
event ReturnValue(address payable _from, bytes data);
function f() public returns(bytes memory){
address payable addr1 = payable(msg.sender);
bytes memory params = new bytes(2);
params[0] = 0x21;
params[1] = 0x22;
bytes memory result = myopcode(addr1, 0x11, 0x12, params);
emit ReturnValue(addr1, result);
return result;
}
}
When I run that code I get invalid jump destination. So, what I need to do, to get my code work correctly?
I found a solution, it certainly looks stupid, but this case is already provided by the developers. All you need is utils().returnDataToArray().
I have a contract A and a contract B.
Contract A declares this function:
function getIntValue() constant returns (uint);
What would be the appropriate assembly code to delegatecall contract A's getIntValue function from B? I'm not yet very experienced with assembly so I only have this so far which doesn't work:
function getContractAIntValue() constant returns (uint c) {
address addr = address(contractA); // contract A is stored in B.
bytes4 sig = bytes4(sha3("getIntValue()")); // function signature
assembly {
let x := mload(0x40) // find empty storage location using "free memory pointer"
mstore(x,sig) // attach function signature
let status := delegatecall(sub(gas, 10000), addr, add(x, 0x04), 0, x, 0x20)
jumpi(invalidJumpLabel, iszero(status)) // error out if unsuccessful delegatecall
c := mload(x)
}
}
Maybe you have solved it cause was asked more than one year ago, but in case some is still looking for it...
address addr = address(contractA); // contract A is stored in B.
bytes memory sig = abi.encodeWithSignature("getIntValue()"); // function signature
// solium-disable-next-line security/no-inline-assembly
assembly {
let result := delegatecall(sub(gas, 10000), addr, add(sig, 0x20), mload(sig), 0, 0)
let size := returndatasize
let ptr := mload(0x40)
returndatacopy(ptr, 0, size)
// revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
// if the call returned error data, forward it
switch result case 0 { revert(ptr, size) }
default { return(ptr, size) }
}