本文转载自微信公众号 PlatON Cross
原创作者: PlatON Cross小编
在《PlatON上的WASM智能合约开发(五)——进阶(代理机制)》中向大家讲解了智能合约的代理机制,代理机制的使用必然会涉及到智能合约之间的跨合约调用(详见《PlatON上的WASM智能合约开发(二)——跨合约调用》)。
而事件是DApp进行链上合约、链下client交互的一种常见机制,在代理机制的使用过程中,交易型(PlatON WASM合约中通过ACTION修饰的方法)合约请求通常会涉及到业务合约事件的捕获与解析。当调用请求通过代理合约完成时,事件一般由实际的业务合约抛出。
本文将通过相关案例,展示在合约代理机制的使用中,对合约事件的捕获解析,以及跨合约调用中对自定义参数的传递。PlatON在相关client SDK中提供了事件的解析机制,有关简单合约事件机制的讲解详见《PlatON上的WASM智能合约开发(三)——事件机制》。
▌合约文件
● dataStruct.h
#pragma once
#include <platon/platon.hpp>
#include <string>
struct MyStruct{
int i;
std::string s;
PLATON_SERIALIZE(MyStruct, (i)(s))
MyStruct(){
i = 0;
s = “”;
}
};
说明:
platon wasm合约底层提供了序列化机制,通过PLATON_SERIALIZE修饰的自定义数据结构可以在跨合约调用时作为参数进行传递。
● eventCalled.cpp
#include <platon/platon.hpp>
#include <string>
#include “dataStruct.h”
class eventCalled: public platon::Contract{
public:
PLATON_EVENT1(outCaller, std::string, int)
ACTION void init(){}
ACTION int outCall(MyStruct& ms){
PLATON_EMIT_EVENT1(outCaller, “outside”, ms.i);
return 99;
}
};
PLATON_DISPATCH(eventCalled, (init)(outCall))
说明:
1)实际完成业务操作的合约,outCall方法以自定义数据结构MyStruct作为参数;
2)在ACTION方法outCall执行时,会通过“outCaller”事件向client传递执行结果。
● outCaller.cpp
#include “dataStruct.h”
#include <platon/platon.hpp>
#include <string>
class outCaller : public platon::Contract{
public:
ACTION void init(std::string& contractAddr){
auto rst = platon::make_address(contractAddr);
if (!rst.second){
//when the contractAddr is illegal, the proxy can not be used
platon::internal::platon_throw(“deploy failed!”);
}
else{
_contractAddr.self() = rst;
}
}
ACTION void setContract(std::string& contractAddr){
auto rst = platon::make_address(contractAddr);
if (!rst.second){
//when the contractAddr is illegal, the proxy can not be used
platon::internal::platon_throw(“deploy failed!”);
}
else{
_contractAddr.self() = rst;
}
}
CONST std::string getContract(){
return _contractAddr.self().first.toString();
}
ACTION int Call(){
if (!(_contractAddr.self().second))
{
platon::internal::platon_throw(“this contract init failed!”);
return -1;
}
auto ms = MyStruct();
auto result = platon::platon_call_with_return_value<int>(_contractAddr.self().first, (unsigned int)(0), (unsigned int)(0), “outCall”, ms);
return result.first;
}
platon::StorageType<“contract”_n, std::pair<platon::Address, bool>> _contractAddr;
};
PLATON_DISPATCH(outCaller, (init)(setContract)(getContract)(Call))
说明:
1)该合约具备代理合约的部分功能,client通过调用Call接口,实际访问到业务合约的eventCalled::outCall方法;
2)合约调用时,将传递自定义数据结构MyStruct。
▌合约调用
合约调用基于client-sdk-python实现,若在案例测试使用过程中遇到任何问题,可通过 PlatON Cross 公众号留言联系技术团队,会第一时间获取答复。
true = True
false = False
#调用需要提供开发网账户
from_address = ‘…’
eventCalledAddr = ‘lat1npffkce3elp0ug80r85rz36y9gjkgevnculnyn’
eventCalled_abi = [{“anonymous”:false,”input”:[{“name”:”topic”,”type”:”string”},{“name”:”arg1″,”type”:”int32″}],”name”:”outCaller”,”topic”:1,”type”:”Event”},{“constant”:false,”input”:[],”name”:”init”,”output”:”void”,”type”:”Action”},{“baseclass”:[],”fields”:[{“name”:”i”,”type”:”int32″},{“name”:”s”,”type”:”string”}],”name”:”MyStruct”,”type”:”struct”},{“constant”:false,”input”:[{“name”:”ms”,”type”:”MyStruct”}],”name”:”outCall”,”output”:”int32″,”type”:”Action”}]
outCallerAddr = ‘lat1wtmjnquh4y08mpvrn0zd4c7hhyy0g9qpz58q8a’
outCaller_abi = [{“constant”:false,”input”:[{“name”:”contractAddr”,”type”:”string”}],”name”:”init”,”output”:”void”,”type”:”Action”},{“constant”:false,”input”:[],”name”:”Call”,”output”:”int32″,”type”:”Action”},{“constant”:false,”input”:[{“name”:”contractAddr”,”type”:”string”}],”name”:”setContract”,”output”:”void”,”type”:”Action”},{“constant”:true,”input”:[],”name”:”getContract”,”output”:”string”,”type”:”Action”}]
def testEventWasm():
w3 = Web3(HTTPProvider(“http://127.0.0.1:6789”))
platon = PlatON(w3)
eventCalled = platon.wasmcontract(address=eventCalledAddr, abi=eventCalled_abi,vmtype=1)
outCaller = platon.wasmcontract(address=outCallerAddr, abi=outCaller_abi,vmtype=1)
#调用outCaller::Call()
tx_hash = outCaller.functions.Call().transact({‘from’:from_address,’gas’:1500000})
tx_events_receipt = platon.waitForTransactionReceipt(tx_hash)
#这里需要使用实际emit事件的合约获取events
topic_param = eventCalled.events.outCaller().processReceipt(tx_events_receipt)
print(topic_param)
说明:
1)调用需要使用outCaller、eventCalled两个合约的abi;
2)首先通过outCaller的abi调用outCaller::Call()方法;
3)然后等待捕获eventCalled的“outCaller”事件,通过eventCalled的abi解析事件内容;
4)调用需要连接开发网节点。
结果:
说明:
python SDK会将事件结果解析成一个AtrributeDict结构,其中’arg1′: 0在本案例中作为用户关心的处理结果返回。
▌总结
跨合约调用的事件机制比较简单,唯一需要注意的是在对事件进行解析时,需要使用实际抛出事件的合约abi才能正常解析。PlatON WASM合约对于需要在合约间调用时传递的参数,底层提供了序列化支持。
本文转载自https://mp.weixin.qq.com/s/_TH8ioZ_8JFfkYR6nE1Sig