Giter Site home page Giter Site logo

solidity-by-example / solidity-by-example.github.io Goto Github PK

View Code? Open in Web Editor NEW
573.0 13.0 181.0 30.56 MB

Solidity By Example

Home Page: https://solidity-by-example.org/

License: MIT License

HTML 0.06% CSS 0.37% Solidity 27.70% TypeScript 71.82% Mustache 0.06%

solidity-by-example.github.io's Introduction

solidity-by-example.github.io

Solidity By Example

License

MIT License

Development

npm i
npm start

Production

npm run build
# Preview produciton
npm run preview
# Deploy
npm run deploy

Memo

TODO: code split / dynamic imports

## Deploy ##
# copy files from contracts
npx ts-node scripts/copy.ts
# md to react
npx ts-node --project ./scripts/tsconfig.json scripts/md-to-react.ts src/pages/array

# md to react all pages
find src/pages -type d -not -path "*/__snapshots__" -exec npx ts-node --project ./scripts/tsconfig.json scripts/md-to-react.ts {} \;

# build routes
npx ts-node --project ./scripts/tsconfig.json scripts/build-routes.ts

# build search index
npx ts-node --project ./scripts/tsconfig.json scripts/build-search-index.ts

## Compile Solidity ##
solc-select install 0.8.24
solc-select use 0.8.24

# compile single file
solc src/pages/hello-world/HelloWorld.sol

# find and compile sol
find src/pages/hacks -name "*.sol" solc {} \;
find src/pages -type f -name "*.sol" -exec sh -c 'solc "$0"' {} \;

## Mics ##
# rename files
find . -type f -name "index.test.js" -exec sh -c 'mv "$0" "${0%.test.js}.test.tsx"' {} \;

solidity-by-example.github.io's People

Contributors

amimaro avatar andrewjiang avatar aragalie avatar atarpara avatar basnijholt avatar dawsbot avatar devanoneth avatar drblessing avatar erbazz avatar eulerbeat5 avatar evalir avatar gitwinst avatar gowtham-the-dev avatar hammer-a avatar harkeshbirman avatar ihorbond avatar janndriessen avatar meetmangukiya avatar paouky avatar pmuens avatar qd-qd avatar rockmanr avatar sm4rty-1 avatar solidity-by-example avatar sunnystefi avatar t4sk avatar tanliwei avatar tanteli avatar teamdandelion avatar thurendous avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

solidity-by-example.github.io's Issues

Chinese Version

We have made a Chinese version for this warehouse and hope to work together.

Echidna

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;

contract ArrayRemoveByShifting {
    uint[] internal arr;

    function remove(uint _index) internal {
        require(_index < arr.length, "index out of bound");

        for (uint i = _index; i < arr.length - 1; i++) {
            arr[i] = arr[i + 1];
        }
        arr.pop();
    }

    // function test() external {
    //     arr = [1, 2, 3, 4, 5];
    //     remove(2);
    //     // [1, 2, 4, 5]
    //     assert(arr[0] == 1);
    //     assert(arr[1] == 2);
    //     assert(arr[2] == 4);
    //     assert(arr[3] == 5);
    //     assert(arr.length == 4);

    //     arr = [1];
    //     remove(0);
    //     // []
    //     assert(arr.length == 0);
    // }
}

contract EchidnaTestArrayShift is ArrayRemoveByShifting {
    uint[] private copy;

    event AssertionFailed();

    function test_remove(uint[] memory _arr, uint _i) public {
        require(_i < _arr.length);

        // emit AssertionFailed();

        arr = _arr;

        // reset copy
        delete copy;
        // copy elements except _i th element
        for (uint i = 0; i < arr.length; i++) {
            if (i != _i) {
                copy.push(arr[i]);
            }
        }

        remove(_i);

        if (arr.length != copy.length) {
            emit AssertionFailed();
        }

        for (uint i = 0; i < arr.length; i++) {
            if (arr[i] != copy[i]) {
                emit AssertionFailed();
            }
        }
    }
}

