* * @author Peter Lai * @license MIT */ namespace Web3; use InvalidArgumentException; use Web3\Providers\Provider; use Web3\Providers\HttpProvider; use Web3\RequestManagers\RequestManager; use Web3\RequestManagers\HttpRequestManager; use Web3\Utils; use Web3\Eth; use Web3\Contracts\Ethabi; use Web3\Contracts\Types\Address; use Web3\Contracts\Types\Boolean; use Web3\Contracts\Types\Bytes; use Web3\Contracts\Types\Integer; use Web3\Contracts\Types\Str; use Web3\Contracts\Types\Uinteger; use Web3\Validators\AddressValidator; use Web3\Validators\HexValidator; use Web3\Formatters\Address as AddressFormatter; class Contract { /** * provider * * @var \Web3\Providers\Provider */ protected $provider; /** * abi * * @var array */ protected $abi; /** * constructor * * @var array */ protected $constructor = []; /** * functions * * @var array */ protected $functions = []; /** * events * * @var array */ protected $events = []; /** * toAddress * * @var string */ protected $toAddress; /** * bytecode * * @var string */ protected $bytecode; /** * eth * * @var \Web3\Eth */ protected $eth; /** * ethabi * * @var \Web3\Contracts\Ethabi */ protected $ethabi; /** * construct * * @param mixed string | Web3\Providers\Provider $provider * @param mixed string | stdClass | array $abi * @return void */ public function __construct($provider, $abi) { if (is_string($provider) && (filter_var($provider, FILTER_VALIDATE_URL) !== false)) { // check the uri schema if (preg_match('/^https?:\/\//', $provider) === 1) { $requestManager = new HttpRequestManager($provider); $this->provider = new HttpProvider($requestManager); } } else if ($provider instanceof Provider) { $this->provider = $provider; } $abi = Utils::jsonToArray($abi, 5); foreach ($abi as $item) { if (isset($item['type'])) { if ($item['type'] === 'function') { $this->functions[$item['name']] = $item; } elseif ($item['type'] === 'constructor') { $this->constructor = $item; } elseif ($item['type'] === 'event') { $this->events[$item['name']] = $item; } } } $this->abi = $abi; $this->eth = new Eth($this->provider); $this->ethabi = new Ethabi([ 'address' => new Address, 'bool' => new Boolean, 'bytes' => new Bytes, 'int' => new Integer, 'string' => new Str, 'uint' => new Uinteger, ]); } /** * call * * @param string $name * @param array $arguments * @return void */ // public function __call($name, $arguments) // { // if (empty($this->provider)) { // throw new \RuntimeException('Please set provider first.'); // } // $class = explode('\\', get_class()); // if (preg_match('/^[a-zA-Z0-9]+$/', $name) === 1) { // } // } /** * get * * @param string $name * @return mixed */ public function __get($name) { $method = 'get' . ucfirst($name); if (method_exists($this, $method)) { return call_user_func_array([$this, $method], []); } } /** * set * * @param string $name * @param mixed $value * @return bool */ public function __set($name, $value) { $method = 'set' . ucfirst($name); if (method_exists($this, $method)) { return call_user_func_array([$this, $method], [$value]); } return false; } /** * getProvider * * @return \Web3\Providers\Provider */ public function getProvider() { return $this->provider; } /** * setProvider * * @param $provider * @return bool */ public function setProvider($provider) { if ($provider instanceof Provider) { $this->provider = $provider; return true; } return false; } /** * getFunctions * * @return array */ public function getFunctions() { return $this->functions; } /** * getEvents * * @return array */ public function getEvents() { return $this->events; } /** * getConstructor * * @return array */ public function getConstructor() { return $this->constructor; } /** * getAbr * * @return array */ public function getAbi() { return $this->abi; } /** * getEth * * @return \Web3\Eth */ public function getEth() { return $this->eth; } /** * at * * @param string $address * @return $this */ public function at($address) { if (AddressValidator::validate($address) === false) { throw new InvalidArgumentException('Please make sure address is valid.'); } $this->toAddress = AddressFormatter::format($address); return $this; } /** * bytecode * * @param string $bytecode * @return $this */ public function bytecode($bytecode) { if (HexValidator::validate($bytecode) === false) { throw new InvalidArgumentException('Please make sure bytecode is valid.'); } $this->bytecode = Utils::stripZero($bytecode); return $this; } /** * new * Deploy a contruct with params. * * @param mixed * @return void */ public function new() { if (isset($this->constructor)) { $constructor = $this->constructor; $arguments = func_get_args(); $callback = array_pop($arguments); if (count($arguments) < count($constructor['inputs'])) { throw new InvalidArgumentException('Please make sure you have put all constructor params and callback.'); } if (is_callable($callback) !== true) { throw new \InvalidArgumentException('The last param must be callback function.'); } if (!isset($this->bytecode)) { throw new \InvalidArgumentException('Please call bytecode($bytecode) before new().'); } $params = array_splice($arguments, 0, count($constructor['inputs'])); $data = $this->ethabi->encodeParameters($constructor, $params); $transaction = []; if (count($arguments) > 0) { $transaction = $arguments[0]; } $transaction['to'] = ''; $transaction['data'] = '0x' . $this->bytecode . Utils::stripZero($data); $this->eth->sendTransaction($transaction, function ($err, $transaction) use ($callback){ if ($err !== null) { return call_user_func($callback, $err, null); } return call_user_func($callback, null, $transaction); }); } } /** * send * Send function method. * * @param mixed * @return void */ public function send() { if (isset($this->functions)) { $arguments = func_get_args(); $method = array_splice($arguments, 0, 1)[0]; $callback = array_pop($arguments); if (!is_string($method) && !isset($this->functions[$method])) { throw new InvalidArgumentException('Please make sure the method is existed.'); } $function = $this->functions[$method]; if (count($arguments) < count($function['inputs'])) { throw new InvalidArgumentException('Please make sure you have put all function params and callback.'); } if (is_callable($callback) !== true) { throw new \InvalidArgumentException('The last param must be callback function.'); } $params = array_splice($arguments, 0, count($function['inputs'])); $data = $this->ethabi->encodeParameters($function, $params); $functionName = Utils::jsonMethodToString($function); $functionSignature = $this->ethabi->encodeFunctionSignature($functionName); $transaction = []; if (count($arguments) > 0) { $transaction = $arguments[0]; } $transaction['to'] = $this->toAddress; $transaction['data'] = $functionSignature . Utils::stripZero($data); $this->eth->sendTransaction($transaction, function ($err, $transaction) use ($callback){ if ($err !== null) { return call_user_func($callback, $err, null); } return call_user_func($callback, null, $transaction); }); } } /** * call * Call function method. * * @param mixed * @return void */ public function call() { if (isset($this->functions)) { $arguments = func_get_args(); $method = array_splice($arguments, 0, 1)[0]; $callback = array_pop($arguments); if (!is_string($method) && !isset($this->functions[$method])) { throw new InvalidArgumentException('Please make sure the method is existed.'); } $function = $this->functions[$method]; if (count($arguments) < count($function['inputs'])) { throw new InvalidArgumentException('Please make sure you have put all function params and callback.'); } if (is_callable($callback) !== true) { throw new \InvalidArgumentException('The last param must be callback function.'); } $params = array_splice($arguments, 0, count($function['inputs'])); $data = $this->ethabi->encodeParameters($function, $params); $functionName = Utils::jsonMethodToString($function); $functionSignature = $this->ethabi->encodeFunctionSignature($functionName); $transaction = []; if (count($arguments) > 0) { $transaction = $arguments[0]; } $transaction['to'] = $this->toAddress; $transaction['data'] = $functionSignature . Utils::stripZero($data); $this->eth->call($transaction, function ($err, $transaction) use ($callback){ if ($err !== null) { return call_user_func($callback, $err, null); } return call_user_func($callback, null, $transaction); }); } } }