Git Product home page Git Product logo

simple-web3-php's People

Contributors

cheplv avatar drlecks avatar freaker2k7 avatar joelcho avatar phanthai12 avatar sigri44 avatar slawomir-pryczek 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

simple-web3-php's Issues

Core/ABI->GetEventFromHash throws exception on invalid index.

Currently, the GetEventFromHash is the following

public function GetEventFromHash($event_hash)
 {
         return $this->events_encoded[$event_hash];
 }

In other parts of code where this is called, from Contract, you're checking to see if(null) but the above will never return null. It assumes that you have a valid ABI for every contract your are checking events from. That's not always the case. So, if the event is undefined by the contract ABI that I provide, shouldn't we return null?

public function GetEventFromHash($event_hash)
    {
        if( !empty( $this->events_encoded[$event_hash] ) ) {
            return $this->events_encoded[$event_hash];
        } else {
            return null;
        }
    }

My scenario is that I need to get all transfer events of unknown ERC20 contracts to track amounts transferred, but these unknown contracts could have other events besides the default Transfer and Approve and is therefore throwing an exception.


  Undefined array key "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"

  at vendor/drlecks/simple-web3-php/Core/ABI.php:92
     88▕ 
     89▕ 
     90▕     public function GetEventFromHash($event_hash)
     91▕     {
  ➜  92▕         return $this->events_encoded[$event_hash];
     93▕     }

Tuple Singature different ..

Hi, i did what you were saying in my other Issue, and now at least the function gets called, but it fails, as the sent datas are wrong. The hash i am sending is completly different to what i would send in a real or even if i am using abi https://abi.hashex.org/

is there any way to contact you to send you over some details to look at it? i did read everything but i cant find any idea why this is happening.

its only because of that tuple.. ^^

thx

Please upgrade kornrunner/ethereum-offline-raw-tx ^0.4.0 to ^0.6.0

Currently, this library is using version ^0.4.0 of kornrunner/ethereum-offline-raw-tx:

"kornrunner/ethereum-offline-raw-tx": "^0.4.0",

However, I noticed that version 0.6.0 of kornrunner/ethereum-offline-raw-tx is now available.
https://github.com/kornrunner/php-ethereum-offline-raw-tx/releases

This version upgrade has transitioned from using web3p/rlp to kornrunner\rlp, which seems to have fixed some bugs. In my local environment, I've encountered an issue with web3p/rlp where the encoding of hex omits the 00, but this issue does not occur with kornrunner\rlp.

Best regards.

合约解析 数组元组模式出现错误

例如下面的abi
"name": "getPairs",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "pairId",
"type": "uint256"
},
{
"internalType": "address",
"name": "pancakePair",
"type": "address"
},
{
"internalType": "address",
"name": "token0",
"type": "address"
},
{
"internalType": "address",
"name": "token1",
"type": "address"
},
{
"internalType": "uint256",
"name": "balance0",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "balance1",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "decimals0",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "decimals1",
"type": "uint256"
}
],
"internalType": "struct GetPairs.pairInfo[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"

正常应该返回类似下面的数据
image

使用该sdk返回的是
image

看起来像是DecodeInput_UInt_Internal计算错误

Seems fixed size array inside struct (tuple) breaks encoding.

Firstly, thanks for the great work here. This package the best for Web3 now. I was running some aggressive testing on the package especially on encoding and decoding, since I plan to replace a fork with this original, in a package I maintain. Mostly so far so good.
But It seems this struct encoded output in incorrect

       const betType = [
            "bool",
            "bytes32[2]",
            "uint256",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8"
        ]

        const bet = {
            "boolOutcome": true,
            "compoundBet": [
                '0x0000000000000000000000000000000000000000000000000000000000000000',
                '0x0000000000000000000000000000000000000000000000000000000000000000'
            ],
            "gold": 0,
            "mode": 20,
            "half": 0,
            "gameTime": 0,
            "team": 0,
            "line": 0,
            "doubleChanceOutcome": 0,
            "goalOutcome": 0,
            "exactScoreOutcome": 0,
            "resultOutcome": 0,
            "rangeOutcome": 0,
            "mixedOutcome": 0,
            "highestScoreOutcome": 0,
            "bothHalfsOutcome": 0
        };
        const coder = new ethers.AbiCoder();
        const encoded =  ethers.keccak256(coder.encode(betType, Object.values(bet)));
        console.log(encoded);

       //0x1f88539754112ef6b7420c2ecd2fbc4c33ed3a206012a595b209803b15cb99a6

Same in PHP

use kornrunner\Keccak;
use SWeb3\ABI;

$bet = json_decode('{
            "boolOutcome": true,
            "compoundBet": [
                "0x0000000000000000000000000000000000000000000000000000000000000000",
                "0x0000000000000000000000000000000000000000000000000000000000000000"
            ],
            "gold": 0,
            "mode": 20,
            "half": 0,
            "gameTime": 0,
            "team": 0,
            "line": 0,
            "doubleChanceOutcome": 0,
            "goalOutcome": 0,
            "exactScoreOutcome": 0,
            "resultOutcome": 0,
            "rangeOutcome": 0,
            "mixedOutcome": 0,
            "highestScoreOutcome": 0,
            "bothHalfsOutcome": 0
        }');
    $encoded = ABI::EncodeParameters_External(
        [
            "bool",
            "bytes32[2]",
            "uint256",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8",
            "uint8"
        ],
        array_values((array)$bet)
    );
    $hash =  Keccak::hash(hex2bin(substr($encoded, 2)), 256);
    dd($hash);

///61785fba8f24fb21b94323ba2b99866fe47135cdce2fca7984799c0ac768dacb

You many need to make some minor modifications to the SWeb3\ABI.php file for this to work,
I have posted the ABI file below.

The issues seems to be on this array

"bytes32[2]",

deleting this variable yields the same result for both ethers and Simple-Web3-Php

e446808eedf5b6afd7944305e8a566cb443e2ff9674855e5248de7529fe7111c

here is my modified SWeb3 \ABI file to save you time when testing

<?php

/**
 * This file is part of simple-web3-php package.
 * 
 * (c) Alex Cabrera  
 * 
 * @author Alex Cabrera
 * @license MIT 
 */

//https://docs.soliditylang.org/en/develop/abi-spec.html

namespace SWeb3;

abstract class VariableType
{
    const None = 0;
    const Array = 1;
    const Tuple = 2;
    const String = 3;
    const Address = 4;
    const Int = 5;
    const UInt = 6;
    const Bool = 7;
    const Bytes = 8;
    const BytesFixed = 9;
}


use stdClass;
use Exception;
use kornrunner\Keccak;
use phpseclib\Math\BigInteger as BigNumber;

class ABI
{
    private $baseJSON;
    public $constructor;
    public $functions;
    public $events;
    public $other_objects;

    //dictionary of encoded signature => function
    public $events_encoded;

    const NUM_ZEROS = 64;


    public function Init($baseJSON)
    {
        $this->functions = [];
        $this->events = [];
        $this->other_objects = [];
        $this->events_encoded = [];
        $parsedJSON = json_decode($baseJSON);

        foreach ($parsedJSON as $func) {
            if ($func->type == 'constructor') {
                $this->constructor = $func;
            } else if ($func->type == 'event') {
                $this->events[$func->name] = $func;
                $this->events_encoded[$this->GetSignatureFromEvent($func)] = $func;
            } else if ($func->type == 'function') {
                $this->functions[$func->name] = $func;
            } else {
                $this->other_objects[] = $func;
            }
        }
    }


    public function GetFunction($function_name)
    {
        if ($function_name == '') return $this->constructor;
        return $this->functions[$function_name];
    }


    public function GetEvent($event_name)
    {
        return $this->events[$event_name];
    }


    public function GetEventFromHash($event_hash)
    {
        return $this->events_encoded[$event_hash];
    }