/*
docker pull trailofbits/eth-security-toolbox
docker run -it --rm -v $PWD:/code trailofbits/eth-security-toolbox

echidna-test ./EchidnaTestArrayShift.sol --contract EchidnaTestArrayShift --check-asserts --test-limit 5000
*/

load code to remix using js or github gist

to load the source in remix

remix.loadurl("https://raw.githubusercontent.com/solidity-by-example/solidity-by-example.github.io/gh-pages/src/pages/Gas/Gas.sol")

github gist

http://remix.ethereum.org/#gist=138fd234be78e9ddc1a0176f57b0dd17

heap sort - maybe

Interesting but sorting is never used ....

contract Heap {

    using SafeMath for uint256;

    // The main operations of a priority queue are insert, delMax, & isEmpty.
    constructor() public {
        // Start at 0
        heap = [0];
    }

    // We will be storing our heap in an array
    uint256[] public heap;

    // Inserts adds in a value to our heap.
    function insert(uint256 _value) public {
        // Add the value to the end of our array
        heap.push(_value);
        // Start at the end of the array
        uint256 currentIndex = heap.length.sub(1);

        // Bubble up the value until it reaches it's correct place (i.e. it is smaller than it's parent)
        while(currentIndex > 1 && heap[currentIndex.div(2)] < heap[currentIndex]) {

        // If the parent value is lower than our current value, we swap them
        (heap[currentIndex.div(2)], heap[currentIndex]) = (_value, heap[currentIndex.div(2)]);
        // change our current Index to go up to the parent
        currentIndex = currentIndex.div(2);
        }
    }

    // RemoveMax pops off the root element of the heap (the highest value here) and rebalances the heap
    function removeMax() public returns(uint256){
        // Ensure the heap exists
        require(heap.length > 1);
        // take the root value of the heap
        uint256 toReturn = heap[1];

        // Takes the last element of the array and put it at the root
        heap[1] = heap[heap.length.sub(1)];
        // Delete the last element from the array
        heap.length = heap.length.sub(1);
    
        // Start at the top
        uint256 currentIndex = 1;

        // Bubble down
        while(currentIndex.mul(2) < heap.length.sub(1)) {
            // get the current index of the children
            uint256 j = currentIndex.mul(2);

            // left child value
            uint256 leftChild = heap[j];
            // right child value
            uint256 rightChild = heap[j.add(1)];

            // Compare the left and right child. if the rightChild is greater, then point j to it's index
            if (leftChild < rightChild) {
                j = j.add(1);
            }

            // compare the current parent value with the highest child, if the parent is greater, we're done
            if(heap[currentIndex] > heap[j]) {
                break;
            }

            // else swap the value
            (heap[currentIndex], heap[j]) = (heap[j], heap[currentIndex]);

            // and let's keep going down the heap
            currentIndex = j;
        }
            // finally, return the top of the heap
            return toReturn;
    }


    function getHeap() public view returns(uint256[]) {
        return heap;
    }

    function getMax() public view returns(uint256) {
        return heap[1];
    }

}

A bug in NFT English Auction code example?

In the English auction code, when a bidder calls bid(), it sends the bid amount (msg.value) to the contract address, but in the bid() function the bids array only adds value of the current highestBid (before highestBid gets set to msg.value in line 69). A bidder won't receive a full refund if it withdraws before it wins or gets outbidded by others.

A hypothetical example may help see the bug better: Assuming the auction contract gets initiated with _startingBid = 0, when the first bidder bidder1 bids 1 ether for the NFT, bids[bidder1] = 0 at line 65. If bidder1 withdraws its bid now, it will receive 0 refund from the contract address, even though it sends 1 ether to the contract when calling bid().

The fix is easy, by moving the 3 lines starting at Line 64 to currently Line 70. The winner should also have the bid amount subtract from amount withdrawable.

I created a pull request with fixes & re-generated page.

uint to uint256 (Typo)

Noticed a slight typo in Primitive Data Types,
Second uint (u256) should be written as uint256

image

Second int (i256) should be written as int256

image

Potential exploit in StakingRewards

Not that these contracts are audited or have real money behind them, but there is an interesting exploit in StakingRewards, I think.

