最近在写ETH的NFT发行转账功能,使用的语言是PHP,但是发现github上使用比较多的web3.php有点问题,当solidity使用string[]类型时候web3.php没有做兼容,最后会导致签名后的数据有问题,交易出现 Warning! Error encountered during contract execution [execution reverted] ,修改后特意来记录一下。
composer.php:
"require"
:
{
"sc0vu/web3.php"
:
"dev-master"
,
"web3p/ethereum-tx"
:
"dev-master"
,
"simplito/elliptic-php"
:
"~1.0.4"
,
"kornrunner/keccak"
:
"~1.0"
,
"graze/guzzle-jsonrpc"
:
"^3.2"
,
"bitwasp/buffertools"
:
"^0.5.0"
IPFSapi和ETHapi都是使用的infura:
https://infura.io/
php上传文件到IPFS可以参考前面的文章,ETH的原生签名交易也可以参考前面的文章。
web3实现ETH ERC20、ERC721签名时候data数据的拼装,封装了一个类可以参考一下:
* Created by PhpStorm.
* User: Echo
* Date: 2021/8/24
* Time: 5:07 PM
use
EthTool\\Credential
;
use
EthTool\\EthInfuraApi
;
use
Web3\\Contract
;
use
Web3\\Utils
;
use
EthTool\\EthApi
;
use
Web3\\Contracts\\Ethabi
;
use
Web3\\Contracts\\Types\\Address
;
use
Web3\\Contracts\\Types\\Boolean
;
use
Web3\\Contracts\\Types\\Bytes
;
use
Web3\\Contracts\\Types\\DynamicBytes
;
use
Web3\\Contracts\\Types\\Integer
;
use
Web3\\Contracts\\Types\\Str
;
use
Web3\\Contracts\\Types\\Uinteger
;
use
IPFS
\\
IPFS
;
use
Web3\\Web3
;
class
Nft
{
private
$abi
=
'你的ABI'
;
private
$contract_address
=
''
;
private
$key
=
""
;
private
$self_address
=
""
;
private
$credential
;
private
$eth_host
=
""
;
private
$api_key
=
""
;
private
$ethabi
;
private
$eth_api
;
private
$address_key
=
"address:nonce:key:"
;
* Nft constructor.
public
function
__construct
(
)
{
$eth_config
=
config
(
"myconfig.ETH"
)
;
$this
->
contract_address
=
$eth_config
[
"contract_address"
]
;
$this
->
key
=
$eth_config
[
"my_key"
]
;
$this
->
credential
=
Credential
::
fromKey
(
$this
->
key
)
;
$this
->
self_address
=
$this
->
credential
->
getAddress
(
)
;
$this
->
eth_host
=
$eth_config
[
"api_host"
]
;
$this
->
api_key
=
$eth_config
[
"api_key"
]
;
$this
->
eth_api
=
new
EthInfuraApi
(
$this
->
eth_host
,
$this
->
api_key
)
;
$this
->
ethabi
=
new
Ethabi
(
[
'address'
=>
new
Address
,
'bool'
=>
new
Boolean
,
'bytes'
=>
new
Bytes
,
'dynamicBytes'
=>
new
DynamicBytes
,
'int'
=>
new
Integer
,
'string'
=>
new
Str
,
'uint'
=>
new
Uinteger
,
]
)
;
* 发行NFT
* @param $name string NFT名称
* @param $description string NFT介绍
* @param $img_url string NFT图片在阿里云的链接
* @param int $number 要发行的个数
* @return array|bool|int|mixed|null|string
* status: -1为失败 1成功
* ipfs_img_url_hash: 图片上传到IPFS的hash
* ipfs_nft_info_url_hash: NFT信息上传到IPFS的hash
public
function
createNft
(
$name
,
$description
,
$img_url
,
$number
=
1
)
{
$number
=
(
int
)
$number
;
if
(
!
$name
||
!
$description
||
!
$img_url
||
$number
<
1
)
{
return
[
"status"
=>
-
1
,
"msg"
=>
"参数格式有误"
$return_info
=
[
"name"
=>
$name
,
"description"
=>
$description
,
"img_url"
=>
$img_url
,
"mint_hash"
=>
""
,
"nft_info"
=>
[
]
$img_url_hash
=
$this
->
uploadPhoto
(
$img_url
)
;
if
(
is_array
(
$img_url_hash
)
&&
isset
(
$img_url_hash
[
"status"
]
)
)
{
return
$img_url_hash
;
$info_url
=
$this
->
uploadData
(
$img_url_hash
,
$name
,
$description
)
;
if
(
is_array
(
$info_url
)
&&
isset
(
$info_url
[
"status"
]
)
)
{
return
$info_url
;
$new_token_id
=
$this
->
getNewTokenId
(
$this
->
self_address
,
$info_url
)
;
if
(
is_array
(
$new_token_id
)
&&
isset
(
$new_token_id
[
"status"
]
)
)
{
return
$new_token_id
;
if
(
$number
==
1
)
{
$mint_hash
=
$this
->
mintNft
(
$this
->
self_address
,
$info_url
)
;
if
(
is_array
(
$mint_hash
)
&&
isset
(
$mint_hash
[
"status"
]
)
)
{
return
$mint_hash
;
array_push
(
$return_info
[
"nft_info"
]
,
"ipfs_img_url_hash"
=>
$img_url_hash
,
"ipfs_nft_info_url_hash"
=>
$info_url
,
"nft_id"
=>
$new_token_id
}
else
{
$address_array
=
[
]
;
$url_array
=
[
]
;
for
(
$i
=
1
;
$i
<=
$number
;
$i
++
)
{
$address_array
[
]
=
$this
->
self_address
;
$url_array
[
]
=
$info_url
;
array_push
(
$return_info
[
"nft_info"
]
,
"ipfs_img_url_hash"
=>
$img_url_hash
,
"ipfs_nft_info_url_hash"
=>
$info_url
,
"nft_id"
=>
$new_token_id
$new_token_id
+=
1
;
$mint_hash
=
$this
->
mintArrayNft
(
$address_array
,
$url_array
)
;
if
(
is_array
(
$mint_hash
)
&&
isset
(
$mint_hash
[
"status"
]
)
)
{
return
$mint_hash
;
$return_info
[
"mint_hash"
]
=
$mint_hash
;
return
[
"status"
=>
1
,
"msg"
=>
"成功"
,
"data"
=>
$return_info
try
{
$key
=
Credential
::
newWallet
(
)
;
$credential
=
Credential
::
fromKey
(
$key
)
;
return
$data
=
[
'private'
=>
$credential
->
getPrivateKey
(
)
,
'public'
=>
$credential
->
getPublicKey
(
)
,
'address'
=>
$credential
->
getAddress
(
)
}
catch
(
\
\
Exception
$e
)
{
return
[
"status"
=>
$e
->
getCode
(
)
,
"msg"
=>
$e
->
getMessage
(
)
* 查询地址中NFT的数量
* @param $address
* @return array|bool|mixed|string
public
function
nftBalance
(
$address
)
{
try
{
$param_data
=
$this
->
ethabi
->
encodeParameter
(
'address'
,
$address
)
;
$param_data
=
Utils
::
stripZero
(
$param_data
)
;
$method_id
=
$this
->
ethabi
->
encodeFunctionSignature
(
"balanceOf(address)"
)
;
$number
=
$this
->
eth_api
->
getCall
(
$this
->
self_address
,
$this
->
contract_address
,
"0x0"
,
$method_id
.
$param_data
)
;
$number
=
Utils
::
toBn
(
$number
)
->
toString
(
)
;
return
$number
;
}
catch
(
\
\
Exception
$e
)
{
return
[
"status"
=>
$e
->
getCode
(
)
,
"msg"
=>
$e
->
getMessage
(
)
* 根据tokenID返回持有者地址
* @param $id
* @return array|bool|mixed|string
public
function
getAddressByTokenId
(
$id
)
{
try
{
$param_data
=
$this
->
ethabi
->
encodeParameter
(
'uint256'
,
$id
)
;
$param_data
=
Utils
::
stripZero
(
$param_data
)
;
$method_id
=
$this
->
ethabi
->
encodeFunctionSignature
(
"ownerOf(uint256)"
)
;
$address
=
$this
->
eth_api
->
getCall
(
$this
->
self_address
,
$this
->
contract_address
,
"0x0"
,
$method_id
.
$param_data
)
;
$address
=
$this
->
ethabi
->
decodeParameter
(
'address'
,
$address
)
;
return
$address
;
}
catch
(
\
\
Exception
$e
)
{
return
[
"status"
=>
$e
->
getCode
(
)
,
"msg"
=>
$e
->
getMessage
(
)
* 根据TokenID返回Token的URL信息
* @param $id
* @return array|bool|mixed|string
public
function
getUrlByTokenId
(
$id
)
{
try
{
$param_data
=
$this
->
ethabi
->
encodeParameter
(
'uint256'
,
$id
)
;
$param_data
=
Utils
::
stripZero
(
$param_data
)
;
$method_id
=
$this
->
ethabi
->
encodeFunctionSignature
(
"tokenURI(uint256)"
)
;
$url
=
$this
->
eth_api
->
getCall
(
$this
->
self_address
,
$this
->
contract_address
,
"0x0"
,
$method_id
.
$param_data
)
;
$url
=
$this
->
ethabi
->
decodeParameter
(
'string'
,
$url
)
;
return
$url
;
}
catch
(
\
\
Exception
$e
)
{
return
[
"status"
=>
$e
->
getCode
(
)
,
"msg"
=>
$e
->
getMessage
(
)
* 返回最新的NFT的ID(预铸造、本地计数使用)
* @param $to_address
* @param $nft_url
* @return array|bool|mixed|string
public
function
getNewTokenId
(
$to_address
,
$nft_url
)
{
try
{
$param_data
=
$this
->
ethabi
->
encodeParameters
(
[
'address'
,
'string'
]
,
[
$to_address
,
"ipfs://"
.
$nft_url
]
$param_data
=
Utils
::
stripZero
(
$param_data
)
;
$method_id
=
$this
->
ethabi
->
encodeFunctionSignature
(
"mint(address,string)"
)
;
$number
=
$this
->
eth_api
->
getCall
(
$this
->
self_address
,
$this
->
contract_address
,
"0x0"
,
$method_id
.
$param_data
)
;
$number
=
Utils
::
toBn
(
$number
)
->
toString
(
)
;
return
$number
;
}
catch
(
\
\
Exception
$e
)
{
return
[
"status"
=>
$e
->
getCode
(
)
,
"msg"
=>
$e
->
getMessage
(
)
* 铸造一个NFT
* @param $to_address address 发布到的地址
* @param $nft_url string NFT的信息URL
* @return array|bool|mixed
public
function
mintNft
(
$to_address
,
$nft_url
)
{
try
{
$param_data
=
$this
->
ethabi
->
encodeParameters
(
[
'address'
,
'string'
]
,
[
$to_address
,
"ipfs://"
.
$nft_url
]
$param_data
=
Utils
::
stripZero
(
$param_data
)
;
$method_id
=
$this
->
ethabi
->
encodeFunctionSignature
(
"mint(address,string)"
)
;
$address_key
=
$this
->
address_key
.
$this
->
self_address
;
getRedis
(
)
->
del
(
$address_key
)
;
$nonce_num
=
getRedis
(
)
->
get
(
$address_key
)
;
if
(
!
$nonce_num
)
{
$nonce_num
=
$this
->
getNonce
(
$this
->
self_address
)
;
$nonce
=
Utils
::
toHex
(
$nonce_num
,
true
)
;
$gas_limit
=
$this
->
eth_api
->
getEstimateGas
(
$this
->
self_address
,
$this
->
contract_address
,
"0x0"
,
$method_id
.
$param_data
)
;
$gasprice
=
$this
->
eth_api
->
getGasPrice
(
)
;
$data
=
[
'nonce'
=>
$nonce
,
'gasPrice'
=>
$gasprice
,
'gasLimit'
=>
$gas_limit
,
'to'
=>
$this
->
contract_address
,
'value'
=>
'0x0'
,
'data'
=>
$method_id
.
$param_data
,
'chainId'
=>
137
$signed
=
$this
->
credential
->
signTransaction
(
$data
)
;
$hash
=
$this
->
eth_api
->
sendRawTransaction
(
$signed
)
;
getRedis
(
)
->
setex
(
$address_key
,
43200
,
$nonce_num
+
1
)
;
return
$hash
;
}
catch
(
\
\
Exception
$e
)
{
return
[
"status"
=>
$e
->
getCode
(
)
,
"msg"
=>
$e
->
getMessage
(
)
public
function
web3Test
(
$address
,
$url
)
{
$web3
=
new
Web3
(
$this
->
eth_host
.
$this
->
api_key
)
;
$contract
=
new
Contract
(
$web3
->
provider
,
$this
->
abi
)
;
$data_aaa
=
$contract
->
at
(
$this
->
contract_address
)
->
getData
(
'mintArray'
,
$address
,
$url
)
;
return
$data_aaa
;
* @param $to_address array 发布到的地址
* @param $nft_url array NFT的信息URL
* @return array|bool|mixed
public
function
mintArrayNft
(
$to_address
,
$nft_url
)
{
if
(
count
(
$to_address
)
!==
count
(
$nft_url
)
||
count
(
$to_address
)
<
1
)
{
return
[
"status"
=>
-
1
,
"msg"
=>
"地址数和NFT信息数不同"
try
{
foreach
(
$nft_url
as
&
$one_url
)
{
$one_url
=
"ipfs://"
.
$one_url
;
$param_data
=
$this
->
ethabi
->
encodeParameters
(
[
'address[]'
,
'string[]'
]
,
[
$to_address
,
$nft_url
]
$param_data
=
Utils
::
stripZero
(
$param_data
)
;
$method_id
=
$this
->
ethabi
->
encodeFunctionSignature
(
"mintArray(address[],string[])"
)
;
$address_key
=
$this
->
address_key
.
$this
->
self_address
;
getRedis
(
)
->
del
(
$address_key
)
;
$nonce_num
=
getRedis
(
)
->
get
(
$address_key
)
;
if
(
!
$nonce_num
)
{
$nonce_num
=
$this
->
getNonce
(
$this
->
self_address
)
;
$nonce
=
Utils
::
toHex
(
$nonce_num
,
true
)
;
$gas_limit
=
$this
->
eth_api
->
getEstimateGas
(
$this
->
self_address
,
$this
->
contract_address
,
"0x0"
,
$method_id
.
$param_data
)
;
$gasprice
=
$this
->
eth_api
->
getGasPrice
(
)
;
$data
=
[
'nonce'
=>
$nonce
,
'gasPrice'
=>
$gasprice
,
'gasLimit'
=>
$gas_limit
,
'to'
=>
$this
->
contract_address
,
'value'
=>
'0x0'
,
'data'
=>
$method_id
.
$param_data
,
'chainId'
=>
137
$signed
=
$this
->
credential
->
signTransaction
(
$data
)
;
$hash
=
$this
->
eth_api
->
sendRawTransaction
(
$signed
)
;
getRedis
(
)
->
setex
(
$address_key
,
43200
,
$nonce_num
+
1
)
;
return
$hash
;
}
catch
(
\
\
Exception
$e
)
{
return
[
"status"
=>
$e
->
getCode
(
)
,
"msg"
=>
$e
->
getMessage
(
)
public
function
transferNft
(
$to_address
,
$nft_id
)
{
public
function
transferMatic
(
$to_address
,
$number
)
{
* 拿到地址交易的nonce值
* @param $address
* @return bool|mixed|string
* @throws \\Exception
protected
function
getNonce
(
$address
)
{
$nonce_num
=
$this
->
eth_api
->
getTransactionCount
(
$address
)
;
if
(
$nonce_num
==
false
)
{
return
false
;
$nonce_num
=
Utils
::
toBn
(
$nonce_num
)
->
toString
(
)
;
return
$nonce_num
;
* 往IPFS上传图片
* @param $url string 图片的远程链接
* @return array|mixed|null
public
function
uploadPhoto
(
$url
)
{
try
{
$ipfs
=
new
IPFS
(
)
;
$hash
=
$ipfs
->
addFromUrl
(
$url
)
;
return
$hash
;
}
catch
(
\
\
Exception
$e
)
{
return
[
"status"
=>
$e
->
getCode
(
)
,
"msg"
=>
$e
->
getMessage
(
)
* 根据图片的链接生成NFT介绍信息并上传到IPFS
* @param $url string 图片在IPFS的链接
* @param $name string NFT名称
* @param $description string NFT介绍
* @return array|null|string
public
function
uploadData
(
$url
,
$name
,
$description
)
{
try
{
$ipfs
=
new
IPFS
(
)
;
$data
=
[
"name"
=>
$name
,
"description"
=>
$description
,
"image"
=>
"ipfs://"
.
$url
$hash
=
$ipfs
->
add
(
json_encode
(
$data
)
)
;
return
$hash
;
}
catch
(
\
\
Exception
$e
)
{
return
[
"status"
=>
$e
->
getCode
(
)
,
"msg"
=>
$e
->
getMessage
(
)
web3.php有问题的代码主要为encodeParameters()方法,也就是对data数据需要的方法和参数进行转换时候。
修改的具体文件为:
vendor/sc0vu/web3.php/src/Contracts/SolidityType.php encode
方法,下面为我修改后的代码:
* encode
* @param mixed $value
* @param string $name
* @return string
public
function
encode
(
$value
,
$name
)
if
(
$this
->
isDynamicArray
(
$name
)
)
{
$length
=
count
(
$value
)
;
$nestedName
=
$this
->
nestedName
(
$name
)
;
$result
=
[
]
;
$result
[
]
=
IntegerFormatter
::
format
(
$length
)
;
if
(
$this
->
isDynamicType
(
$nestedName
)
)
{
$start
=
0
;
foreach
(
$value
as
$k
=>
$val
)
{
if
(
$start
==
0
)
{
$l
=
$length
*
32
;
}
else
{
$v_1
=
Utils
::
toHex
(
$value
[
$k
-
1
]
)
;
$l
=
(
floor
(
(
mb_strlen
(
$v_1
)
+
63
)
/
64
)
+
1
)
*
32
;
$start
+=
$l
;
$result
[
]
=
IntegerFormatter
::
format
(
$start
)
;
foreach
(
$value
as
$val
)
{
$result
[
]
=
$this
->
encode
(
$val
,
$nestedName
)
;
return
$result
;
}
elseif
(
$this
->
isStaticArray
(
$name
)
)
{
$length
=
$this
->
staticArrayLength
(
$name
)
;
$nestedName
=
$this
->
nestedName
(
$name
)
;
$result
=
[
]
;
foreach
(
$value
as
$val
)
{
$result
[
]
=
$this
->
encode
(
$val
,
$nestedName
)
;
return
$result
;
return
$this
->
inputFormat
(
$value
,
$name
)
;
之所以这么改是因为官方说string是动态元素,所以要加上偏移量,原话如下:
since strings are dynamic elements we need to find their offsets
c
,
d
and
e
:
参考链接:
https://docs.soliditylang.org/en/latest/abi-spec.html#use-of-dynamic-types
ERC20
令牌水龙头
以太坊
主网上以及Ropsten,Kovan,Rinkeby和Görli测试网上的
ERC20
令牌龙头。
访问页面,设置适当的代币数量,单击“免费代币”按钮。 而已!
您将需要一些以太(
ETH
)来支付网络
交易
费用。 请参阅下面的,以获得testnet醚。
部署的水龙头令牌
Testnet乙醚龙头
Testnet
ETH
龙头
请提交PR以及上述信息的更新。
(c)PepperSec.com/麻省理工学院执照。
EOSIO21协议:victory_hand_light_skin_tone::index_pointing_up_medium-light_skin_tone:
将您的
ERC20
令牌传送到EOS(或任何EOSIO侧链或分叉-例如WAX,TELOS或BOS)。
EOSIO21是启用跨链的协议 :chains:
ETH
和EOS之间的代币移动。
ETH
(
ERC20
)-> EOS21->任何EOSIO链(代币)
该协议的目的是为应用程序开发人员提供一种在链之间移动其令牌和应用程序的标准。
加入,讨论EOS21。 另外,请考虑对sheos21sheos作为Block Producer投票。
-EOS区块链
-EOS合同开发工具包
-Javascript运行时(已通过v8.10和10.11测试)
EOSIO
Chainlink是一个去中心化的预言机网络,它可以让区块链中的智能合约安全地访问外部世界的数据。在这个教程中,我们将探索chainlink预言机网络的搭建,并学习如何使用预置或自定义的适配器
实现
智能合约与外部世界数据的桥接。
以太坊
教程链接:
Dapp入门 | 电商Dapp实战 |
ERC721
实战 |
Php
对接
| Java
对接
| Python
对接
| C#
对接
| Dart
对接
智能合约被锁定在区块链里,与外部世界隔离开来。然而在许多应用中,智能合约的 运行需要依赖于外部真实世界的信息。
以Ocean协议为例:只有当提供的数据被证明是可以使用时,数据提供商才可以得到代币奖励。因此一个
composer require sc0vu/web3.
php
dev-master
或者您可以在composer.json中添加此行
"sc0vu/web3.
php
": "dev-master"
use Web3 \ Web3 ;
$ web3 = new Web3 ( 'http://localhost:8545' );
使用提供者
use Web3 \ Web3 ;
use Web3 \ Providers \
所有
实现
了这些函数的合约都是
ERC20
Token
ERC20
可以表示任何同质的可以
交易
的内容: 货币、股票、积分、债券、利息...
可以用数量来表示的内容 基本上可以
ERC20
表示
ERC 20 的缺点
以下是一个遇到很多次的场景:有一天老板过来找你(开发者),最近存币生息很火,我们也做一个合约吧, 用户打币过来给他计算利息, 看起来是一个很简单的需求,你满口答应说好,结果自
linux中混杂设备的定义:在linux中存在一类字符设备,它们共享一个主设备号,但次设备号不同,这类字符设备被称为混杂设备。
linux中描述一个混杂设备
struct miscdevice {
int minor; 次设备号
const char * name; 设备名
const struct file_operations *ops;
struct head_list head;
struct device *parent;
struct device *this_module;
注册一个混杂设
好的。
ERC20
是一种常用的
以太坊
代币标准,用Web3.js来
实现
ERC20
授权,你需要先确保已经安装并配置了Web3.js。
一、首先,你需要获取
ERC20
合约的地址和ABI(应用程序二进制接口)。
二、然后,你可以使用Web3.js的`
eth
.contract`方法来创建一个合约对象。
const contract = new web3.
eth
.Contract(ABI, contractAddress);
三、接着,你可以调用合约对象的`approve`方法来
实现
授权。
contract.m
eth
ods.approve(spender, amount).send({
from: owner,
gas: GAS_LIMIT
}, function(error, transactionHash) {
if (!error) {
console.log(transactionHash);
} else {
console.error(error);
上面的代码中,`spender`是你要授权的地址,`amount`是你要授权的数量,`owner`是你的地址,`GAS_LIMIT`是执行
交易
所需的最大gas数量。
注意:需要确保你在调用`approve`方法时已经连接到
以太坊
网络,并且你有足够的以太币来支付执行
交易
的gas费用。