    private static function GetParameterType(?string $abi_string_type)
    {
        //bytes
        if ($abi_string_type == 'bytes')                                return VariableType::Bytes;
        else if (Utils::string_contains($abi_string_type, 'bytes'))     return VariableType::BytesFixed;

        //dynamic
        else if (Utils::string_contains($abi_string_type, 'tuple'))     return VariableType::Tuple;
        else if (Utils::string_contains($abi_string_type, 'string'))       return VariableType::String;

        //static 
        else if (Utils::string_contains($abi_string_type, 'uint'))        return VariableType::UInt;
        else if (Utils::string_contains($abi_string_type, 'int'))         return VariableType::Int;
        else if (Utils::string_contains($abi_string_type, 'fixed'))       return VariableType::Int;
        else if (Utils::string_contains($abi_string_type, 'bool'))         return VariableType::Bool;
        else if (Utils::string_contains($abi_string_type, 'address'))      return VariableType::Address;

        //error
        else {
            var_dump("parameter error: " . $abi_string_type);
        }

        return VariableType::Int;
    }


    private static function IsStaticParameter(int $vType): bool
    {
        return $vType == VariableType::UInt
            || $vType == VariableType::Int
            || $vType == VariableType::Bool
            || $vType == VariableType::Address
            || $vType == VariableType::BytesFixed;
    }


    private static function ExistsDynamicParameter(array $components): bool
    {
        foreach ($components as $comp) {
            if (is_string($comp)) {
                $isStatic = self::IsStaticParameter(self::GetParameterType($comp));
            } else {
                if (isset($comp->components)) {
                    $isStatic = !self::ExistsDynamicParameter($comp->components);
                } else {
                    $isStatic = self::IsStaticParameter(self::GetParameterType($comp->type));
                }
            }

            if (!$isStatic) {
                return true;
            }
        }

        return false;
    }


    public function isCallFunction($function_name)
    {
        $function = $this->GetFunction($function_name);
        if ($function == null) return false;

        $stateMutability = "";
        if (isset($function->stateMutability)) $stateMutability = $function->stateMutability;

        return ($stateMutability == 'pure' || $stateMutability == 'view');
    }


    public function isSendFunction($function_name)
    {
        $function = $this->GetFunction($function_name);
        if ($function == null) return false;

        $stateMutability = "";
        if (isset($function->stateMutability)) $stateMutability = $function->stateMutability;

        return ($stateMutability != 'pure' && $stateMutability != 'view');
    }



    /***************************************ENCODE  */


    public function EncodeData($function_name, $data)
    {
        $function = $this->GetFunction($function_name);
        $data = $this->forceWrapperArray($function, $data);

        $hashData = "0x";

        if ($function_name != '') {
            //function signature (first 4 bytes) (not for constructor)
            $signature = $this->GetSignatureFromFunction($function);
            $sha3 = Keccak::hash($signature, 256);
            $hashData .= substr($sha3, 0, 8);
        }

        if ($function !== null) {
            $hashData .= self::EncodeGroup($function->inputs, $data);
        }

        return $hashData;
    }


    public function GetSignatureFromEvent($function)
    {
        $signature = $this->GetSignatureFromFunction($function);
        return  '0x' . Keccak::hash($signature, 256);
    }


    private function GetSignatureFromFunction($function)
    {
        $signature = $function->name . $this->GetSignatureFromFunction_Inputs($function->inputs);
        return $signature;
    }


    private function GetSignatureFromFunction_Inputs($function_inputs)
    {
        $signature = "(";
        foreach ($function_inputs as $input) {
            $type = $input->type;
            if ($type == 'tuple') $type = $this->GetSignatureFromFunction_Inputs($input->components);
            else if ($type == 'tuple[]') $type = $this->GetSignatureFromFunction_Inputs($input->components) . '[]';
            else if ($type == 'uint' || $type == 'int') $type .= '256';
            else if ($type == 'uint[]') $type = 'uint256[]';
            else if ($type == 'int[]') $type = 'int256[]';

            $signature .= $type . ',';
        }

        if (count($function_inputs) > 0)  $signature = substr($signature, 0, -1);
        $signature .= ')';

        return $signature;
    }


    private function forceWrapperArray($function, $data)
    {
        if ($function === null || count($function->inputs) === 0) {
            $data = [];
        } else if ($data === null) {
            $data = [];
        } else if (!is_array($data)) {
            $data = [$data];
        } else {
            $tempData = $data;
            $input = $function->inputs[0];
            $input_array_dimension = substr_count($input->type, '[');

            while ($input_array_dimension > 0) {
                if (is_array($tempData[0])) {
                    if ($input_array_dimension == 1) break;
                    else $tempData = $tempData[0];
                } else {
                    $data = [$data];
                    break;
                }

                $input_array_dimension--;
            }
        }

        return $data;
    }


    public static function EncodeGroup(array $inputs, $data): string
    {
        $hashData = "";
        $currentDynamicIndex = 0; {
            $staticInputCount = 0;
            foreach ($inputs as $input) {
                $varType = self::GetParameterType($input->type);

                // for non-tuple item, we'll have in-place value or offset
                if ($varType != VariableType::Tuple) {
                    $staticInputCount++;
                    continue;
                }

                // for tuple we'll have several in place values or one pointer to the start of several in-place values
                if (self::ExistsDynamicParameter($input->components)) {
                    $staticInputCount++;
                } else {
                    $staticInputCount += count($input->components);
                }
            }
            $currentDynamicIndex = $staticInputCount * self::NUM_ZEROS / 2;
        }

        //parameters
        $i = 0;
        foreach ($inputs as $pos => $input) {
            $var_name = $pos;
            if (is_object($input)) {
                if (isset($input->name)) $var_name = $input->name;
            } else if (is_string($input)) {
                $var_name =  $input;
            }

            $inputData = is_object($data) ? $data->$var_name : $data[$pos];
            if (is_array($data) && $inputData === null) $inputData = $data[$var_name];

            $hashData .= self::EncodeInput($input, $inputData, 1, $currentDynamicIndex);

            if (isset($input->hash)) $currentDynamicIndex += strlen($input->hash) / 2;
            $i++;
        }

        foreach ($inputs as $pos => $input) {
            $hashData .= self::EncodeInput($input, null, 2, $currentDynamicIndex);
        }

        if (count($inputs) == 0) {
            $hashData .= self::NUM_ZEROS / 2;
        }

        return $hashData;
    }


    public static function EncodeParameters_External(array $input_types, array $data): string
    {
        $inputs = array();
        foreach ($input_types as $it) {
            $input = new stdClass();
            $input->name = $it;
            $input->type = $it;
            $inputs[] = $input;
        }

        return '0x' . self::EncodeGroup($inputs, $data);
    }


    public static function EncodeParameter_External(string $input_name, $data): string
    {
        $hashData = "";
        $currentDynamicIndex = self::NUM_ZEROS / 2;

        $input = new stdClass();
        $input->type = $input_name;

        $hashData .= self::EncodeInput($input, $data, 1, $currentDynamicIndex);

        if (isset($input->hash)) $currentDynamicIndex += strlen($input->hash) / 2;

        $hashData .= self::EncodeInput($input, null, 2, $currentDynamicIndex);

        return '0x' . $hashData;
    }




    private static function EncodeInput_Array($full_input, $inputData)
    {
        $inputs = [];
        $currentDynamicIndex = count($inputData) * self::NUM_ZEROS / 2;

        //prepare clean input 
        $last_array_marker     = strrpos($full_input->type, '[');
        $clean_type         = substr($full_input->type, 0, $last_array_marker);

        // $last_array_marker     = strrpos($full_input->internalType, '[');
        // $clean_internalType = substr($full_input->internalType, 0, $last_array_marker);

        //array length
        $hashData = self::EncodeInput_UInt(count($inputData));

        foreach ($inputData as $pos => $element) {
            $input = new stdClass();
            $input->type = $clean_type;
            //$input->internalType = $clean_internalType; 
            if (isset($full_input->components)) {
                $input->components = $full_input->components;
            }
            $inputs[] = $input;

            $hashData .= self::EncodeInput($input, $element, 1, $currentDynamicIndex);

            if (isset($input->hash)) $currentDynamicIndex += strlen($input->hash) / 2;
        }

        foreach ($inputs as $pos => $input) {
            $data = $inputData[$pos];
            $hashData .= self::EncodeInput($input, $data, 2, $currentDynamicIndex);
        }

        if (count($inputs) == 0) {
            $hashData .= self::NUM_ZEROS / 2;
        }

        return $hashData;
    }