Because of the way the reward rate is calculated, and the fact there is no separation between total supply and balanceOf if the rewards and staking tokens are the same, a malicious owner/user could manipulate the contract into setting a too high reward rate.

This is only possible if the stakingToken and rewardsToken are the same, and the owner sets a ridiculously high rewardAmount in notifyRewardAmount, after depositing a large amount of the tokens. This would mostly be an issue in rugpulls or memetokens, where the owner and minter of the token could create an artificially high rewardRate, then withdraw their tokens, then users couldn't earn rewards. Furthermore, if two users stake where the rewardRate is too high, someone who stakes a tiny amount could steal the staking balance of another user. The full exploit is written as a foundry test case and I published it in a gist here:

https://gist.github.com/Drblessing/a6ebdfcf91a4b7a0e1c02f860ce5160e

I'm new to solidity so I could be totally wrong here. Thanks! I would love to hear any feedback you may have on it.

contrast is too low for code keywords...

dark mode doesn't fix this for me, it's just a CSS issue.

in Foundation.css, here:

.hljs-code,
.hljs-number,
.hljs-literal,
.hljs-keyword,
.hljs-selector-tag {
  color: #099;
}

Changing#099 to #0758fc increases the contrast from 3 (insufficient) to 4.74 (passing) according to devtools.

will dig in and see if I can do a quick pull request.

Outdated echidna examples

Hello,

The echidna examples in the repo seem to be outdated. It says to run echidna-test instead of just echidna as in the documentation. I got an error when I tried echidna-test.

Also there are some minor corrections in the contracts:

  • TestEchidna.sol:
    • To use assertions you need the flag --test-mode assertion instead of --check-asserts
    • echidna in not buggy anymore, so you don't need to switch to solc 0.7 to run assertions
  • EchidnaTestTimeAndCaller.sol:
    • The third address is 0x30000 instead of 0x00a329C0648769a73afAC7F9381e08fb43DBEA70

I would be more than happy to create a PR with the corrections but I wanted to confirm with you first if you consider these changes valid.

Cheers.

example not work expected - Create2

In the Precompute Contract Address with Create2 example

    function deploy(bytes memory bytecode, uint _salt) public payable {
        ......
        assembly {
            addr := create2(
                callvalue(), // wei sent with current call
                ......
            )
       ......

It is said that you can send msg.value to the newly deployed contract.

So I had a test just now, to deploy this contract by create2, and try to send 1 wei to it.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

contract SimpleContract {
    uint public myNumber;

    function setNumber(uint number) public {
        myNumber = number;
    }

    fallback() external payable{}
}

The bytecode of this contract is 0x608060405234801561001057600080fd5b5061015c806100206000396000f3fe60806040526004361061002d5760003560e01c806323fd0e40146100305780633fb5c1cb1461005b5761002e565b5b005b34801561003c57600080fd5b50610045610084565b60405161005291906100ad565b60405180910390f35b34801561006757600080fd5b50610082600480360381019061007d91906100f9565b61008a565b005b60005481565b8060008190555050565b6000819050919050565b6100a781610094565b82525050565b60006020820190506100c2600083018461009e565b92915050565b600080fd5b6100d681610094565b81146100e157600080fd5b50565b6000813590506100f3816100cd565b92915050565b60006020828403121561010f5761010e6100c8565b5b600061011d848285016100e4565b9150509291505056fea2646970667358221220a02fa857b764cbf10d2cc497fdc2937c27717eb3033d67276baef7b1c60d183864736f6c63430008120033

Use any salt you like.

It will get reverted if you send eth value to deploy(bytes memory bytecode, uint _salt) function.

An error in StakingRewards causes rewards to be negative

This line in StakingRewards should be return rewardPerTokenStored;.

Once _totalSupply becomes non-zero and some accounts has positive userRewardPerTokenPaid stored, then _totalSupply gets back to zero(all tokens has been withdraw), rewardPerToken() will return zero and those accounts will get negative earned()1(causes an uint underflow).

By the way, I think the contract needs further refactoring.

encode call memo

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

interface IERC20 {
    function transfer(address, uint) external;
}

contract Token {
    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));
    }
}

