generated from open-ibc/ibc-app-solidity-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CustomChanIbcApp.sol
205 lines (180 loc) · 8.09 KB
/
CustomChanIbcApp.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
import '@open-ibc/vibc-core-smart-contracts/contracts/libs/Ibc.sol';
import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcReceiver.sol';
import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcDispatcher.sol';
import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/ProofVerifier.sol';
// CustomChanIbcApp is a contract that can be used as a base contract
// for IBC-enabled contracts that send packets over a custom IBC channel.
contract CustomChanIbcApp is IbcReceiverBase, IbcReceiver {
// received packet as chain B
IbcPacket[] public recvedPackets;
// received ack packet as chain A
AckPacket[] public ackPackets;
// received timeout packet as chain A
IbcPacket[] public timeoutPackets;
struct ChannelMapping {
bytes32 channelId;
bytes32 cpChannelId;
}
// ChannelMapping array with the channel IDs of the connected channels
ChannelMapping[] public connectedChannels;
// add supported versions (format to be negotiated between apps)
string[] supportedVersions = ['1.0'];
constructor(IbcDispatcher _dispatcher) IbcReceiverBase(_dispatcher) {}
function updateDispatcher(IbcDispatcher _dispatcher) external onlyOwner {
dispatcher = _dispatcher;
}
function getConnectedChannels() external view returns (ChannelMapping[] memory) {
return connectedChannels;
}
function updateSupportedVersions(string[] memory _supportedVersions) external onlyOwner {
supportedVersions = _supportedVersions;
}
/**
* @dev Implement a function to send a packet that calls the dispatcher.sendPacket function
* It has the following function handle:
* function sendPacket(bytes32 channelId, bytes calldata payload, uint64 timeoutTimestamp) external;
*/
/**
* @dev Packet lifecycle callback that implements packet receipt logic and returns and acknowledgement packet.
* MUST be overriden by the inheriting contract.
*
* @param packet the IBC packet encoded by the source and relayed by the relayer.
*/
function onRecvPacket(IbcPacket memory packet) external virtual onlyIbcDispatcher returns (AckPacket memory ackPacket) {
recvedPackets.push(packet);
// do logic
return AckPacket(true, abi.encodePacked('{ "account": "account", "reply": "got the message" }'));
}
/**
* @dev Packet lifecycle callback that implements packet acknowledgment logic.
* MUST be overriden by the inheriting contract.
*
* @param packet the IBC packet encoded by the source and relayed by the relayer.
* @param ack the acknowledgment packet encoded by the destination and relayed by the relayer.
*/
function onAcknowledgementPacket(IbcPacket calldata packet, AckPacket calldata ack) external virtual onlyIbcDispatcher {
ackPackets.push(ack);
// do logic
}
/**
* @dev Packet lifecycle callback that implements packet receipt logic and return and acknowledgement packet.
* MUST be overriden by the inheriting contract.
* NOT SUPPORTED YET
*
* @param packet the IBC packet encoded by the counterparty and relayed by the relayer
*/
function onTimeoutPacket(IbcPacket calldata packet) external virtual onlyIbcDispatcher {
timeoutPackets.push(packet);
// do logic
}
/**
* @dev Create a custom channel between two IbcReceiver contracts
* @param local a CounterParty struct with the local chain's portId and version (channelId can be empty)
* @param ordering the channel ordering (NONE, UNORDERED, ORDERED) equivalent to (0, 1, 2)
* @param feeEnabled in production, you'll want to enable this to avoid spamming create channel calls (costly for relayers)
* @param connectionHops 2 connection hops to connect to the destination via Polymer
* @param counterparty the address of the destination chain contract you want to connect to
* @param proof ICS23 proof struct with dummy data (only needed on ChanOpenTry)
*/
function createChannel(
CounterParty calldata local,
uint8 ordering,
bool feeEnabled,
string[] calldata connectionHops,
CounterParty calldata counterparty,
Ics23Proof calldata proof
) external virtual onlyOwner{
dispatcher.openIbcChannel(
IbcChannelReceiver(address(this)),
local,
ChannelOrder(ordering),
feeEnabled,
connectionHops,
counterparty,
proof
);
}
function onOpenIbcChannel(
string calldata version,
ChannelOrder,
bool,
string[] calldata,
CounterParty calldata counterparty
) external view virtual onlyIbcDispatcher returns (string memory selectedVersion) {
if (bytes(counterparty.portId).length <= 8) {
revert invalidCounterPartyPortId();
}
/**
* Version selection is determined by if the callback is invoked on behalf of ChanOpenInit or ChanOpenTry.
* ChanOpenInit: self version should be provided whereas the counterparty version is empty.
* ChanOpenTry: counterparty version should be provided whereas the self version is empty.
* In both cases, the selected version should be in the supported versions list.
*/
bool foundVersion = false;
selectedVersion = keccak256(abi.encodePacked(version)) == keccak256(abi.encodePacked(''))
? counterparty.version
: version;
for (uint256 i = 0; i < supportedVersions.length; i++) {
if (keccak256(abi.encodePacked(selectedVersion)) == keccak256(abi.encodePacked(supportedVersions[i]))) {
foundVersion = true;
break;
}
}
require(foundVersion, 'Unsupported version');
// if counterpartyVersion is not empty, then it must be the same foundVersion
if (keccak256(abi.encodePacked(counterparty.version)) != keccak256(abi.encodePacked(''))) {
require(
keccak256(abi.encodePacked(counterparty.version)) == keccak256(abi.encodePacked(selectedVersion)),
'Version mismatch'
);
}
// do logic
return selectedVersion;
}
function onConnectIbcChannel(
bytes32 channelId,
bytes32 counterpartyChannelId,
string calldata counterpartyVersion
) external virtual onlyIbcDispatcher {
// ensure negotiated version is supported
bool foundVersion = false;
for (uint256 i = 0; i < supportedVersions.length; i++) {
if (keccak256(abi.encodePacked(counterpartyVersion)) == keccak256(abi.encodePacked(supportedVersions[i]))) {
foundVersion = true;
break;
}
}
require(foundVersion, 'Unsupported version');
// do logic
ChannelMapping memory channelMapping = ChannelMapping({
channelId: channelId,
cpChannelId: counterpartyChannelId
});
connectedChannels.push(channelMapping);
}
function onCloseIbcChannel(bytes32 channelId, string calldata, bytes32) external virtual onlyIbcDispatcher {
// logic to determin if the channel should be closed
bool channelFound = false;
for (uint256 i = 0; i < connectedChannels.length; i++) {
if (connectedChannels[i].channelId == channelId) {
for (uint256 j = i; j < connectedChannels.length - 1; j++) {
connectedChannels[j] = connectedChannels[j + 1];
}
connectedChannels.pop();
channelFound = true;
break;
}
}
require(channelFound, 'Channel not found');
// do logic
}
/**
* This func triggers channel closure from the dApp.
* Func args can be arbitary, as long as dispatcher.closeIbcChannel is invoked propperly.
*/
function triggerChannelClose(bytes32 channelId) external virtual onlyOwner {
dispatcher.closeIbcChannel(channelId);
}
}