    private static function EncodeInput($input, $inputData, $round, &$currentDynamicIndex)
    {
        $hash = "";

        if ($round == 1) {
            $input_type = is_string($input) ? $input : $input->type;
            $varType = self::GetParameterType($input_type);

            //dynamic
            if (Utils::string_contains($input->type, '[')) {
                $input->hash =  self::EncodeInput_Array($input, $inputData);
                $res = self::EncodeInput_UInt($currentDynamicIndex);
                return $res;
            } else if ($varType == VariableType::Tuple) {
                $res = self::EncodeGroup($input->components, $inputData);

                // if the tuple is dynamic, we return offset and add tuple's data at the end
                if (self::ExistsDynamicParameter($input->components)) {
                    $input->hash = $res;
                    return self::EncodeInput_UInt($currentDynamicIndex);
                }
                return $res;
            } else if ($varType == VariableType::String) {
                $input->hash = self::EncodeInput_String($inputData);
                $res = self::EncodeInput_UInt($currentDynamicIndex);
                return $res;
            } else if ($varType == VariableType::Bytes) {
                $input->hash = self::EncodeInput_Bytes($inputData);
                $res = self::EncodeInput_UInt($currentDynamicIndex);
                return $res;
            }
            //static
            else if ($varType == VariableType::UInt) {
                return self::EncodeInput_UInt($inputData);
            } else if ($varType == VariableType::Int) {
                return self::EncodeInput_Int($inputData);
            } else if ($varType == VariableType::Bool) {
                return self::EncodeInput_Bool($inputData);
            } else if ($varType == VariableType::Address) {
                return self::EncodeInput_Address($inputData);
            } else if ($varType == VariableType::BytesFixed) {
                return self::EncodeInput_BytesFixed($inputData);
            }
        } else if ($round == 2) {
            if (isset($input->hash) and $input->hash != '') {
                return $input->hash;
            }
        }

        return  $hash;
    }

    private static function EncodeInput_UInt($data)
    {
        if (is_string($data) && ctype_digit($data)) {
            $bn = Utils::toBn($data);
            $hash = self::AddZeros($bn->toHex(true), true);
        } else if ($data instanceof BigNumber) {
            $hash = self::AddZeros($data->toHex(true), true);
        } else if (is_int($data) || is_long($data)) {
            $hash = self::AddZeros(dechex($data), true);
        } else {
            throw new Exception("EncodeInput_UInt, not valid input type");
        }

        return  $hash;
    }

    private static function EncodeInput_Int($data)
    {
        if (is_string($data) && ctype_digit($data)) {
            $bn = Utils::toBn($data);
            $hash = self::AddNegativeF($bn->toHex(true), true);
        } else if ($data instanceof BigNumber) {
            if ($data->toString()[0] == '-')
                $hash = self::AddNegativeF($data->toHex(true), true);
            else
                $hash = self::AddZeros($data->toHex(true), true);
        } else  if (is_int($data) || is_long($data)) {
            $hash = self::AddZerosOrF(dechex($data), true);
        } else {
            throw new Exception("EncodeInput_Int, not valid input type");
        }

        return  $hash;
    }

    private static function EncodeInput_Bool($data)
    {
        $hash = $data ? '1' : '0';
        $hash = self::AddZeros($hash, true);
        return  $hash;
    }

    private static function EncodeInput_Address($data)
    {
        $hash = self::AddZeros(substr($data, 2), true);
        return  $hash;
    }

    private static function EncodeInput_String($data)
    {
        //length + hexa string
        $hash = self::EncodeInput_UInt(strlen($data)) . self::AddZeros(bin2hex($data), false);

        return  $hash;
    }

    private static function EncodeInput_Bytes($data)
    {
        $hexa = $data;

        //I'm not proud of this. Official parsers seem to handle 0x as 0x0 when input is type bytes
        //I think it can cause problems when you want to use bytes as a string, because you can't save the string "0x"
        //but looking at issue #50 it seems clear that the current evm behaviour is this.
        if ($data == '0x') {
            $data = '';
        }

        //if data is not a valid hexa, it means its a binary rep
        if (substr($data, 0, 2) != '0x' || !ctype_xdigit(substr($data, 2)) || strlen($data) % 2 != 0) {
            $hexa = bin2hex($data);
        }

        if (substr($hexa, 0, 2) == '0x') {
            $hexa = substr($hexa, 2);
        }

        //length + hexa string
        $hash = self::EncodeInput_UInt(strlen($hexa) / 2) . self::AddZeros($hexa, false);

        return  $hash;
    }


    private static function EncodeInput_BytesFixed($data)
    {
        $hexa = $data;

        //if data is not a valid hexa, it means its a binary rep
        if (substr($data, 0, 2) == '0x' || !ctype_xdigit(substr($data, 2)) || strlen($data) % 2 != 0) {
            $hexa = substr($data, 2);
        }

        if (substr($hexa, 0, 2) == '0x') {
            $hexa = substr($hexa, 2);
        }

        //length + hexa string
        $hash = self::AddZeros($hexa, false);

        return  $hash;
    }


    private static function AddZeros($data, $add_left)
    {
        $remaining = strlen($data);
        if ($remaining > self::NUM_ZEROS) $remaining %= self::NUM_ZEROS;
        $total = self::NUM_ZEROS - $remaining;

        $res = $data;

        if ($total > 0) {
            for ($i = 0; $i < $total; $i++) {
                if ($add_left)   $res = '0' . $res;
                else            $res .= '0';
            }
        }

        return $res;
    }


    private static function AddNegativeF($data, $add_left)
    {
        $remaining = strlen($data);
        if ($remaining > self::NUM_ZEROS) $remaining %= self::NUM_ZEROS;
        $total = self::NUM_ZEROS - $remaining;

        $res = $data;

        if ($total > 0) {
            for ($i = 0; $i < $total; $i++) {
                if ($add_left)   $res = 'f' . $res;
                else            $res .= 'f';
            }
        }

        return $res;
    }


    private static function AddZerosOrF($data, $add_left)
    {
        $valueToAdd = (strtolower($data[0]) == 'f' && strlen($data) == 16) ? 'f' : '0';

        $remaining = strlen($data);
        if ($remaining > self::NUM_ZEROS) $remaining %= self::NUM_ZEROS;
        $total = self::NUM_ZEROS - $remaining;

        $res = $data;

        if ($total > 0) {
            for ($i = 0; $i < $total; $i++) {
                if ($add_left)   $res = $valueToAdd . $res;
                else            $res .= $valueToAdd;
            }
        }

        return $res;
    }


    /***************************************DECODE  */

    public function DecodeData($function_name, $encoded)
    {
        $encoded = substr($encoded, 2);
        $function = $this->GetFunction($function_name);

        $decoded = self::DecodeGroup($function->outputs, $encoded, 0);

        return $decoded;
    }


