部署和调用
智能合约的部署
部署合约其实是创建了一个CreateSmartContract类型的交易,可以通过fullnode api wallet/deploycontract来创建交易,创建完成后,再进行交易的签名和广播:
curl --request POST \
--url https://api.shasta.trongrid.io/wallet/deploycontract \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '
{
"owner_address": "41D1E7A6BC354106CB410E65FF8B181C600FF14292",
"abi": "[{\"constant\":false,\"inputs\":[{\"name\":\"key\",\"type\":\"uint256\"},{\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"key\",\"type\":\"uint256\"}],\"name\":\"get\",\"outputs\":[{\"name\":\"value\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]",
"bytecode": "608060405234801561001057600080fd5b5060de8061001f6000396000f30060806040526004361060485763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416631ab06ee58114604d5780639507d39a146067575b600080fd5b348015605857600080fd5b506065600435602435608e565b005b348015607257600080fd5b50607c60043560a0565b60408051918252519081900360200190f35b60009182526020829052604090912055565b600090815260208190526040902054905600a165627a7a72305820fdfe832221d60dd582b4526afa20518b98c2e1cb0054653053a844cf265b25040029",
"fee_limit": 1000000,
"origin_energy_limit": 100000,
"name": "SomeContract",
"call_value": 0,
"consume_user_resource_percent": 100
}
'返回的结果为:
{
"visible": false,
"txID": "e4eb4df3a64a33565059ee3cef29b93b79f58ece9e7e8153fda3bbfe40fb0524",
"contract_address": "416c5d359d1836085cdad65788e1ce53d3d7a13dd6",
"raw_data": {
"contract": [
{
"parameter": {
"value": {
"owner_address": "41d1e7a6bc354106cb410e65ff8b181c600ff14292",
"new_contract": {
"bytecode": "608060405234801561001057600080fd5b5060de8061001f6000396000f30060806040526004361060485763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416631ab06ee58114604d5780639507d39a146067575b600080fd5b348015605857600080fd5b506065600435602435608e565b005b348015607257600080fd5b50607c60043560a0565b60408051918252519081900360200190f35b60009182526020829052604090912055565b600090815260208190526040902054905600a165627a7a72305820fdfe832221d60dd582b4526afa20518b98c2e1cb0054653053a844cf265b25040029",
"consume_user_resource_percent": 100,
"name": "SomeContract",
"origin_address": "41d1e7a6bc354106cb410e65ff8b181c600ff14292",
"abi": {
"entrys": [
{
"inputs": [
{
"name": "key",
"type": "uint256"
},
{
"name": "value",
"type": "uint256"
}
],
"name": "set",
"stateMutability": "Nonpayable",
"type": "Function"
},
{
"outputs": [
{
"name": "value",
"type": "uint256"
}
],
"constant": true,
"inputs": [
{
"name": "key",
"type": "uint256"
}
],
"name": "get",
"stateMutability": "View",
"type": "Function"
}
]
},
"origin_energy_limit": 100000
}
},
"type_url": "type.googleapis.com/protocol.CreateSmartContract"
},
"type": "CreateSmartContract"
}
],
"ref_block_bytes": "0a49",
"ref_block_hash": "975853d6629c8702",
"expiration": 1652153760000,
"fee_limit": 1000000,
"timestamp": 1652153701556
},
"raw_data_hex": "0a020a492208975853d6629c87024080f2a6e08a305add03081e12d8030a30747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e437265617465536d617274436f6e747261637412a3030a1541d1e7a6bc354106cb410e65ff8b181c600ff142921289030a1541d1e7a6bc354106cb410e65ff8b181c600ff142921a5c0a2b1a03736574220e12036b65791a0775696e743235362210120576616c75651a0775696e74323536300240030a2d10011a03676574220e12036b65791a0775696e743235362a10120576616c75651a0775696e743235363002400222fd01608060405234801561001057600080fd5b5060de8061001f6000396000f30060806040526004361060485763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416631ab06ee58114604d5780639507d39a146067575b600080fd5b348015605857600080fd5b506065600435602435608e565b005b348015607257600080fd5b50607c60043560a0565b60408051918252519081900360200190f35b60009182526020829052604090912055565b600090815260208190526040902054905600a165627a7a72305820fdfe832221d60dd582b4526afa20518b98c2e1cb0054653053a844cf265b2504002930643a0c536f6d65436f6e747261637440a08d0670b4a9a3e08a309001c0843d"
}不同类型的交易的交易的结构都是一样的,只是raw_data中包含的contract内容不同,主要体现在交易中的contract.parameter.value字段.
部署合约交易中的contract.parameter.value包含以下内容:
owner_address: 合约部署者地址new_contract: 新部署的合约的详细信息- origin_address:合约部署者地址
- contract_address:合约地址
- ABI:合约的ABI
- bytecode: 合约代码编译后字节码
- call_value:部署合约时传入合约的TRX数量
- consume_user_resource_percent:用户能量费用占比
- name:合约名
- origin_energy_limit:对于每笔交易消耗合约部署者的能量上限,以sun为单位
call_token_value: 发送到新部署合约的TRC10代币数量token_id: TRC10 token id
智能合约的调用和查询
triggersmartcontract
合约调用是创建了一个TriggerSmartContract类型的交易,可以通过fullnode api wallet/triggersmartcontract来创建触发合约调用的交易,创建完成交易后,需要对其签名,然后广播到全网。
curl -X POST https://api.shasta.trongrid.io/wallet/triggersmartcontract -d '{
"contract_address":"419E62BE7F4F103C36507CB2A753418791B1CDC182",
"function_selector":"transfer(address,uint256)",
"parameter":"00000000000000000000004115208EF33A926919ED270E2FA61367B2DA3753DA0000000000000000000000000000000000000000000000000000000000000032",
"fee_limit":100000000,
"call_value":0,
"owner_address":"41977C20977F412C2A1AA4EF3D49FEE5EC4C31CDFB"
}'参数说明:
- contract_address:合约的地址
- owner_address:调用者地址
- function_selector:触发的合约方法
- parameter:编码后的合约方法的参数值,本例中应该传入的参数有两个,分别是address和uint256类型,具体如何对参数进行编码和解码,请参考参数编码解码章节
- fee_limit:调用者愿意承担的交易所消耗的能量的上限,请参考feelimit说明
- call_value:传入合约的TRX数量
上述命令执行成功后,将返回如下结果,其中包括一个合约调用交易transaction:
{
"result": {
"result": true
},
"transaction": {
"visible": false,
"txID": "d2ce86097df40287ad45ebc67f0d546ee98c2d7cd7c101e4d4d5b0c8a752d900",
"raw_data": {
"contract": [{
"parameter": {
"value": {
"data": "a9059cbb00000000000000000000004115208ef33a926919ed270e2fa61367b2da3753da0000000000000000000000000000000000000000000000000000000000000032",
"owner_address": "41977c20977f412c2a1aa4ef3d49fee5ec4c31cdfb",
"contract_address": "419e62be7f4f103c36507cb2a753418791b1cdc182"
},
"type_url": "type.googleapis.com/protocol.TriggerSmartContract"
},
"type": "TriggerSmartContract"
}],
"ref_block_bytes": "1c51",
"ref_block_hash": "74912b480b7b887c",
"expiration": 1652169501000,
"fee_limit": 100000000,
"timestamp": 1652169442098
},
"raw_data_hex": "0a021c51220874912b480b7b887c40c8d2e7e78a305aae01081f12a9010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412740a1541977c20977f412c2a1aa4ef3d49fee5ec4c31cdfb1215419e62be7f4f103c36507cb2a753418791b1cdc1822244a9059cbb00000000000000000000004115208ef33a926919ed270e2fa61367b2da3753da000000000000000000000000000000000000000000000000000000000000003270b286e4e78a30900180c2d72f"
}
}合约调用交易中的contract.parameter.value包含以下内容:
- owner_address:合约调用者地址
- contract_address:合约地址
- data:合约函数选择器及其参数, 前4个字节是合约函数选择器,它是对合约函数名称及参数做Keccak-256 运算得到的结果的前 4 字节,用于虚拟机对函数的寻址。data的其余字节是函数的参数,具体如何编解码,请参考参数编码解码章节。
- call_value:发送到合约的TRX数量
- token_id:发送到合约的TRC10代币的token id
- call_token_value:发送到合约的TRC10代币的数量
triggerconstantcontract
通过fullnode api wallet/triggerconstantcontract来调用合约的常量函数,因为是查询操作,不需要上链,所以不需要签名和广播:
curl -X POST https://127.0.0.1:8090/wallet/triggerconstantcontract -d '{
"contract_address":"419E62BE7F4F103C36507CB2A753418791B1CDC182",
"function_selector":"balanceOf(address)",
"parameter":"000000000000000000000041977C20977F412C2A1AA4EF3D49FEE5EC4C31CDFB",
"owner_address":"41977C20977F412C2A1AA4EF3D49FEE5EC4C31CDFB"
}'
参数说明:
- contract_address:合约的地址
- owner_address:调用者地址
- function_selector:触发的合约方法
- parameter:合约方法要传入的参数,具体如何编解码,请参考参数编码解码章节。
返回结果如下:
{
"result": {
"result": true
},
"constant_result": ["0000000000000000000000000000000000000000000000000000000430e1b700"],
"transaction": {
"ret": [{}],
"visible": false,
"txID": "7f47212aed2fdab232195feece54fc302bde2ce379e92ffd0d0e95206ce7a3bb",
"raw_data": {
"contract": [{
"parameter": {
"value": {
"data": "70a08231000000000000000000000041977c20977f412c2a1aa4ef3d49fee5ec4c31cdfb",
"owner_address": "41977c20977f412c2a1aa4ef3d49fee5ec4c31cdfb",
"contract_address": "419e62be7f4f103c36507cb2a753418791b1cdc182"
},
"type_url": "type.googleapis.com/protocol.TriggerSmartContract"
},
"type": "TriggerSmartContract"
}],
"ref_block_bytes": "5ab1",
"ref_block_hash": "f24f075df912f43e",
"expiration": 1590382815000,
"timestamp": 1590382762536
},
"raw_data_hex": "0a025ab12208f24f075df912f43e4098cecfd1a42e5a8e01081f1289010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412540a1541977c20977f412c2a1aa4ef3d49fee5ec4c31cdfb1215419e62be7f4f103c36507cb2a753418791b1cdc182222470a08231000000000000000000000041977c20977f412c2a1aa4ef3d49fee5ec4c31cdfb70a8b4ccd1a42e"
}
}constant_result:就是查询合约的结果,本例中返回值是uint256类型,具体如何对返回值进行编码和解码,请参考参数编码解码章节
用data调用合约
除了直接输入函数选择器与参数,还可将二者通过ABI规范编码为十六进制数据,拼接后作为data字段提交。
示例:
在一个智能合约上调用标准的 TRC-20 transfer 函数。函数签名通常是 transfer(address,uint256)。
转移 100 个代币到地址 TV3nb5HYFe2xBEmyb3ETe93UGkjAhWyzrs。
- 函数签名:
transfer(address,uint256) - 函数选择器: 对
transfer(address,uint256)进行 Keccak-256 哈希计算,取前 4 个字节。对于这个函数,通常是a9059cbb。 - 参数编码:
- 地址
TV3nb5HYFe2xBEmyb3ETe93UGkjAhWyzrs需要被编码为一个 32 字节的十六进制值。 uint256值100需要被编码为一个 32 字节的十六进制值。
- 地址
最终的 data 字段就是函数选择器和编码参数的拼接。
一个调用 transfer(address,uint256) 发送 100 个代币到 TV3nb5HYFe2xBEmyb3ETe93UGkjAhWyzrs 的典型 data 值可能看起来像这样(这是一个简化的例子,实际编码遵循特定的 ABI 规则):
a9059cbb00000000000000000000000041a614f89f8a93d7f8824f607c7b2a3c3e0d5e190000000000000000000000000000000000000000000000000000000000000064
在这个例子中:
a9059cbb是transfer(address,uint256)的函数选择器。00000000000000000000000041a614f89f8a93d7f8824f607c7b2a3c3e0d5e19是接收者地址TV3nb5HYFe2xBEmyb3ETe93UGkjAhWyzrs的编码(十六进制表示,并填充到 32 字节)。0000000000000000000000000000000000000000000000000000000000000064是值100的编码(十六进制表示为64,并填充到 32 字节)。
完整调用:
curl --request POST \
--url https://api.shasta.trongrid.io/wallet/triggersmartcontract \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"owner_address": "TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g",
"contract_address": "TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs",
"data": "a9059cbb00000000000000000000000041a614f89f8a93d7f8824f607c7b2a3c3e0d5e190000000000000000000000000000000000000000000000000000000000000064",
"fee_limit": 1000000000,
"call_value": 0,
"visible": true
}
'结果:
{
"result": {
"result": true
},
"transaction": {
"visible": true,
"txID": "e33c025b615a5125c8a28ec7e012a8b880c5d855f010c07dca45cb2d40609b82",
"raw_data": {
"contract": [{
"parameter": {
"value": {
"data": "a9059cbb00000000000000000000000041a614f89f8a93d7f8824f607c7b2a3c3e0d5e190000000000000000000000000000000000000000000000000000000000000064",
"owner_address": "TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g",
"contract_address": "TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs"
},
"type_url": "type.googleapis.com/protocol.TriggerSmartContract"
},
"type": "TriggerSmartContract"
}],
"ref_block_bytes": "8e6a",
"ref_block_hash": "9858ac3fec3262ff",
"expiration": 1747130115000,
"fee_limit": 1000000000,
"timestamp": 1747130056923
},
"raw_data_hex": "0a028e6a22089858ac3fec3262ff40b8c7c7c8ec325aae01081f12a9010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412740a1541fd49eda0f23ff7ec1d03b52c3a45991c24cd440e12154142a1e39aefa49290f2b3f9ed688d7cecf86cd6e02244a9059cbb00000000000000000000000041a614f89f8a93d7f8824f607c7b2a3c3e0d5e19000000000000000000000000000000000000000000000000000000000000006470db81c4c8ec3290018094ebdc03"
}
}consume_user_resource_percent
合约调用需要支付一定的资源费用,为了鼓励用户进行合约交易,降低其调用成本,TRON网络支持合约部署者分摊一部分的合约调用费用。在部署合约时,可通过参数consume_user_resource_percent来设置用户的能量支付比率。
用户能量支付比率是用户为智能合约执行支付的能量的比例。 例如,如果用户能量支付比率设置为60,则用户支付60%的合约执行所需的能量,而开发者(合同部署者)支付剩余的40%的能量。 此参数只能为0到100之间(含0和100)的整数。 建议开发者适当调高用户能量支付比例,以防用户攻击合约并耗尽合约所有者的帐户资源。
合约成功部署后,也可以修改用户能量支付比率,比如合约拥有者可以通过fullnode HTTP API wallet/updatesetting 来修改合约的用户能量支付比率。
注意:虽然合约开发者可能需要承担一定比例的合约调用所需的能量费用,但是当开发者账户的能量不足以支付他所承担的部分时或本次调用消耗开发者账户的能量超过了origin_energy_limit时,剩余部分全部由调用者承担。更多内容请参考能量消耗机制章节。
feelimit
feelimit是指调用者愿意承担的智能合约部署或调用所消耗的能量成本的上限,以sun为单位 (1TRX = 1e6 sun),目前可设置的能量费用上限为15000 TRX,如果将feelimit设置为大于 15000TRX,则会产生错误。
由于执行合约时,Energy的计算和扣除是逐条进行的,一但能量用量超出最大值,则合约执行失败,已扣除的Energy无法退还;
在部署合约到主网之前,最好设置一个合理的费用限制。比如在部署大型合约或运行复杂功能时,需要设置更大的feelimit,但是,由于存在合约执行超时、合约中存在无限循环、非法操作和向不存在的帐户转账等情况,设置相对较低的费用限制将是更好的选择。
- 有关 feelimit 的更详细介绍,请参阅 FeeLimit 。
- 对于如何为交易设置 feelimit 的操作指南,请参阅 feeLimit 参数设置。
Updated 5 days ago