You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
9.7 KiB
290 lines
9.7 KiB
<?php
|
|
|
|
|
|
namespace App\Services\CoinService;
|
|
|
|
|
|
use App\Libs\Ethtool\Callback;
|
|
use App\Libs\Ethtool\Credential;
|
|
use App\Services\CoinService\Interfaces\CoinServiceInterface;
|
|
use App\Services\CoinService\Libs\Etherscan;
|
|
use GuzzleHttp\Client;
|
|
use phpseclib\Math\BigInteger as BigNumber;
|
|
use Web3\Contract;
|
|
use Web3\Eth;
|
|
use Web3\Personal;
|
|
use Web3\Providers\HttpProvider;
|
|
use Web3\RequestManagers\HttpRequestManager;
|
|
use Web3\Utils;
|
|
use Web3\Web3;
|
|
|
|
class GethTokenService implements CoinServiceInterface
|
|
{
|
|
private $web3;
|
|
private $contract;
|
|
public $eth;
|
|
private $personal;
|
|
private $provider;
|
|
private $contractAddress;
|
|
|
|
private $apikey = 'cfaf58162bea2da0b03e76e2dd64207a06b27233779d5cc25176a92e0973';
|
|
private $gwei = '300';
|
|
private $gas = 80000; // 合约transfer交易所需gas
|
|
private $gas2 = 21656; // ETH交易所需gas
|
|
|
|
public function __construct($contractAddress, $abi)
|
|
{
|
|
$this->password = config('coin.geth_pwd');
|
|
$this->provider = config('coin.geth_host');
|
|
$this->eth = new Eth($this->provider);
|
|
$this->personal = new Personal($this->provider);
|
|
$this->contractAddress = $contractAddress;
|
|
$this->contract = new Contract(new HttpProvider(new HttpRequestManager($this->provider, 30)), $abi);
|
|
}
|
|
|
|
public function weiToUsdt($value)
|
|
{
|
|
list($bnq, $bnr) = Utils::fromWei($value, 'ether');
|
|
$balance = $bnq->toString() + ($bnr->toString() / pow(10, 6));
|
|
return custom_number_format($balance, 6);
|
|
}
|
|
|
|
public function getBalance($account)
|
|
{
|
|
$this->contract->at($this->contractAddress)->call('balanceOf', $account, function ($err, $data) use (&$balance) {
|
|
if ($data) {
|
|
return $balance = $this->weiToUsdt($data[0]);
|
|
}
|
|
return $balance = -1;
|
|
});
|
|
return $balance;
|
|
}
|
|
|
|
public function listAccounts()
|
|
{
|
|
$this->personal->listAccounts(function ($err, $account) use (&$accountList) {
|
|
if ($err !== null) {
|
|
// do something
|
|
$accountList = 0;
|
|
return 0;
|
|
}
|
|
$accountList = $account;
|
|
dd($accountList);
|
|
});
|
|
return $accountList;
|
|
}
|
|
|
|
/*解锁账户*/
|
|
public function unlockAccount($address, $password)
|
|
{
|
|
$param = [$address, $password, 10];
|
|
$result = $this->interactiveEth('personal_unlockAccount', $param);
|
|
if ($result) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*发起代币交易*/
|
|
public function sendTransaction($from, $toAddress, $amount, $decimals = 18)
|
|
{
|
|
if ($this->unlockAccount($from, $this->password) != 1) return 0;
|
|
|
|
// Token合约地址
|
|
$contractAddress = $this->contractAddress;
|
|
|
|
$bet = pow(10, $decimals); // 代币发布时小数点位数 decimals
|
|
$value = base_convert(custom_number_format($amount * $bet, 0), 10, 16);
|
|
|
|
// nonce
|
|
$cb = new Callback;
|
|
$this->eth->getTransactionCount($from, 'latest', $cb);
|
|
$nonce = $cb->result;
|
|
$nonce = $nonce->toString();
|
|
|
|
$gasPrice = Utils::toHex(Utils::toWei($this->getEthGasPrice('fast'), 'Gwei'), true);
|
|
$gas = $this->getGasUse();
|
|
|
|
$this->contract->at($contractAddress)->send(
|
|
'transfer',
|
|
$toAddress,
|
|
$value,
|
|
[
|
|
"from" => $from,
|
|
"gas" => $gas,
|
|
"gasPrice" => $gasPrice,
|
|
'nonce' => $nonce == 0 ? '0x0' : Utils::toHex($nonce, true),
|
|
],
|
|
function ($err, $data) use (&$result) {
|
|
if ($err !== null) {
|
|
return $result = 0;
|
|
}
|
|
if (strlen($data) < 5) return 0; //成功但没有获取到hash
|
|
return $result = $data;
|
|
}
|
|
);
|
|
return $result;
|
|
}
|
|
|
|
public function collection($from, $to, $value)
|
|
{
|
|
$contractAddress = config('coin.erc20_usdt.contractAddress');
|
|
$password = $this->password;
|
|
if ($this->unlockAccount($from, $password) != 1) return 0;
|
|
|
|
$gasPrice = Utils::toHex(Utils::toWei($this->getEthGasPrice('fast'), 'Gwei'), true);
|
|
$gas = $this->getGasUse();
|
|
|
|
// $value = "0x" . base_convert(bcmul($value,'1000000000000000000',0),10,16);
|
|
$bet = 1000000; // 代币发布时小数点位数 decimals
|
|
$value = base_convert($value * $bet, 10, 16);
|
|
|
|
// nonce
|
|
$cb = new Callback;
|
|
$this->eth->getTransactionCount($from, 'latest', $cb);
|
|
$nonce = $cb->result;
|
|
$nonce = $nonce->toString();
|
|
|
|
$this->contract->at($contractAddress)->send(
|
|
'transfer',
|
|
$to,
|
|
$value,
|
|
[
|
|
"from" => $from,
|
|
"gas" => $gas,
|
|
"gasPrice" => $gasPrice,
|
|
'nonce' => $nonce == 0 ? '0x0' : Utils::toHex($nonce, true),
|
|
// 'value' => '0x0',
|
|
// 'data' => '0x' . 'a9059cbb' . str_pad(substr($to, 2), 64, "0", STR_PAD_LEFT) . str_pad($value, 64, "0", STR_PAD_LEFT),
|
|
],
|
|
function ($err, $data) use (&$result) {
|
|
if ($err !== null) {
|
|
return $result = 0;
|
|
}
|
|
if (strlen($data) < 5) return 0; //成功但没有获取到hash
|
|
return $result = $data;
|
|
}
|
|
);
|
|
return $result;
|
|
}
|
|
|
|
/*获取symbol*/
|
|
public function getSymbol()
|
|
{
|
|
$this->contract->at($this->contractAddress)->call('symbol', function ($err, $data) use (&$result) {
|
|
if ($err) {
|
|
return $result = 0;
|
|
}
|
|
return $result = $data[0];
|
|
});
|
|
return $result;
|
|
}
|
|
|
|
public function interactiveEth($method, array $params)
|
|
{
|
|
$opts = [
|
|
'json' => [
|
|
'jsonrpc' => '2.0',
|
|
'method' => $method,
|
|
'params' => $params,
|
|
'id' => time()
|
|
]
|
|
];
|
|
$rsp = (new Client())->post($this->provider, $opts);
|
|
if (isset(\GuzzleHttp\json_decode($rsp->getBody())->error)) return 0;
|
|
// dd(\GuzzleHttp\json_decode($rsp->getBody()));
|
|
return \GuzzleHttp\json_decode($rsp->getBody())->result;
|
|
}
|
|
|
|
public function getTransaction($transactionId)
|
|
{
|
|
// TODO: Implement getTransaction() method.
|
|
}
|
|
|
|
public function newAccount()
|
|
{
|
|
// TODO: Implement newAccount() method.
|
|
}
|
|
|
|
// ERC20-Token 离线构建交易并广播
|
|
public function sendRawToken($from, $private_key, $to, $amount, $contractAddress = '0xdac17f958d2ee523a2206206994597c13d831ec7')
|
|
{
|
|
try {
|
|
$etherscan = new Etherscan();
|
|
$credential = Credential::fromKey($private_key);
|
|
$walletAddress = $credential->getAddress();
|
|
if ($private_key != $credential->getPrivateKey()) {
|
|
info('error PrivateKey');
|
|
return false;
|
|
}
|
|
|
|
$gasPrice = Utils::toHex(Utils::toWei($this->getEthGasPrice('fast'), 'Gwei'), true);
|
|
$gas = $this->getGasUse();
|
|
|
|
if ($contractAddress == '0xdac17f958d2ee523a2206206994597c13d831ec7') {
|
|
$decimals = 6; // 这里是USDT 精度是6
|
|
} else {
|
|
$decimals = 18;
|
|
}
|
|
$bet = pow(10, $decimals); // 代币发布时小数点位数 decimals
|
|
$value = base_convert(custom_number_format($amount * $bet, 0), 10, 16);
|
|
|
|
// nonce
|
|
$nonce = $etherscan->getTransactionCount($from, 'latest');
|
|
// $nonce = $nonce->toString();
|
|
|
|
$raw = [
|
|
// 'nonce' => $nonce == 0 ? '0x0' : Utils::toHex($nonce, true),
|
|
'nonce' => $nonce,
|
|
'gasPrice' => $gasPrice,
|
|
'gasLimit' => $gas, //16进制 十进制60000
|
|
'to' => $contractAddress, //代币合约地址
|
|
'value' => '0x0',
|
|
//8位方法名 64位对方地址 64位金额
|
|
'data' => '0x' . 'a9059cbb' . str_pad(substr($to, 2), 64, "0", STR_PAD_LEFT) . str_pad($value, 64, "0", STR_PAD_LEFT),
|
|
'chainId' => 1,
|
|
];
|
|
$signed = $credential->signTransaction($raw); // 进行离线签名
|
|
// dd($signed,$raw);
|
|
$hash = $etherscan->sendRawTransaction($signed); // 发送裸交易
|
|
} catch (\Exception $e) {
|
|
info($e);
|
|
return false;
|
|
}
|
|
|
|
return $hash;
|
|
}
|
|
|
|
/**
|
|
* 以太坊网络上的快速,标准和安全的低汽油价格
|
|
*/
|
|
public function getEthGasPrice($t = 'average')
|
|
{
|
|
// $url = 'https://data-api.defipulse.com/api/v1/egs/api/ethgasAPI.json?api-key=' . $this->apikey;
|
|
$url = 'https://ethgasstation.info/api/ethgasAPI.json?api-key=' . $this->apikey;
|
|
$rsp = (new Client())->get($url);
|
|
if (isset(\GuzzleHttp\json_decode($rsp->getBody())->error)) return $this->gwei;
|
|
|
|
$data = \GuzzleHttp\json_decode($rsp->getBody());
|
|
if ($t == 'average') {
|
|
if (!isset($data->average)) return $this->gwei;
|
|
$average = $data->average / 10;
|
|
return $average > $this->gwei ? $this->gwei : (string)$average;
|
|
} else {
|
|
if (isset($data->fast)) {
|
|
$fast = $data->fast / 10;
|
|
return $fast > $this->gwei ? $this->gwei : (string)$fast;
|
|
}
|
|
return $this->gwei;
|
|
}
|
|
}
|
|
|
|
// 获取交易预估gas用量
|
|
public function getGasUse($estimate = true, $transaction = [])
|
|
{
|
|
if ($estimate) {
|
|
return Utils::toHex($this->gas, true);
|
|
} else {
|
|
$gas = $this->interactiveEth('eth_estimateGas', $transaction);
|
|
return $gas ?: Utils::toHex($this->gas, true);
|
|
}
|
|
}
|
|
}
|
|
|