    public static function DecodeGroup($outputs, $encoded, $index)
    {
        $group             = new stdClass();
        $first_index     = $index;
        $elem_index     = 1;
        $tuple_count     = 1;
        $array_count     = 1;
        $output_count     = count($outputs);


        foreach ($outputs as $output) {
            $output_type         = is_string($output) ? $output : $output->type;
            $varType             = self::GetParameterType($output_type);
            $output_type_offset = self::GetOutputOffset($output);
            $var_name             = '';

            //dynamic
            if (Utils::string_contains($output->type, '[')) {
                $var_name             = $output->name != '' ? $output->name : 'array_' . $array_count;

                //arrays with all static parameters have no initial array offset 
                $isStaticArray = self::IsStaticParameter($varType);
                if ($varType == VariableType::Tuple) {
                    $isStaticArray = !self::ExistsDynamicParameter($output->components);
                }
                $isStaticLength = $isStaticArray && !Utils::string_contains($output->type, '[]');

                $dynamic_data_start = 0;
                if ($isStaticLength)     $dynamic_data_start = $index;
                else                     $dynamic_data_start = $first_index + self::DecodeInput_UInt_Internal($encoded, $index) * 2;

                $group->$var_name = self::DecodeInput_Array($output, $encoded, $dynamic_data_start);
                $array_count++;
            } else if ($varType == VariableType::Tuple) {
                $var_name = $output->name != '' ? $output->name : 'tuple_' . $tuple_count;

                //tuples with only static parameters have no initial tuple offset
                $hasDynamicParameters     = self::ExistsDynamicParameter($output->components);

                $dynamic_data_start     = $index;
                if ($hasDynamicParameters) {
                    $dynamic_data_start = $first_index + self::DecodeInput_UInt_Internal($encoded, $index) * 2;
                }

                $group->$var_name = self::DecodeGroup($output->components, $encoded, $dynamic_data_start);
                $tuple_count++;
            } else if ($varType == VariableType::String) {
                $var_name             = $output->name != '' ? $output->name : 'elem_' . $elem_index;
                $dynamic_data_start = $first_index + self::DecodeInput_UInt_Internal($encoded, $index) * 2;
                $group->$var_name     = self::DecodeInput_String($encoded, $dynamic_data_start);
            } else if ($varType == VariableType::Bytes) {
                $var_name             = $output->name != '' ? $output->name : 'elem_' . $elem_index;
                $dynamic_data_start = $first_index + self::DecodeInput_UInt_Internal($encoded, $index) * 2;
                $group->$var_name     = self::DecodeInput_Bytes($encoded, $dynamic_data_start);
            }
            //static
            else {
                $var_name = 'result';
                if ($output->name != '')      $var_name = $output->name;
                else if ($output_count > 1)     $var_name = 'elem_' . $elem_index;

                $group->$var_name = self::DecodeInput_Generic($varType, $encoded, $index);
            }

            $elem_index++;
            $index += $output_type_offset * self::NUM_ZEROS;
        }

        return $group;
    }


    public static function DecodeParameter_External(string $output_type, string $encoded)
    {
        $output = new stdClass();
        $output->type = $output_type;
        $varType = self::GetParameterType($output_type);
        $dynamic_data_start = self::NUM_ZEROS;

        $res = "";

        if (substr($encoded, 0, 2) == '0x') {
            $encoded = substr($encoded, 2);
        }

        //dynamic
        if (Utils::string_contains($output->type, '[')) {
            $res = self::DecodeInput_Array($output, $encoded, $dynamic_data_start);
        } else if ($varType == VariableType::Tuple) {
            //tuples with only static parameters have no initial tuple offset 
            $res = self::DecodeGroup($output->components, $encoded, $dynamic_data_start);
        } else if ($varType == VariableType::String) {
            $res = self::DecodeInput_String($encoded, $dynamic_data_start);
        } else if ($varType == VariableType::Bytes) {
            $res = self::DecodeInput_Bytes($encoded, $dynamic_data_start);
        }
        //simple 
        else {
            $res = self::DecodeInput_Generic($varType, $encoded, 0);
        }

        return $res;
    }


    private static function DecodeInput_Array($output, $encoded, $index)
    {
        $array = [];
        $first_index = $index;

        $clean_output = clone $output;
        $last_array_marker     = strrpos($clean_output->type, '[');
        $clean_output->type     = substr($clean_output->type, 0, $last_array_marker);

        $varType         = self::GetParameterType($clean_output->type);
        $isStaticType     = self::IsStaticParameter($varType);
        if ($varType == VariableType::Tuple) {
            $isStaticType = !self::ExistsDynamicParameter($output->components);
        }

        $length = 0;
        if ($isStaticType) {
            $last_array_marker_end     = strrpos($output->type, ']');
            $length                 = (int) substr($output->type, $last_array_marker + 1, $last_array_marker_end - $last_array_marker - 1);
        }

        if ($length <= 0) {
            $length         = self::DecodeInput_UInt_Internal($encoded, $first_index);
            $first_index     += self::NUM_ZEROS;
            $index             += self::NUM_ZEROS;
        }

        $element_offset = 1;
        if ($isStaticType) {
            $element_offset = self::GetOutputOffset($clean_output);
        }

        for ($i = 0; $i < $length; $i++) {
            $res = "error";
            if (Utils::string_contains($clean_output->type, '[')) {
                $isStaticLength = $isStaticType && !Utils::string_contains($clean_output->type, '[]');
                //arrays with all static parameters have no initial array offset 
                $element_start = $index;
                if ($isStaticLength) {
                    $element_start = $index;
                } else {
                    $element_start = $first_index + self::DecodeInput_UInt_Internal($encoded, $index) * 2;
                }

                $res = self::DecodeInput_Array($clean_output, $encoded, $element_start);
            } else if ($varType == VariableType::Tuple) {
                //tuple with all static parameters have no initial array offset  
                if ($isStaticType) {
                    $element_start = $index;
                } else {
                    $element_start = $first_index + self::DecodeInput_UInt_Internal($encoded, $index) * 2;
                }

                $res = self::DecodeGroup($clean_output->components, $encoded, $element_start);
            } else if ($varType == VariableType::String) {
                $element_start = $first_index + self::DecodeInput_UInt_Internal($encoded, $index) * 2;
                $res = self::DecodeInput_String($encoded, $element_start);
            } else if ($varType == VariableType::Bytes) {
                $element_start = $first_index + self::DecodeInput_UInt_Internal($encoded, $index) * 2;
                $res = self::DecodeInput_Bytes($encoded, $element_start);
            } else {
                $res = self::DecodeInput_Generic($varType, $encoded, $index);
            }

            $array[] = $res;
            $index += self::NUM_ZEROS * $element_offset;
        }

        return $array;
    }


    private static function DecodeInput_Generic($variableType, $encoded, $start)
    {
        if ($variableType == VariableType::String) {
            return self::DecodeInput_String($encoded, $start);
        } else if ($variableType == VariableType::UInt) {
            return self::DecodeInput_UInt($encoded, $start);
        } else if ($variableType == VariableType::Int) {
            return self::DecodeInput_Int($encoded, $start);
        } else if ($variableType == VariableType::Bool) {
            return self::DecodeInput_Bool($encoded, $start);
        } else if ($variableType == VariableType::Address) {
            return self::DecodeInput_Address($encoded, $start);
        } else if ($variableType == VariableType::BytesFixed) {
            return self::DecodeInput_BytesFixed($encoded, $start);
        } else if ($variableType == VariableType::Bytes) {
            return self::DecodeInput_Bytes($encoded, $start);
        }
    }


    private static function DecodeInput_UInt_Internal($encoded, $start)
    {
        $partial = substr($encoded, $start, 64);
        $partial = self::RemoveZeros($partial, true);

        return hexdec($partial);
    }


    private static function DecodeInput_UInt($encoded, $start)
    {
        $partial = substr($encoded, $start, 64);
        $partial = self::RemoveZeros($partial, true);

        $partial_big = new BigNumber($partial, 16);

        return $partial_big;
    }


    private static function DecodeInput_Int($encoded, $start)
    {
        $partial = substr($encoded, $start, 64);
        $partial_big = new BigNumber($partial, -16);

        return $partial_big;
    }


    private static function DecodeInput_Bool($encoded, $start)
    {
        $partial = substr($encoded, $start, 64);
        $partial = self::RemoveZeros($partial, true);
        return $partial == '1';
    }


    private static function DecodeInput_Address($encoded, $start)
    {
        $partial = self::RemoveZeros(substr($encoded, $start, 64), true);

        //add zero padding from left for 20 bytes
        $partial = str_pad($partial, 40, '0', STR_PAD_LEFT);

        return '0x' . $partial;
    }


    private static function DecodeInput_String($encoded, $start)
    {
        $length = self::DecodeInput_UInt_Internal($encoded, $start);
        $start += self::NUM_ZEROS;

        $partial = substr($encoded, $start, $length * 2);
        return hex2bin($partial);
    }


    private static function DecodeInput_BytesFixed($encoded, $start)
    {
        $partial = self::RemoveZeros(substr($encoded, $start, 64), false);
        return hex2bin($partial);
    }


