Ethereum Precompiled Contracts

The Bifrost Network supports the standard precompiled contract installed on the Ethereum mainnet. The following is the table of addresses of the implemented precompiled contracts.

ECRECOVER

It returns the signer address of the ecdsa signature (v, r, s) if the signature is valid. It can be called directly in Solidity code.

// contract address: 0x0000000000000000000000000000000000000001
function ecrecover(bytes32 data, bytes32 v, bytes32 r, bytes32 s) returns (address);

SHA256

It returns the keccak-hash value of the data. It can be called directly in Solidity code.

// contract address: 0x0000000000000000000000000000000000000002
function sha256(bytes data) returns (bytes32);

RIPEMD160

It returns the ripemd160-hash value of the data. It can be called directly in Solidity code.

// contract address: 0x0000000000000000000000000000000000000003
function ripemd160(bytes data) returns (bytes32);

Identity

Also known as datacopy, this function serves as a cost-efficient way to copy data in memory. Since the Solidity compiler does not support this function, this function must be called by assembly as follows.

// contract address: 0x0000000000000000000000000000000000000004
contract Identity{

    bytes public memoryStored;

    function callDatacopy(bytes memory data) public returns (bytes memory) {
        bytes memory result = new bytes(data.length);
        assembly {
            let len := mload(data)
            if iszero(call(gas(), 0x04, 0, add(data, 0x20), len, add(result,0x20), len)) {
                invalid()
            }
        }

        memoryStored = result;

        return result;
    }
}

Modular Exponentiation

This function calculates the remainder when an integer b(base) is raised to the e-th power(the exponent) and is divided by a positive integer m(the modulus). Since the Solidity compiler does not support this function, this function must be called by assembly as follows.

// contract address: 0x0000000000000000000000000000000000000005
contract ModularCheck {

    uint public checkResult;

    // Function to Verify ModExp Result
    function verify( uint _base, uint _exp, uint _modulus) public {
        checkResult = modExp(_base, _exp, _modulus);
    }

    function modExp(uint256 _b, uint256 _e, uint256 _m) public returns (uint256 result) {
        assembly {
            // Free memory pointer
            let pointer := mload(0x40)
            // Define length of base, exponent and modulus. 0x20 == 32 bytes
            mstore(pointer, 0x20)
            mstore(add(pointer, 0x20), 0x20)
            mstore(add(pointer, 0x40), 0x20)
            // Define variables base, exponent and modulus
            mstore(add(pointer, 0x60), _b)
            mstore(add(pointer, 0x80), _e)
            mstore(add(pointer, 0xa0), _m)
            // Store the result
            let value := mload(0xc0)
            // Call the precompiled contract 0x05 = bigModExp
            if iszero(call(not(0), 0x05, 0, pointer, 0xc0, value, 0x20)) {
                revert(0, 0)
            }
            result := mload(value)
        }
    }
}

BN128Add

The BN128Add precompiled contract implements a native elliptic curve point addition. It returns an elliptic curve point representing (ax, ay) + (bx, by) such that (ax, ay) and (bx, by) are valid points on the curve BN256. Since the Solidity compiler does not support this function, this function must be called by assembly as follows.

// contract address: 0x0000000000000000000000000000000000000006
contract Precompiles {
    function callBn256Add(bytes32 ax, bytes32 ay, bytes32 bx, bytes32 by) public returns (bytes32[2] memory result) {
        bytes32[4] memory input;
        input[0] = ax;
        input[1] = ay;
        input[2] = bx;
        input[3] = by;
        assembly {
            let success := call(gas, 0x06, 0, input, 0x80, result, 0x40)
            switch success
            case 0 {
                revert(0,0)
            }
        }
    }
}

BN128Mul

The BN128Mul precompiled contract implements a native elliptic curve multiplication with a scalar value. It returns an elliptic curve point representing scalar * (x, y) such that (x, y) is a valid curve point on the curve BN256. Since the Solidity compiler does not support this function, this function must be called by assembly as follows.

// contract address: 0x0000000000000000000000000000000000000007
contract Precompiles {
    function callBn256ScalarMul(bytes32 x, bytes32 y, bytes32 scalar) public returns (bytes32[2] memory result) {
        bytes32[3] memory input;
        input[0] = x;
        input[1] = y;
        input[2] = scalar;
        assembly {
            let success := call(gas, 0x07, 0, input, 0x60, result, 0x40)
            switch success
            case 0 {
                revert(0,0)
            }
        }
    }
}

BN128Pairing

The BN128Pairing precompile implements elliptic curve paring operation to perform zkSNARK verification. For more information, check out the EIP-197 standard. Since the Solidity compiler does not support this function, this function must be called by assembly as follows.

// contract address: 0x0000000000000000000000000000000000000008
contract Precompiles {
    function callBn256Pairing(bytes memory input) public returns (bytes32 result) {
        // input is a serialized bytes stream of (a1, b1, a2, b2, ..., ak, bk) from (G_1 x G_2)^k
        uint256 len = input.length;
        require(len % 192 == 0);
        assembly {
            let memPtr := mload(0x40)
            let success := call(gas, 0x08, 0, add(input, 0x20), len, memPtr, 0x20)
            switch success
            case 0 {
                revert(0,0)
            } default {
                result := mload(memPtr)
            }
        }
    }
}

Blake2F

This EIP will enable the BLAKE2b hash function and other higher-round 64-bit BLAKE2 variants to run cost-effectively on the EVM, allowing easier interoperability between Ethereum and Zcash or any other Equihash-based PoW coin. For more information, check out the EIP-152 standard. Since the Solidity compiler does not support this function, this function must be called by assembly as follows.

// contract address: 0x0000000000000000000000000000000000000009
function callBlake2F(uint32 rounds, bytes32[2] memory h, bytes32[4] memory m, bytes8[2] memory t, bool f) public view returns (bytes32[2] memory) {
    bytes32[2] memory output;

    bytes memory args = abi.encodePacked(rounds, h[0], h[1], m[0], m[1], m[2], m[3], t[0], t[1], f);

    assembly {
        if iszero(staticcall(not(0), 0x09, add(args, 32), 0xd5, output, 0x40)) {
            revert(0, 0)
        }
    }

    return output;
}

Last updated