0.7

  • test all contracts on remix
  • update fallback

EXTCODE hack

pragma solidity ^0.8;

contract Target {
    function isContract(address account) public view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly { size := extcodesize(account) }
        return size > 0;
    }
    
    bool public pwned = false;
    
    function protected() external {
        require(!isContract(msg.sender), "no contract allowed");
        pwned = true;
    }
}

contract Attack {
    function pwn(address _target) external {
        Target(_target).protected();
    }
}

contract Hack {
    bool public isContract;
    address public addr;
    
    constructor(address _target) {
        isContract = Target(_target).isContract(address(this));
        addr = address(this);
        Target(_target).protected();
    }
}

array shift

contract ArrayShift {
    // [1, 2, 3] -- remove(1) --> [1, 3, 3] --> [1, 3]
    // [1, 2, 3, 4, 5, 6] -- remove(2) --> [1, 2, 4, 5, 6, 6] --> [1, 2, 4, 5, 6]
    // [1, 2, 3, 4, 5, 6] -- remove(0) --> [2, 3, 4, 5, 6, 6] --> [2, 3, 4, 5, 6]
    // [1] -- remove(0) --> [1] --> []
    
    uint[] public arr;

    function remove(uint _index) public {
        require(_index < arr.length, "index out of bound");

        // Write your code here
        for (uint i = _index; i < arr.length - 1; i++) {
            arr[i] = arr[i + 1];
        }
        arr.pop();
    }
    
    function test() external {
        arr = [1, 2, 3, 4, 5];
        remove(2);
        // [1, 2, 4, 5]
        assert(arr[0] == 1);
        assert(arr[1] == 2);
        assert(arr[2] == 4);
        assert(arr[3] == 5);
        assert(arr.length == 4);
        
        arr = [1];
        remove(0);
        // []
        assert(arr.length == 0);
    }
}

memo

  • Uniswap V2 flashswap
  • commit reveal / seal bid auction

library - sqrt

replace library math example with square root function

example - deploy arbitrary contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;

contract TestContract1 {
    address public owner = msg.sender;
    
    function setOwner(address _owner) public {
        require(msg.sender == owner, "not owner");
        owner = _owner;
    }
}

contract TestContract2 {
    address public owner = msg.sender;
    uint public value = msg.value;
    uint public x;
    uint public y;
    
    constructor(uint _x, uint _y) payable {
        x = _x;
        y = _y;
    }
}

contract Proxy {
    event Deploy(address);
    
    fallback() external payable {}
    
    function deploy(bytes memory _code) external payable returns (address addr) {
        assembly {
            // create(v, p, n)
            // v = amount of ETH to send
            // p = pointer in memory to start of code
            // n = size of code
            addr := create(callvalue(), add(_code, 0x20), mload(_code))       
        }
        // return address 0 on error
        require(addr != address(0), "deploy failed");
        
        emit Deploy(addr);
     }
    
    function execute(address _target, bytes memory _data)
        external
        payable
    {
        (bool success, ) = _target.call{value: msg.value}(_data);
        require(success, "failed");
    }
}

contract Helper {
    function getBytecode1() external pure returns (bytes memory) {
        bytes memory bytecode = type(TestContract1).creationCode;
        return bytecode;
    }
    
    function getBytecode2(uint _x, uint _y) external pure returns (bytes memory) {
        bytes memory bytecode = type(TestContract2).creationCode;
        return abi.encodePacked(bytecode, abi.encode(_x, _y));
    }
    
    
    function getCalldata(address _owner) external pure returns (bytes memory) {
        return abi.encodeWithSignature("setOwner(address)", _owner);
    }
}

Contributions Template?

I would like to create several new entries in the Applications and Defi categories.

Do you allow people to contribute?
What format do you expect us to use?

Looking forward to add content,

:)

Mistake in code explaination

Hey man,

I am learning solidity and found this bit of a mistake in your explaination of the code
the code I am refering to is in Bitwiser.sol

  • your code
    // Get position of most significant bit
    // x = 1100 = 10, most significant bit = 1000, so this function will return 3
    function mostSignificantBit(uint256 x) external pure returns (uint256) {
        uint256 i = 0;
        while ((x >>= 1) > 0) {
            ++i;
        }
        return i;
    }

in the second comment 1100 should be equals to 12 not 10
1100 => 8 + 4 + 0 + 0 => 12

proxy memo

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

// Transparent upgradeable proxy pattern
// Topics
// - Intro (wrong way)
// - Return data from fallback
// - Storage for implementation and admin
// - Separate user / admin interfaces
// - Proxy admin
// - Demo

contract CounterV1 {
    uint public count;

    function inc() external {
        count += 1;
    }
}

contract CounterV2 {
    uint public count;

    function inc() external {
        count += 1;
    }

    function dec() external {
        count -= 1;
    }
}

contract BuggyProxy {
    address public implementation;
    address public admin;

    constructor() {
        admin = msg.sender;
    }

    function _delegate() private {
        (bool ok, bytes memory res) = implementation.delegatecall(msg.data);
        require(ok, "delegatecall failed");
    }

    fallback() external payable {
        _delegate();
    }

    receive() external payable {
        _delegate();
    }

    function upgradeTo(address _implementation) external {
        require(msg.sender == admin, "not authorized");
        implementation = _implementation;
    }
}

contract Dev {
    function selectors()
        external
        view
        returns (
            bytes4,
            bytes4,
            bytes4
        )
    {
        return (
            Proxy.admin.selector,
            Proxy.implementation.selector,
            Proxy.upgradeTo.selector
        );
    }
}

contract Proxy {
    // All functions / variables should be private, forward all calls to fallback

    // -1 for unknown preimage
    // 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
    bytes32 private constant IMPLEMENTATION_SLOT =
        bytes32(uint(keccak256("eip1967.proxy.implementation")) - 1);
    // 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
    bytes32 private constant ADMIN_SLOT =
        bytes32(uint(keccak256("eip1967.proxy.admin")) - 1);

    constructor() {
        _setAdmin(msg.sender);
    }

    modifier ifAdmin() {
        if (msg.sender == _getAdmin()) {
            _;
        } else {
            _fallback();
        }
    }

    function _getAdmin() private view returns (address) {
        return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
    }

    function _setAdmin(address _admin) private {
        require(_admin != address(0), "admin = zero address");
        StorageSlot.getAddressSlot(ADMIN_SLOT).value = _admin;
    }

    function _getImplementation() private view returns (address) {
        return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
    }

    function _setImplementation(address _implementation) private {
        require(_implementation.code.length > 0, "implementation is not contract");
        StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = _implementation;
    }

    // Admin interface //
    function changeAdmin(address _admin) external ifAdmin {
        _setAdmin(_admin);
    }

    // 0x3659cfe6
    function upgradeTo(address _implementation) external ifAdmin {
        _setImplementation(_implementation);
    }

    // 0xf851a440
    function admin() external ifAdmin returns (address) {
        return _getAdmin();
    }

    // 0x5c60da1b
    function implementation() external ifAdmin returns (address) {
        return _getImplementation();
    }

    // User interface //
    function _delegate(address _implementation) internal virtual {
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.

            // calldatacopy(t, f, s) - copy s bytes from calldata at position f to mem at position t
            // calldatasize() - size of call data in bytes
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.

            // delegatecall(g, a, in, insize, out, outsize) -
            // - call contract at address a
            // - with input mem[in…(in+insize))
            // - providing g gas
            // - and output area mem[out…(out+outsize))
            // - returning 0 on error (eg. out of gas) and 1 on success
            let result := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            // returndatacopy(t, f, s) - copy s bytes from returndata at position f to mem at position t
            // returndatasize() - size of the last returndata
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                // revert(p, s) - end execution, revert state changes, return data mem[p…(p+s))
                revert(0, returndatasize())
            }
            default {
                // return(p, s) - end execution, return data mem[p…(p+s))
                return(0, returndatasize())
            }
        }
    }

    function _fallback() private {
        _delegate(_getImplementation());
    }

    fallback() external payable {
        _fallback();
    }

    receive() external payable {
        _fallback();
    }
}

contract ProxyAdmin {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    function getProxyAdmin(address proxy) external view returns (address) {
        (bool ok, bytes memory res) = proxy.staticcall(
            abi.encodeCall(Proxy.implementation, ())
        );
        require(ok, "call failed");
        return abi.decode(res, (address));
    }

    function getProxyImplementation(address proxy) external view returns (address) {
        (bool ok, bytes memory res) = proxy.staticcall(abi.encodeCall(Proxy.admin, ()));
        require(ok, "call failed");
        return abi.decode(res, (address));
    }

    function changeProxyAdmin(address payable proxy, address admin) external onlyOwner {
        Proxy(proxy).changeAdmin(admin);
    }

    function upgrade(address payable proxy, address implementation) external onlyOwner {
        Proxy(proxy).upgradeTo(implementation);
    }
}

library StorageSlot {
    struct AddressSlot {
        address value;
    }

    function getAddressSlot(bytes32 slot)
        internal
        pure
        returns (AddressSlot storage r)
    {
        assembly {
            r.slot := slot
        }
    }
}

contract TestSlot {
    bytes32 public constant slot = keccak256("TEST_SLOT");

    function getSlot() external view returns (address) {
        return StorageSlot.getAddressSlot(slot).value;
    }

    function writeSlot(address _addr) external {
        StorageSlot.getAddressSlot(slot).value = _addr;
    }
}

Explain why low-level call is not recommeded

Thanks for creating this website, I frequently return to it when in doubt about how a particular syntax works in Solidity.

Some feedback on the call page. You say that:

... it is not the recommend way to call existing functions.

But do not explain why.

It might be helpful to add a few thoughts about why using the low-level calls is not recommended. As far as I know, the reasons are:

  • Reverts are not bubbled up
  • Type checks are bypassed
  • Function existence checks are omitted
  • No argument packing (though I'm not sure about this point)

minimal proxy notes

pragma solidity ^0.7;

/*
- minimal proxy contract (deploy)

- Why is constructor not called? deploys a contract that only calls delegatecall
- Why is it cheap to deploy contract? 
- Why is the original contract not affected? uses delegatecall

- delegatecall
- minimal proxy contract (interaction)
- Why is it cheap to deploy contract? there is minimal code
- Why is the original contract not affected? uses delegatecall

- Why is constructor not called? deploys a simple contract that fowards all calls using delegatecall
    - vyper create_forwarder_to
    - pseudo code (notice constructor of master copy is not called)
- actual code

3d602d80600a3d3981f3363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3

    - bytecode
    - 2 parts (
        runtime code - delegate call
        creation code - return contract to delegate call
    )
    - creation code vs runtime code
- clone factory
  - mstore diagram
*/

contract PseudoMinimalProxy {
    address masterCopy;
    
    constructor(address _masterCopy) {
        // notice that constructor of master copy is not called
        masterCopy = _masterCopy;
    }
    
    function forward() external returns (bytes memory) {
        (bool success, bytes memory data) = masterCopy.delegatecall(msg.data);
        require(success);
        
        return data;
    }
}

contract MinimalProxy {
  function clone(address target) external returns (address result) {
    bytes20 targetBytes = bytes20(target); // convert address to 20 bytes
    
    // runtime code //
    // code to delegatecall to address
    // 363d3d373d3d3d363d73 address 5af43d82803e903d91602b57fd5bf3
    
    // creation code //
    // copy runtime code into memory and return it
    // 3d602d80600a3d3981f3
    
    assembly {
        /*
        reads the 32 bytes of memory starting at position 0x40
        
        In solidity, the 0x40 slot in memory is special: it contains the "free memory pointer"
        which points to the end of the currently allocated memory.
        */
      let clone := mload(0x40)
      // store 32 bytes to memory
      mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
      // store 32 bytes to memory at starting at clone + 20 bytes
      // 0x14 = 20
      mstore(add(clone, 0x14), targetBytes)
      // store 32 bytes to memory at starting at clone + 40 bytes
      // 0x28 = 40
      mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
      // create new contract with code at clone + 0x37 (55 bytes), send 0 Ether
      result := create(0, clone, 0x37)
    }
  }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.