    private static function DecodeInput_Bytes($encoded, $start)
    {
        $length = self::DecodeInput_UInt_Internal($encoded, $start);
        $start += self::NUM_ZEROS;

        $partial = substr($encoded, $start, $length * 2);
        return hex2bin($partial);
    }


    private static function RemoveZeros($data, $remove_left)
    {
        $index = $remove_left ? 0 : strlen($data) - 1;
        $current = substr($data, $index, 1);
        while ($current == '0') {
            if ($remove_left) {
                $data = substr($data, 1, strlen($data) - 1);
            } else {
                $data = substr($data, 0, -1);
                $index--;
            }
            $current = substr($data, $index, 1);
        }

        return $data;
    }


    private static function GetOutputOffset($output): int
    {
        $output_type     = is_string($output) ? $output : $output->type;
        $varType         = self::GetParameterType($output_type);

        if (Utils::string_contains($output_type, '[')) {
            $last_array_marker         = strrpos($output->type, '[');
            $last_array_marker_end     = strrpos($output->type, ']');
            $length = (int) substr($output->type, $last_array_marker + 1, $last_array_marker_end - $last_array_marker - 1);

            if ($length > 0) {
                if ($varType == VariableType::Tuple) {
                    if (!self::ExistsDynamicParameter($output->components)) {
                        return $length * self::GetOutputOffset_StaticComponents($output->components);
                    }
                } else if (self::IsStaticParameter($varType)) {
                    return $length;
                }
            }
        } else if ($varType == VariableType::Tuple) {
            if (!self::ExistsDynamicParameter($output->components)) {
                return self::GetOutputOffset_StaticComponents($output->components);
            }
        }

        return 1;
    }


    private static function GetOutputOffset_StaticComponents($components): int
    {
        $offset = 0;

        foreach ($components as $comp) {
            $output_type     = is_string($comp) ? $comp : $comp->type;
            $varType         = self::GetParameterType($output_type);

            if (Utils::string_contains($output_type, '[') || $varType == VariableType::Tuple) {
                $offset += self::GetOutputOffset($comp);
            } else {
                $offset++;
            }
        }

        return $offset;
    }



    //EVENTS

    //parses event parameters
    //event inputs are splitted between indexed topics and encoded data string
    public function DecodeEvent($event_object, $log): stdClass
    {
        $res = new stdClass();
        $res->indexed = array();
        $res->indexed[] = $event_object->name;

        $res->data = array();

        //split inputs between indexed and raw data
        $indexed_index = 1;
        $data_inputs = array();

        foreach ($event_object->inputs as $input) {
            if ($input->indexed) {
                $input_type = is_string($input) ? $input : $input->type;
                $varType = self::GetParameterType($input_type);
                $res->indexed[$input->name] = $this->DecodeInput_Generic($varType, $log->topics[$indexed_index], 2);

                $indexed_index++;
            } else {
                $data_inputs[] = $input;
            }
        }

        //parse raw data
        $encoded = substr($log->data, 2);
        $res->data = $this->DecodeGroup($data_inputs, $encoded, 0);

        //Return
        return $res;
    }
}

\drlecks\simple-web3-php\Core\ABI.php:272 Error type

Приветствую, есть вот такая ошибка.

{#1527 ▼ // vendor\drlecks\simple-web3-php\Core\ABI.php:272 +"type": "bytes32" }
Line 271 - $var_name = $input->name;

Без имени

Пытается взять name но дает type

$line = ABI::EncodeParameters_External(['bytes32', 'address', 'address', 'uint256'], ['0xc0d4a6ac8f8fb36bff3758bb7eaad7f9ce4f649ace74192d64d0625c1b7e0943', 'asd', 'asdasd', 10]);

why is there a memory shortage during the Call contract command?

why is there a memory shortage during the Call contract command?

Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 33554432 bytes) in /web3/vendor/drlecks/simple-web3-php/Core/ABI.php on line 812

Even if the PHP memory is set to 2048M, there is still an error indicating insufficient memory.

The php script is as follows:

require 'vendor/autoload.php'; 

use SWeb3\SWeb3;
use SWeb3\SWeb3_Contract;

//initialize SWeb3 main object
$sweb3 = new SWeb3('https://bsc-dataseed1.defibit.io/');

$contractABI = '[{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"userInfo","outputs":[{"components":[{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"bool","name":"isUser","type":"bool"},{"internalType":"address","name":"upline","type":"address"},{"internalType":"uint256","name":"checkpoint","type":"uint256"},{"internalType":"uint256","name":"invested","type":"uint256"},{"internalType":"uint256","name":"refReward","type":"uint256"},{"internalType":"uint256","name":"ltrReward","type":"uint256"},{"internalType":"uint256","name":"earnReward","type":"uint256"},{"internalType":"uint256","name":"amtFull","type":"uint256"},{"internalType":"uint256","name":"amtEarn","type":"uint256"},{"internalType":"uint256","name":"amtDaily","type":"uint256"},{"internalType":"uint256","name":"beforeCutoff","type":"uint256"},{"internalType":"uint256[3]","name":"refCounts","type":"uint256[3]"},{"internalType":"uint256","name":"ht_invested","type":"uint256"},{"internalType":"uint256","name":"ht_refReward","type":"uint256"},{"internalType":"uint256","name":"wk_refReward","type":"uint256"},{"internalType":"uint256","name":"wk_percentage","type":"uint256"},{"internalType":"uint256","name":"ht_ltrReward","type":"uint256"},{"internalType":"uint256","name":"ht_earned","type":"uint256"},{"internalType":"uint256","name":"ht_compounded","type":"uint256"},{"internalType":"uint256","name":"ht_withdrawn","type":"uint256"},{"internalType":"uint256","name":"lotteryTickets","type":"uint256"}],"internalType":"struct TRUEFUND_V2.DataUserEx","name":"_dataUserEx","type":"tuple"},{"components":[{"internalType":"enum TRUEFUND_V2.Flags","name":"flag","type":"uint8"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"uint256","name":"amt1","type":"uint256"},{"internalType":"uint256","name":"amt2","type":"uint256"},{"internalType":"uint256","name":"amt3","type":"uint256"},{"internalType":"address","name":"addr1","type":"address"},{"internalType":"address","name":"addr2","type":"address"}],"internalType":"struct TRUEFUND_V2.History[10]","name":"_history","type":"tuple[10]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OWNER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]';

$contractAddress = '0x6485E794e03b12366c1607bDAA1581Dc3bd73F0F';

$contract = new SWeb3_contract($sweb3, $contractAddress, $contractABI);
//$res = $contract->call('OWNER');		//pass
//print_r($res);exit;

$params = [
	'_user' => '0x3f7BBDEBc9397116693eFF279395400146C15653'
];

$res = $contract->call('userInfo', $params);
print_r($res);

how to solve it?

Error on hex BN convert

Hello,

I don't know how to BN is converted, but result from smart contract is different (int256). Don't have the same result :
image

image

Debug code is :

gettype($res) :
string(6) "object"

$res :
object(stdClass)#129 (1) { ["result"]=> object(phpseclib\Math\BigInteger)#128 (2) { ["value"]=> string(22) "0x88fc3778e02d105c0000" ["engine"]=> string(3) "gmp" } }

gettype($res->result) :
string(6) "object"

$res->result :
object(phpseclib\Math\BigInteger)#128 (2) { ["value"]=> string(22) "0x88fc3778e02d105c0000" ["engine"]=> string(3) "gmp" }

$sweb3->utils->isHex("0x89056739d164b5e90000") :
bool(true)

$sweb3->utils->hexToBn("0x89056739d164b5e90000") :
object(phpseclib\Math\BigInteger)#202 (2) { ["value"]=> string(24) "0x0089056739d164b5e90000" ["engine"]=> string(3) "gmp" }

$sweb3->utils->fromWei($res->result, "ether") :
array(2) { [0]=> object(phpseclib\Math\BigInteger)#130 (2) { ["value"]=> string(8) "0xf76c91" ["engine"]=> string(3) "gmp" } [1]=> object(phpseclib\Math\BigInteger)#204 (2) { ["value"]=> string(18) "0x084f3304b81c0000" ["engine"]=> string(3) "gmp" } }

$sweb3->utils->toEther($res->result, "wei" :
array(2) { [0]=> object(phpseclib\Math\BigInteger)#130 (2) { ["value"]=> string(8) "0xf76c91" ["engine"]=> string(3) "gmp" } [1]=> object(phpseclib\Math\BigInteger)#202 (2) { ["value"]=> string(18) "0x084f3304b81c0000" ["engine"]=> string(3) "gmp" } } -562031.4012466292

Send array parameters

I'm trying to make a call to this contract where getAmountsOut method requires 2 parameters. One uint256 and address[].

I can't make it work because I don't know how I need to call the function and pass the parameters to make it work.

P.S.: It's working while trying to call a method that requires only parameters uint256. I have issues while passing [].

Screenshot 2023-01-24 at 13 43 01

Appreciate!

Also check it out https://stackoverflow.com/questions/75219723/web3-php-contract-call for a better explanation using WEB3 PHP

Автовыввод

Подскажите как сделать что бы с кошелька n перевести на комиссию и отправить с кошелька x на другой токены bep20

Call to a member function toHex() on array

Hi,

When calling $sweb3->send($sendParams), I get this error :

Fatal error: Uncaught Error: Call to a member function toHex() on array in /var/www/vhosts/hexakrown.io/httpdocs/vendor/drlecks/simple-web3-php/Core/Utils.php:98 Stack trace: #0 /var/www/vhosts/hexakrown.io/httpdocs/vendor/drlecks/simple-web3-php/Core/Utils.php(195): SWeb3\Utils::toHex() #1 /var/www/vhosts/hexakrown.io/httpdocs/vendor/drlecks/simple-web3-php/Core/SWeb3.php(131): SWeb3\Utils::forceAllNumbersHex() #2 /var/www/vhosts/hexakrown.io/httpdocs/api/INFURA/sendRawTransaction.php(388): SWeb3\SWeb3->send() #3 /var/www/vhosts/hexakrown.io/httpdocs/api/INFURA/sendRawTransaction.php(176): SWeb3\SendETH() #4 {main} thrown in /var/www/vhosts/hexakrown.io/httpdocs/vendor/drlecks/simple-web3-php/Core/Utils.php on line 98

Here is my code :

function SendETH($sweb3, $to, $amount)
{
//send 0.001 eth to 0x3Fc47d792BD1B0f423B0e850F4E2AD172d408447
// 0,015 000 000 000 000 000 want 1 000 000 000 000 000 000
//estimate gas cost
//if you don't provide explicit gas price, the system will update current gas price from the net (call)
$sendParams = [
'from' => $sweb3->personal->address,
'to' => $to,
'value' => $amount // Already with 18 décimals
];

//get function estimateGas
/*$gasEstimateResult = $sweb3->call('eth_estimateGas', [$sendParams]);

if(!isset($gasEstimateResult->result))
    throw new Exception('SendETH - GAS estimation error: ' . json_encode($gasEstimateResult));   

$gasEstimate = $sweb3->utils->hexToDec($gasEstimateResult->result);*/

$gasEstimate = 39000000;

//prepare sending
$sendParams['nonce'] = $sweb3->personal->getNonce(); 
$sendParams['gasLimit'] = $gasEstimate;

$result = $sweb3->send($sendParams); 
return json_encode($result);

}

I have commented gasEstimation because I got this error too.

It looks like in Utils.php the bn() function returns an array in place of a number.

Any suggestion ?

Thanks.

can I get the Latest 25 BEP-20 Token Transfer Events on a specified contract through Simple-Web3-Php?

can I get the Latest 25 BEP-20 Token Transfer Events on a specified contract through Simple-Web3-Php?

by browse to view Latest 25 BEP-20 Token Transfer Events: https://bscscan.com/address/0x6485E794e03b12366c1607bDAA1581Dc3bd73F0F#tokentxns

How to get event records through "Simple-Web3-Php"? and how to get the number of start and end block?
https://bscscan.com/block/32018276

//initialize SWeb3 main object
$extra_curl_params = [
	CURLOPT_SSL_VERIFYPEER => false,
	CURLOPT_SSL_VERIFYHOST => false
];
$sweb3 = new SWeb3('https://rpc.ankr.com/bsc', $extra_curl_params);

$contractABI = '[]';
//initialize contract from address and ABI string
$contract = new SWeb3_contract($sweb3, '0x6485E794e03b12366c1607bDAA1581Dc3bd73F0F', $contractABI); 

$last_block = '32018276';
$start_block = $last_block - 25;
//EVENTS
$res = $contract->getLogs(0x.$start_block, 0x .$last_block);

PrintCallResult('getLogs', $res);

but the above script cannot obtain data, taking the above contract as an example, how to get the number of start and end block?

Attemps to read property "inputs" on null

in this line:
$result = $contract->deployContract( [123123], $extra_params);

i got error: Attemps to read property "inputs" on null

it was because my contract doesn't have a constructor, so you should handle the case when the compiled contract doesn't have a constructor.

substr(): Argument #3 ($length) must be of type ?int, float given

$contract->getLogs();

fails with:

 at vendor/drlecks/simple-web3-php/Core/ABI.php:893
    889▕     {
    890▕         $length = self::DecodeInput_UInt_Internal($encoded, $start);
    891▕         $start += self::NUM_ZEROS;
    892▕ 
  ➜ 893▕         $partial = substr($encoded, $start, $length * 2);
    894▕         return hex2bin($partial);
    895▕     }
    896▕ 
    897▕ 

Actual for both 0.10 and master

How to obtain correct bytes32 string?

With var_dump im getting this:

 stdClass: object(stdClass)#209 (7) { ["elem_1"]=> string(16) "��gt+&��&���j%B)" ["elem_2"]=> string(16) "��gt+&��&���j%B)" ["elem_3"]=> string(16) "��gt+&��&���j%B)" ["elem_4"]=> string(16) "��gt+&��&���j%B)" ["elem_5"]=> string(16) "��gt+&��&���j%B)" ["elem_6"]=> object(phpseclib\Math\BigInteger)#213 (2) { ["value"]=> string(10) "0x6575156b" ["engine"]=> string(3) "gmp" } ["elem_7"]=> object(phpseclib\Math\BigInteger)#215 (2) { ["value"]=> string(10) "0x0145e1e6" ["engine"]=> string(3) "gmp" } }

How bytes32 string comes from this 0xdbe567742b26e60826fc0f8a6a25422900000000000000000000000000000000 to this ��gt+&��&���j%B)

Stargate finance tuple issue

https://bscscan.com/address/0x4a364f8c717cAAD9A442737Eb7b8A55cc6cf18D8#code

$contract = new SWeb3_contract($sweb3, '0x4a364f8c717cAAD9A442737Eb7b8A55cc6cf18D8', $StarGateRouterAbi);
$callData = [109, 1, $wallet, "0x", ["dstGasForCall" => 0, "dstNativeAmount" => 0, "dstNativeAddr" => "0x"]];
$res = $contract->call('quoteLayerZeroFee', $callData);
PrintCallResult('quoteLayerZeroFee', $res);

error is
Undefined array key 0
at vendor/drlecks/simple-web3-php/Core/ABI.php:272

What i doing wrong?

P.S. also i try like this

$callData = [109, 1, $wallet, "0x", new stdClass()];
//tuple(uint256, string, string[])
$callData[4]->dstGasForCall = 0;
$callData[4]->dstNativeAmount = 0;
$callData[4]->dstNativeAddr = '0x';
$res = $contract->call('quoteLayerZeroFee', $callData);
PrintCallResult('quoteLayerZeroFee', $res);

another error like he want array instead object

Passt Tuple parameters to contract

Hi, thanks for your great library. So i was trying around for serveral hours now xD and before i get crazy, i decided to write here..

i want to do a contract send , with some parameters and one tuple .. if i check these transactions.. it looks almost fine, beside the tuple part is just wrong.

can you see any mistake i am doing here?

$res = $contract->send('mint',[$walletx,1077778,"linkofjson.json",[5,false]];

the [5,false] is the tuple part .. i tried them with '"' each, and whatever combination.. it just wont fit.

Greetings and thank you very much

Split ABIv2 encoding/decoding

Maybe it'll be good to spli this project into separated project just for ABI encoding/decoding which other php projects for interacting with ethereum based nodes can use. I think nothing else supports abiv2/tuples as good as this one and this is rather complex to implement and test properly so it should benefit everyone else writing php code, if they'll just use this one.

Custom blockchain ID

Is there any way to set custom Chain ID? I manually write like below but I don't see it's a good practice.
$sweb3->chainId = 80001;

I think we would need a setter function on SWeb3.php for chain ID.

Add custom convert value

Hello,

Possibility to add a function to convert according to a precise value of decimals, useful in some cases, such as :

function convertWithDecimals($result, $decimals) {
    // divide $result by $decimals
}

ERROR: estimateGas error: execution reverted: Ownable: caller is not an owner

Hello
I'm so glad that you created this library, it makes communication with smart-contracts easier.
I'm working on NFT solution on Polygon chain and I have problem with sending data to my smart-contract. I understand from error message that it's about owner address but I have no idea how to set this address correct in $contract->send function call.

PHP code:

        $web3 = new SWeb3(
            $setting->getNetworkUrl()
        );

        $web3->setPersonalData(
            $setting->getWalletAddress(),
            $setting->getWalletPrivateKey()
        );
        $web3->chainId = 0x13881; //mumbai
        $contract = new SWeb3_Contract(
            $web3,
            $setting->getArtifactsContractAddress(),
            json_encode($setting->getArtifactsContractAbi())
        );

        $contract->send(
            'addArtifact',
            [
                $itemId, //int
                $transaction->getInProviderId(), //string
                $setting->getArtifactBaseUrl()."/".$itemId, //string
                base64_encode(json_encode($itemJsonArray)), //string
                $sender, //address
            ]
        );

And smart-contract function:

    function addArtifact(uint256 tokenId, uint256 transactionId, string memory uri, string memory tokenData, address to)
        public onlyOwner
    {
        _mint(to, tokenId);

        _setTokenURI(tokenId, uri);
        _tokenDatas[tokenId] = tokenData;

        delete _waitingTransactions[transactionId];
        
        emit Minted(tokenId);
    }

Any help will be appricieted

[Conflicts] Can't install via composer require

Hello,
I'm having issues installing this package. There seems to be a number of conflicts.

Your requirements could not be resolved to an installable set of packages.

Problem 1
- Root composer.json requires drlecks/simple-web3-php ^0.9.0 -> satisfiable by drlecks/simple-web3-php[v0.9].
- drlecks/simple-web3-php v0.9 requires kornrunner/ethereum-offline-raw-tx ^0.4.0 -> found kornrunner/ethereum-offline-raw-tx[0.4.0] but the package is fixed to 0.2.4 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.

null result from $sweb3->getTransactionReceipt($txHash);

$sweb3->getTransactionReceipt($txHash);
return
Exception 'Exception' with message 'getTransactionReceipt error: {"jsonrpc":"2.0","id":1,"result":null}'

but curl from command line with same params return result
curl -X POST https://data-seed-prebsc-2-s3.binance.org:8545/ -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0x5d6f5b24f318f2fd012caee68ae7e2c4e905ba38a083e9f762566a9a588ee290"],"id":1}'

PS. endpoint https://data-seed-prebsc-2-s3.binance.org:8545/ for chain 0x61 (bsc testnet)

hello from somewhere in the world

I am looking for a php library that will allow me to convert Wei token transaction values to human readable values like the ones seen in the block explorer.

Is this possible with this library using a helper or something like that?

Is it also compatible with the Binance smart chain? thank you

Add blockTag to the SWeb3_Contract.php

Within the call() method, add the following.

    $blockTag = "latest";
    if (isset($extraParams->blockTag)) {
        $blockTag = $extraParams->blockTag;
        unset($extraParams->blockTag);
    }

    $data = [$extraParams, $blockTag];

This will allow us to choose the block we want to query the contract, making it possible for us to go back in time to get data from the contract on a specific block or day.

Signature is not correct for a complex contract

Hello,
I have used the Opensea contract at 0x00000000006c3852cbEf3e08E8dF289169EdE581 and got the ABI from here. I have tried to check the signature of the function fulfillBasicOrder(), which is quite complex :
$abi->Init($opensea_abi);
$function = $abi->GetFunction('fulfillBasicOrder');
$data = $abi->GetSignatureFromEvent($function);

I get : 0xaf84e809e6076db3ce2cb7f5da51cfda86a4f3e774067fcbb34d23c187460303.

When checking the trace of a call made to this function, I can see that the prefix should be 0xfb0f3ee1 while I got 0xaf84e809 in my example.

Any clue why ?

Thanks and regards,
Mosi

ABI Encoding, Keccak256 Hashing

How can I encode Ethereum ABI types, create a hash using Keccak256, and sign the resulting hash with a private key?

const types = ['address', 'uint256', 'uint256'];
const values = [user.address, 1, currentDateInSeconds];
const encodedStr = ethers.utils.defaultAbiCoder.encode(types, values);
const keccak256 = ethers.utils.keccak256(encodedStr);

const signature = EthCrypto.sign(PRIV_KEY,  keccak256);

The output data of the read contract command is missing [solved]

The output data of the read contract command is missing, actually there is a lot of data in _dataContract, but many values in the current code output data are missing.

namespace SWeb3;
require 'vendor/autoload.php'; 

use SWeb3\SWeb3;            //always needed, to create the Web3 object
use SWeb3\Utils;           //sweb3 helper classes (for example, hex conversion operations)
use SWeb3\SWeb3_Contract;  //contract creation and interaction
use SWeb3\Accounts;        //account creation
use SWeb3\Account;         //single account management (signing)
use phpseclib\Math\BigInteger as BigNumber;		//BigInt handling
use stdClass;             //for object interaction 

//initialize SWeb3 main object
$sweb3 = new SWeb3('https://bsc-dataseed1.defibit.io/');

// Contract ABI
$contractABI = '[{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"contractInfo","outputs":[{"components":[{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"uint256","name":"launched","type":"uint256"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"minDeposit","type":"uint256"},{"internalType":"uint256","name":"agg_invested","type":"uint256"},{"internalType":"uint256","name":"ht_invested","type":"uint256"},{"internalType":"uint256","name":"ht_refReward","type":"uint256"},{"internalType":"uint256","name":"ht_earned","type":"uint256"},{"internalType":"uint256","name":"ht_withdrawn","type":"uint256"},{"internalType":"uint256","name":"ht_lottery","type":"uint256"},{"internalType":"uint256","name":"insBalance","type":"uint256"},{"internalType":"uint256","name":"ht_insSentAgg","type":"uint256"},{"internalType":"uint256","name":"ht_insRecvAgg","type":"uint256"},{"internalType":"uint256","name":"avgBalance","type":"uint256"},{"internalType":"uint256","name":"weekIndex","type":"uint256"},{"internalType":"uint256","name":"wk_refReward","type":"uint256"},{"internalType":"uint256","name":"weekEndsIn","type":"uint256"},{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TRUEFUND_V2.Deposit[10]","name":"topDeposits","type":"tuple[10]"},{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TRUEFUND_V2.Deposit[10]","name":"topReferrers","type":"tuple[10]"},{"components":[{"internalType":"enum TRUEFUND_V2.Flags","name":"flag","type":"uint8"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"uint256","name":"amt1","type":"uint256"},{"internalType":"uint256","name":"amt2","type":"uint256"},{"internalType":"uint256","name":"amt3","type":"uint256"},{"internalType":"address","name":"addr1","type":"address"},{"internalType":"address","name":"addr2","type":"address"}],"internalType":"struct TRUEFUND_V2.History[20]","name":"history","type":"tuple[20]"},{"components":[{"internalType":"uint256","name":"bank","type":"uint256"},{"internalType":"uint256","name":"ticketsCount","type":"uint256"},{"internalType":"uint256","name":"usersCount","type":"uint256"},{"internalType":"uint256","name":"winnerTicket","type":"uint256"},{"internalType":"address","name":"winnerUser","type":"address"},{"internalType":"uint256","name":"maxUsers","type":"uint256"},{"internalType":"uint256","name":"percent","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"prize","type":"uint256"}],"internalType":"struct TRUEFUND_V2.LotteryInfo","name":"lotteryPrev","type":"tuple"},{"components":[{"internalType":"uint256","name":"bank","type":"uint256"},{"internalType":"uint256","name":"ticketsCount","type":"uint256"},{"internalType":"uint256","name":"usersCount","type":"uint256"},{"internalType":"uint256","name":"winnerTicket","type":"uint256"},{"internalType":"address","name":"winnerUser","type":"address"},{"internalType":"uint256","name":"maxUsers","type":"uint256"},{"internalType":"uint256","name":"percent","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"prize","type":"uint256"}],"internalType":"struct TRUEFUND_V2.LotteryInfo","name":"lotteryCurr","type":"tuple"},{"components":[{"internalType":"uint256","name":"finished","type":"uint256"},{"internalType":"address","name":"winner","type":"address"},{"internalType":"uint256","name":"bank","type":"uint256"},{"internalType":"uint256","name":"prize","type":"uint256"}],"internalType":"struct TRUEFUND_V2.LotteryResult[20]","name":"lotteryResults","type":"tuple[20]"}],"internalType":"struct TRUEFUND_V2.DataContract","name":"_dataContract","type":"tuple"},{"components":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"allowance","type":"uint256"}],"internalType":"struct TRUEFUND_V2.DataUser","name":"_dataUser","type":"tuple"}],"stateMutability":"view","type":"function"}]';

$contractAddress = '0x6485E794e03b12366c1607bDAA1581Dc3bd73F0F';

$contract = new SWeb3_contract($sweb3, $contractAddress, $contractABI);
$walletAddress = '0x6485E794e03b12366c1607bDAA1581Dc3bd73F0F';

$callName = 'contractInfo';
$result = $contract->call($callName, $walletAddress);
PrintCallResult($callName, $result);     // output result

function PrintCallResult($callName, $result)
{
    echo "<br/> Call -> <b>". $callName . "</b><br/>";

    echo "Result -> " . PrintObject($result) . "<br/>"; 
}

function PrintObject($x)
{ 
	if ($x instanceof BigNumber)
	{
		return $x . '';
	}
	
	if (is_object($x)) {
		$x = (array)($x); 
	}

	if (is_array($x))
	{
		$text = "[";
		$first = true;
		foreach($x as $key => $value)
		{
			if ($first)  	$first = false;
			else 			$text .= ", ";

			$text .= $key . " : " . PrintObject($value);
		}

		return $text . "]"; 
	}
	 
	return $x . '';
}

how to solve this issue?

Leading zero in address trimmed

Hi
I call a contract method that return a list of addresses but address that start with 0x0 trimmed and get wrong
address that get from PHP 0x5b2ae867657564db1d783b73eaa68e202e933db
but the correct address is 0x05b2AE867657564dB1D783b73eaa68E202e933db

Get Balance

Hi,
Is there a method to get the balance of an address?
If we use the batch calls it gives the below error
$sweb3->call('eth_getBalance', [$sweb3->personal->address]);
Array ( [0] => stdClass Object ( [jsonrpc] => 2.0 [id] => 1 [error] => stdClass Object ( [code] => -32602 [message] => missing value for required argument 1 ) ) )

if we add 'latest'
$sweb3->call('eth_getBalance', [$sweb3->personal->address, 'latest']);
Response:
Array ( [0] => stdClass Object ( [jsonrpc] => 2.0 [id] => 1 [result] => 0x0 ) )

Tuple compilation data is not the same

` $abic = '[{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "tokenIn",
"type": "address"
},
{
"internalType": "address",
"name": "tokenOut",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountOutMinimum",
"type": "uint256"
},
{
"internalType": "uint160",
"name": "sqrtPriceLimitX96",
"type": "uint160"
}
],
"internalType": "struct IV3SwapRouter.ExactInputSingleParams",
"name": "params",
"type": "tuple"
}
],
"name": "exactInputSingle",
"outputs": [
{
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "function"
}]';
$abi = new ABI();
$abi->Init($abic);

    $send_data = new \stdClass();
    $send_data->tokenIn = '0xC4663f169AAFB1d4506CE189D63810E24A8B63ea';
    $send_data->tokenOut = '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1';
    $send_data->fee = 10000;
    $send_data->recipient = '0xA972989634d1e59864b98F3AEFe7D34822024b8e';
    $send_data->deadline = 1677582887;
    $send_data->amountIn = '118048000000000000000000';
    $send_data->amountOutMinimum = '30232125832148302';
    $send_data->sqrtPriceLimitX96 = 0;


    $rs = $abi->EncodeData('exactInputSingle', $send_data);
    file_put_contents('222222.txt', $rs);
    var_dump($rs);
    die;`

Code as above

Compiled inputData
0x414bf3890000000000000000000000000000000000000000000000000000000000000020000000000000000000000000C4663f169AAFB1d4506CE189D63810E24A8B63ea00000000000000000000000082aF49447D8a07e3bd95BD0d56f35241523fBab10000000000000000000000000000000000000000000000000000000000002710000000000000000000000000A972989634d1e59864b98F3AEFe7D34822024b8e0000000000000000000000000000000000000000000000000000000063fde2270000000000000000000000000000000000000000000018ff65185ae400800000000000000000000000000000000000000000000000000000006b67f55107b54e0000000000000000000000000000000000000000000000000000000000000000

Uniswap compiled data
0x414bf389000000000000000000000000c4663f169aafb1d4506ce189d63810e24a8b63ea00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab10000000000000000000000000000000000000000000000000000000000002710000000000000000000000000a972989634d1e59864b98f3aefe7d34822024b8e0000000000000000000000000000000000000000000000000000000063fde2270000000000000000000000000000000000000000000018ff65185ae400800000000000000000000000000000000000000000000000000000006b67f55107b54e0000000000000000000000000000000000000000000000000000000000000000

Transaction hash https://arbiscan.io/tx/0xc6aaff56c30476a6f0355877fae1415f57575542bf498525005038b8ff3818a5

countryBlocked!

hi. i am getting this error when i call send method of library. what should i do?

{
"error": "countryBlocked",
"message": "This service is not available in your country"
}

Can't send ETH to addresses starting with "0x0"

This code trims the address and removes all leading zeros, making it impossible to send ETH to addresses starting with 0x0:

Web3p\RLP\Types\Str:42

if (mb_strlen($input) > 2) {
  $input = ltrim($input, '0');
}

The exception:
transaction could not be decoded: could not decode RLP components: could not decode list item 3 to Address: invalid address: 0x93...

The address in question begins with 0x0093.

2 Bugs in encoding tuples ( Incorrect assumption that tuple is always dynamic ) ?

I think i came across an error for encoding tuples. First in EncodeInput, it will always put tuple's content at the end of currently constructed data block using $input->hash. For non-dynamic parameters it should be put in-place as far as i understand the specs so that's the first error which will prevent correctly encoding some tuples.

In EncodeGroup
$currentDynamicIndex = count($inputs) * self::NUM_ZEROS / 2;
This is probably not correct as well as single tuple can have several non-dynamic fields only, eg. 3 UINTs, so the assumption that first dynamic 32b data block starts at position which is just a number of inputs is not always correct (we need to process all tuples before we know first dynamic block number).

Just getting to know this code, so please let me know if that makes sense.

As far as i understand how it forks currently:
Currently for non-dynamic tuples the code incorrectly moves dynamic index forward by number of non-dynamic tuples and then it incorrectly places the tuple content at the end. It should move the dynamic index by number of non-dynamic tuples elements. Eg. if you have 2 non-dynamic tuples with 4 elements each the dynamic index will be moved by 2 positions, instead of 2x4, and data of these tuples will be placed at the end.

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.