Open vnxz opened 2 years ago
Hello, you should sign your transaction before sending it, refer to this link, there is an example: https://github.com/web3p/web3.php/issues/216
Here is a full example how to call a function on a smart contract which require a signer (to change the status of the blockchain). A simple call like balanceOf function doesn't require a signer so thats why it's working, but when you need to change content, you need to sign the transaction before like @jose5958 wrote before.
Assuming below:
And also this code require the use of an other package from the same author : https://github.com/web3p/ethereum-tx It is used to send raw transaction with signer
public function transferMyGreatToken($recipientAccount, $quantity): mixed
{
Log::debug("Call {$quantity} transferMyGreatToken for recipient={$recipientAccount}");
$transactionCount = null;
$this->contract->eth->getTransactionCount($this->ownerAccount, function ($err, $transactionCountResult) use(&$transactionCount) {
if($err) {
Log::error('getTransactionCount error: ' . $err->getMessage());
throw new RuntimeException($err->getMessage());
}
$transactionCount = $transactionCountResult;
});
Log::debug("transactionCount=$transactionCount");
$gasPrice = null;
$this->contract->eth->gasPrice(function ($err, $gasPriceResult) use(&$gasPrice) {
if($err) {
Log::error('gasPrice error: ' . $err->getMessage());
throw new RuntimeException($err->getMessage());
}
$gasPrice = $gasPriceResult;
});
Log::debug("gasPrice=$gasPrice");
$rawTransactionData = '0x' . $this->contract->getData('transferMyGreatToken', $recipientAccount, $quantity);
$transactionParams = [
'nonce' => "0x" . dechex($transactionCount->toString()),
'from' => $this->ownerAccount,
'to' => $this->contractAddress,
'gasPrice' => '0x' . hexdec(dechex($gasPrice->toString())),
'value' => '0x0',
'data' => $rawTransactionData
];
$estimatedGas = null;
$this->contract->eth->estimateGas($transactionParams, function ($err, $gas) use (&$estimatedGas) {
if ($err) {
Log::error('estimateGas error: ' . $err->getMessage());
throw new RuntimeException($err->getMessage());
}
$estimatedGas = $gas;
});
Log::debug("estimatedGas=$estimatedGas");
$gasPriceLimit = $this->computeGasLimit($estimatedGas);
$transactionParams['gasLimit'] = '0x' . dechex($gasPriceLimit);
$transactionParams['chainId'] = $this->chainId;
$tp = json_encode($transactionParams);
Log::debug("transactionParams=$tp");
$tx = new Web3Transaction($transactionParams);
$signedTx = '0x' . $tx->sign($this->ownerPKey);
$txHash = null;
$this->contract->eth->sendRawTransaction($signedTx, function ($err, $txResult) use (&$txHash) {
if($err) {
Log::error('transaction error: ' . $err->getMessage());
throw new RuntimeException($err->getMessage());
}
$txHash = $txResult;
});
Log::debug("txHash=$txHash");
$txReceipt = null;
Log::debug("Waiting for transaction receipt");
for ($i=0; $i <= $this->secondsToWaitForReceipt; $i++) {
$this->contract->eth->getTransactionReceipt($txHash, function ($err, $txReceiptResult) use(&$txReceipt) {
if($err) {
Log::error('getTransactionReceipt error: ' . $err->getMessage());
throw new RuntimeException($err->getMessage());
}
$txReceipt = $txReceiptResult;
});
if ($txReceipt) {
break;
}
sleep(1);
}
$txStatus = $txReceipt->status;
Log::debug("txStatus=$txStatus");
return $txStatus;
}
$this->contract->getData is not function
what this ???????????????????????
@MariusMez
what is $this->chainId value?!
The chainId of the blockchain you are using...
// See: https://chainlist.org
public const chainIds = [
'mainnet' => 1,
'ropsten' => 3,
'rinkeby' => 4,
'goerli' => 5,
'kovan' => 42,
'mumbai' => 80001,
'polygon_testnet' => 80001,
'polygon' => 137,
'matic' => 137,
];
@MariusMez
Thanks for the reply
another question
$this->computeGasLimit $this->secondsToWaitForReceipt
What does it calculate! I know what it is but I do not know how :)
If you have a sample code, thank you for sharing
Indeed, I didn't remove this optional part. In fact this method use an empiric way to define the maximum gas limit to use:
/**
* Compute the maximum gasLimit value to use for a transaction
* TODO: think of a better way to handle this
* @param $estimatedGas
* @return int
*/
public function computeGasLimit($estimatedGas): int
{
$maxGasLimit = (int) config('web3.max_gas_limit', 15000000);
$factorToMultiplyGasEstimate = (int) config('web3.factor_to_multiply_gas_estimate');
$gasPriceLimit = hexdec(dechex($estimatedGas->toString())) * $factorToMultiplyGasEstimate;
if ($gasPriceLimit > $maxGasLimit) {
$gasPriceLimit = $maxGasLimit;
}
Log::debug("gasPriceLimit=$gasPriceLimit");
return $gasPriceLimit;
}
And $this->secondsToWaitForReceipt is a timeout you have to set (20s/30s)
Here is a full example how to call a function on a smart contract which require a signer (to change the status of the blockchain). A simple call like balanceOf function doesn't require a signer so thats why it's working, but when you need to change content, you need to sign the transaction before like @jose5958 wrote before.
Assuming below:
- $this->contract = new Contract($provider, $abi);
- $this->ownerAccount = address of contract owner
- $this->ownerPKey = private key of the contract owner used to sign the transaction
And also this code require the use of an other package from the same author : https://github.com/web3p/ethereum-tx It is used to send raw transaction with signer
public function transferMyGreatToken($recipientAccount, $quantity): mixed { Log::debug("Call {$quantity} transferMyGreatToken for recipient={$recipientAccount}"); $transactionCount = null; $this->contract->eth->getTransactionCount($this->ownerAccount, function ($err, $transactionCountResult) use(&$transactionCount) { if($err) { Log::error('getTransactionCount error: ' . $err->getMessage()); throw new RuntimeException($err->getMessage()); } $transactionCount = $transactionCountResult; }); Log::debug("transactionCount=$transactionCount"); $gasPrice = null; $this->contract->eth->gasPrice(function ($err, $gasPriceResult) use(&$gasPrice) { if($err) { Log::error('gasPrice error: ' . $err->getMessage()); throw new RuntimeException($err->getMessage()); } $gasPrice = $gasPriceResult; }); Log::debug("gasPrice=$gasPrice"); $rawTransactionData = '0x' . $this->contract->getData('transferMyGreatToken', $recipientAccount, $quantity); $transactionParams = [ 'nonce' => "0x" . dechex($transactionCount->toString()), 'from' => $this->ownerAccount, 'to' => $this->contractAddress, 'gasPrice' => '0x' . hexdec(dechex($gasPrice->toString())), 'value' => '0x0', 'data' => $rawTransactionData ]; $estimatedGas = null; $this->contract->eth->estimateGas($transactionParams, function ($err, $gas) use (&$estimatedGas) { if ($err) { Log::error('estimateGas error: ' . $err->getMessage()); throw new RuntimeException($err->getMessage()); } $estimatedGas = $gas; }); Log::debug("estimatedGas=$estimatedGas"); $gasPriceLimit = $this->computeGasLimit($estimatedGas); $transactionParams['gasLimit'] = '0x' . dechex($gasPriceLimit); $transactionParams['chainId'] = $this->chainId; $tp = json_encode($transactionParams); Log::debug("transactionParams=$tp"); $tx = new Web3Transaction($transactionParams); $signedTx = '0x' . $tx->sign($this->ownerPKey); $txHash = null; $this->contract->eth->sendRawTransaction($signedTx, function ($err, $txResult) use (&$txHash) { if($err) { Log::error('transaction error: ' . $err->getMessage()); throw new RuntimeException($err->getMessage()); } $txHash = $txResult; }); Log::debug("txHash=$txHash"); $txReceipt = null; Log::debug("Waiting for transaction receipt"); for ($i=0; $i <= $this->secondsToWaitForReceipt; $i++) { $this->contract->eth->getTransactionReceipt($txHash, function ($err, $txReceiptResult) use(&$txReceipt) { if($err) { Log::error('getTransactionReceipt error: ' . $err->getMessage()); throw new RuntimeException($err->getMessage()); } $txReceipt = $txReceiptResult; }); if ($txReceipt) { break; } sleep(1); } $txStatus = $txReceipt->status; Log::debug("txStatus=$txStatus"); return $txStatus; }
i m trying to understand this in plane php but i m stuck on this line $rawTransactionData = '0x' . $this->contract->getData('transferMyGreatToken', $recipientAccount, $quantity);
what is TransferMyGreateToken can i have this in base php please thanks for you help guidance you providing here
It's the name of the smart contract method you want to call. For example purpose I choose 'transferMyGreatToken', but of course you need to change it. (For a specific smart contract wich is not yours, you can find all methods/function names and event in the smart contract ABI)
It's the name of the smart contract method you want to call. For example purpose I choose 'transferMyGreatToken', but of course you need to change it. (For a specific smart contract wich is not yours, you can find all methods/function names and event in the smart contract ABI)
this is the code i m trying to run $rawTransactionData = '0x' . $my_contract->getData("transferFrom", $ownerAccount, $toAccount, $quantity);
here is smart contract method function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "BEP20: transfer amount exceeds allowance")); return true; }
and here is abi $contract_abi = '[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"TokenPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_airdrop","type":"uint256"}],"name":"setDrop","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"},{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"rate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"airdrop","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"},{"name":"amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"},{"name":"amount","type":"uint256"}],"name":"burnFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"airdropTokens","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_rate","type":"uint256"}],"name":"setPrice","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"}],"name":"addMinter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceMinter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"rewards","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"isMinter","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_rewards","type":"uint256"}],"name":"setRewards","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"buyTokens","outputs":[{"name":"","type":"bool"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_decimals","type":"uint8"},{"name":"_initialSupply","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"account","type":"address"}],"name":"MinterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"account","type":"address"}],"name":"MinterRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]';
but i m getting error Fatal error: Uncaught InvalidArgumentException: Please make sure you have put all function params and callback. in /var/www/web3/vendor/sc0vu/web3.php/src/Contract.php:561 Stack trace: #0 /var/www/web3/sendtoken.php(96): Web3\Contract->send() #1 {main} thrown in /var/www/web3/vendor/sc0vu/web3.php/src/Contract.php on line 561
It's the name of the smart contract method you want to call. For example purpose I choose 'transferMyGreatToken', but of course you need to change it. (For a specific smart contract wich is not yours, you can find all methods/function names and event in the smart contract ABI)
this is the code i m trying to run $rawTransactionData = '0x' . $my_contract->getData("transferFrom", $ownerAccount, $toAccount, $quantity);
here is smart contract method function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "BEP20: transfer amount exceeds allowance")); return true; }
and here is abi $contract_abi = '[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"TokenPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_airdrop","type":"uint256"}],"name":"setDrop","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"},{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"rate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"airdrop","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"},{"name":"amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"},{"name":"amount","type":"uint256"}],"name":"burnFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"airdropTokens","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_rate","type":"uint256"}],"name":"setPrice","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"}],"name":"addMinter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceMinter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"rewards","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"isMinter","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_rewards","type":"uint256"}],"name":"setRewards","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"buyTokens","outputs":[{"name":"","type":"bool"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_decimals","type":"uint8"},{"name":"_initialSupply","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"account","type":"address"}],"name":"MinterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"account","type":"address"}],"name":"MinterRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]';
but i m getting error Fatal error: Uncaught InvalidArgumentException: Please make sure you have put all function params and callback. in /var/www/web3/vendor/sc0vu/web3.php/src/Contract.php:561 Stack trace: #0 /var/www/web3/sendtoken.php(96): Web3\Contract->send() #1 {main} thrown in /var/www/web3/vendor/sc0vu/web3.php/src/Contract.php on line 561
any help guys please how can i send token using base php with this library
did you found a solution?
I try to transfer token to another account by laravel. error is
here is code
here is smart contract
may be due to cannot call directly transfer method ? or what? can someone explain me how can call contract method by laravel for this contract.