Skip to content

Instantly share code, notes, and snippets.

@KcPele
Created August 16, 2023 07:11
Show Gist options
  • Save KcPele/378ef1af3e45c39eb6a953bfbe16bb8c to your computer and use it in GitHub Desktop.
Save KcPele/378ef1af3e45c39eb6a953bfbe16bb8c to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.18+commit.87f61d96.js&optimize=false&runs=200&gist=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IERC20 {
function transfer(address, uint) external;
}
contract AbiEncode {
function test(address _contract, bytes calldata data) external {
(bool ok, ) = _contract.call(data);
require(ok, "call failed");
}
function encodeWithSignature(
address to,
uint amount
) external pure returns (bytes memory) {
// Typo is not checked - "transfer(address, uint)"
return abi.encodeWithSignature("transfer(address,uint256)", to, amount);
}
function encodeWithSelector(
address to,
uint amount
) external pure returns (bytes memory) {
// Type is not checked - (IERC20.transfer.selector, true, amount)
return abi.encodeWithSelector(IERC20.transfer.selector, to, amount);
}
function encodeCall(address to, uint amount) external pure returns (bytes memory) {
// Typo and type errors will not compile
return abi.encodeCall(IERC20.transfer, (to, amount));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/interfaces/IERC1271.sol";
import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import "./lib/Bytecode.sol";
import "./interface/IERC6551Account.sol";
contract ERC6551Account is IERC165, IERC1271, IERC6551Account {
uint256 private _nonce;
receive() external payable {}
function executeCall(
address to,
uint256 value,
bytes calldata data
) external payable returns (bytes memory result) {
require(msg.sender == owner(), "Not token owner");
bool success;
(success, result) = to.call{value: value}(data);
if (!success) {
assembly {
revert(add(result, 32), mload(result))
}
}
_nonce++;
}
function token()
public
view
returns (uint256 chainId, address tokenContract, uint256 tokenId)
{
uint256 length = address(this).code.length;
return
abi.decode(
Bytecode.codeAt(address(this), length - 0x60, length),
(uint256, address, uint256)
);
}
function owner() public view returns (address) {
(uint256 chainId, address tokenContract, uint256 tokenId) = this
.token();
if (chainId != block.chainid) return address(0);
return IERC721(tokenContract).ownerOf(tokenId);
}
function nonce() external view returns (uint256) {
return _nonce;
}
function supportsInterface(bytes4 interfaceId) public pure returns (bool) {
return (interfaceId == type(IERC165).interfaceId ||
interfaceId == type(IERC6551Account).interfaceId);
}
function isValidSignature(
bytes32 hash,
bytes memory signature
) external view returns (bytes4 magicValue) {
bool isValid = SignatureChecker.isValidSignatureNow(
owner(),
hash,
signature
);
if (isValid) {
return IERC1271.isValidSignature.selector;
}
return "";
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/utils/Create2.sol";
import "./interface/IERC6551Registry.sol";
contract ERC6551Registry is IERC6551Registry {
error InitializationFailed();
function createAccount(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt,
bytes calldata initData
) external returns (address) {
bytes memory code = _creationCode(
implementation,
chainId,
tokenContract,
tokenId,
salt
);
address _account = Create2.computeAddress(
bytes32(salt),
keccak256(code)
);
if (_account.code.length != 0) return _account;
_account = Create2.deploy(0, bytes32(salt), code);
if (initData.length != 0) {
(bool success, ) = _account.call(initData);
if (!success) revert InitializationFailed();
}
emit AccountCreated(
_account,
implementation,
chainId,
tokenContract,
tokenId,
salt
);
return _account;
}
function account(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
) external view returns (address) {
bytes32 bytecodeHash = keccak256(
_creationCode(implementation, chainId, tokenContract, tokenId, salt)
);
return Create2.computeAddress(bytes32(salt), bytecodeHash);
}
function _creationCode(
address implementation_,
uint256 chainId_,
address tokenContract_,
uint256 tokenId_,
uint256 salt_
) internal pure returns (bytes memory) {
return
abi.encodePacked(
hex"3d60ad80600a3d3981f3363d3d373d3d3d363d73",
implementation_,
hex"5af43d82803e903d91602b57fd5bf3",
abi.encode(salt_, chainId_, tokenContract_, tokenId_)
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
/// @dev the ERC-165 identifier for this interface is `0x400a0398`
interface IERC6551Account {
/// @dev Token bound accounts MUST implement a `receive` function.
///
/// Token bound accounts MAY perform arbitrary logic to restrict conditions
/// under which Ether can be received.
receive() external payable;
/// @dev Executes `call` on address `to`, with value `value` and calldata
/// `data`.
///
/// MUST revert and bubble up errors if call fails.
///
/// By default, token bound accounts MUST allow the owner of the ERC-721 token
/// which owns the account to execute arbitrary calls using `executeCall`.
///
/// Token bound accounts MAY implement additional authorization mechanisms
/// which limit the ability of the ERC-721 token holder to execute calls.
///
/// Token bound accounts MAY implement additional execution functions which
/// grant execution permissions to other non-owner accounts.
///
/// @return The result of the call
function executeCall(
address to,
uint256 value,
bytes calldata data
) external payable returns (bytes memory);
/// @dev Returns identifier of the ERC-721 token which owns the
/// account
///
/// The return value of this function MUST be constant - it MUST NOT change
/// over time.
///
/// @return chainId The EIP-155 ID of the chain the ERC-721 token exists on
/// @return tokenContract The contract address of the ERC-721 token
/// @return tokenId The ID of the ERC-721 token
function token()
external
view
returns (uint256 chainId, address tokenContract, uint256 tokenId);
/// @dev Returns the owner of the ERC-721 token which controls the account
/// if the token exists.
///
/// This is value is obtained by calling `ownerOf` on the ERC-721 contract.
///
/// @return Address of the owner of the ERC-721 token which owns the account
function owner() external view returns (address);
/// @dev Returns a nonce value that is updated on every successful transaction
///
/// @return The current account nonce
function nonce() external view returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
/// @dev the ERC-165 identifier for this interface is `0x74420f4c`
interface IERC6551Executable {
/**
* @dev Executes a low-level operation if the caller is a valid signer on the account
*
* Reverts and bubbles up error if operation fails
*
* @param to The target address of the operation
* @param value The Ether value to be sent to the target
* @param data The encoded operation calldata
* @param operation A value indicating the type of operation to perform
*
* Accounts implementing this interface MUST accept the following operation parameter values:
* - 0 = CALL
* - 1 = DELEGATECALL
* - 2 = CREATE
* - 3 = CREATE2
*
* Accounts implementing this interface MAY support additional operations or restrict a signer's
* ability to execute certain operations
*
* @return The result of the operation
*/
function execute(
address to,
uint256 value,
bytes calldata data,
uint256 operation
) external payable returns (bytes memory);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
interface IERC6551Registry {
/// @dev The registry SHALL emit the AccountCreated event upon successful account creation
event AccountCreated(
address account,
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
);
/// @dev Creates a token bound account for an ERC-721 token.
///
/// If account has already been created, returns the account address without calling create2.
///
/// If initData is not empty and account has not yet been created, calls account with
/// provided initData after creation.
///
/// Emits AccountCreated event.
///
/// @return the address of the account
function createAccount(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt,
bytes calldata initData
) external returns (address);
/// @dev Returns the computed address of a token bound account
///
/// @return The computed address of the account
function account(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
) external view returns (address);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library Bytecode {
error InvalidCodeAtRange(uint256 _size, uint256 _start, uint256 _end);
/**
@notice Generate a creation code that results on a contract with `_code` as bytecode
@param _code The returning value of the resulting `creationCode`
@return creationCode (constructor) for new contract
*/
function creationCodeFor(
bytes memory _code
) internal pure returns (bytes memory) {
/*
0x00 0x63 0x63XXXXXX PUSH4 _code.length size
0x01 0x80 0x80 DUP1 size size
0x02 0x60 0x600e PUSH1 14 14 size size
0x03 0x60 0x6000 PUSH1 00 0 14 size size
0x04 0x39 0x39 CODECOPY size
0x05 0x60 0x6000 PUSH1 00 0 size
0x06 0xf3 0xf3 RETURN
<CODE>
*/
return
abi.encodePacked(
hex"63",
uint32(_code.length),
hex"80_60_0E_60_00_39_60_00_F3",
_code
);
}
/**
@notice Returns the size of the code on a given address
@param _addr Address that may or may not contain code
@return size of the code on the given `_addr`
*/
function codeSize(address _addr) internal view returns (uint256 size) {
assembly {
size := extcodesize(_addr)
}
}
/**
@notice Returns the code of a given address
@dev It will fail if `_end < _start`
@param _addr Address that may or may not contain code
@param _start number of bytes of code to skip on read
@param _end index before which to end extraction
@return oCode read from `_addr` deployed bytecode
Forked from: https://gist.github.com/KardanovIR/fe98661df9338c842b4a30306d507fbd
*/
function codeAt(
address _addr,
uint256 _start,
uint256 _end
) internal view returns (bytes memory oCode) {
uint256 csize = codeSize(_addr);
if (csize == 0) return bytes("");
if (_start > csize) return bytes("");
if (_end < _start) revert InvalidCodeAtRange(csize, _start, _end);
unchecked {
uint256 reqSize = _end - _start;
uint256 maxSize = csize - _start;
uint256 size = maxSize < reqSize ? maxSize : reqSize;
assembly {
// allocate output byte array - this could also be done without assembly
// by using o_code = new bytes(size)
oCode := mload(0x40)
// new "memory end" including padding
mstore(
0x40,
add(oCode, and(add(add(size, 0x20), 0x1f), not(0x1f)))
)
// store length in memory
mstore(oCode, size)
// actually retrieve the code, this needs assembly
extcodecopy(_addr, add(oCode, 0x20), _start, size)
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract NFT is ERC721, ERC721URIStorage, Ownable {
uint256 private _tokenId;
constructor() ERC721("TestNFT", "TNFT") {}
function totalSupply() public view returns (uint256) {
return _tokenId;
}
function _burn(
uint256 tokenId
) internal override(ERC721, ERC721URIStorage) {
super._burn(tokenId);
}
function tokenURI(
uint256 tokenId
) public view override(ERC721, ERC721URIStorage) returns (string memory) {
return super.tokenURI(tokenId);
}
function supportsInterface(
bytes4 interfaceId
) public view override(ERC721, ERC721URIStorage) returns (bool) {
return super.supportsInterface(interfaceId);
}
function safeMint(address to, string memory uri) public onlyOwner {
_tokenId++;
_safeMint(to, _tokenId);
_setTokenURI(_tokenId, uri);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract Token is ERC20 {
constructor() ERC20("TestToken", "TT") {
_mint(msg.sender, 1000 * 10 ** decimals());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment