-
Notifications
You must be signed in to change notification settings - Fork 857
/
CustomChanIbcApp.sol
204 lines (178 loc) · 8.04 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
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
import {IbcPacket, AckPacket, ChannelOrder} from "@open-ibc/vibc-core-smart-contracts/contracts/libs/Ibc.sol";
import {FeeSender} from "@open-ibc/vibc-core-smart-contracts/contracts/implementation_templates/FeeSender.sol";
import {IBCErrors} from "@open-ibc/vibc-core-smart-contracts/contracts/libs/IbcErrors.sol";
import {IbcReceiverBase, IbcReceiver} from "@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcReceiver.sol";
import {IbcDispatcher} from "@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcDispatcher.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, FeeSender {
struct ChannelMapping {
bytes32 channelId;
bytes32 cpChannelId;
}
// 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;
// ChannelMapping array with the channel IDs of the connected channels
ChannelMapping[] public connectedChannels;
// add supported versions (format to be negotiated between apps)
string[] public supportedVersions = ["1.0"];
constructor(IbcDispatcher _dispatcher) IbcReceiverBase(_dispatcher) {}
function updateDispatcher(IbcDispatcher _dispatcher) external onlyOwner {
dispatcher = _dispatcher;
}
function updateSupportedVersions(string[] memory _supportedVersions) external onlyOwner {
supportedVersions = _supportedVersions;
}
function getConnectedChannels() external view returns (ChannelMapping[] memory) {
return connectedChannels;
}
/**
* @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, bool skipAck)
{
recvedPackets.push(packet);
// do logic
// solhint-disable-next-line quotes
return (AckPacket(true, abi.encodePacked("{ 'account': 'account', 'reply': 'got the message' }")), false);
}
/**
* @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 version a version string to negotiate between the two chains
* @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 counterpartyPortId the portID of the destination chain contract you want to connect to
*/
function createChannel(
string calldata version,
uint8 ordering,
bool feeEnabled,
string[] calldata connectionHops,
string calldata counterpartyPortId
) external onlyOwner {
dispatcher.channelOpenInit(version, ChannelOrder(ordering), feeEnabled, connectionHops, counterpartyPortId);
}
// solhint-disable-next-line ordering
function onChanOpenTry(
ChannelOrder,
string[] memory,
bytes32 channelId,
string memory,
bytes32 counterpartyChannelId,
string calldata counterpartyVersion
) external virtual onlyIbcDispatcher returns (string memory selectedVersion) {
return _connectChannel(channelId, counterpartyChannelId, counterpartyVersion);
}
function onChanOpenAck(bytes32 channelId, bytes32 counterpartyChannelId, string calldata counterpartyVersion)
external
virtual
onlyIbcDispatcher
{
_connectChannel(channelId, counterpartyChannelId, counterpartyVersion);
}
function onChanOpenConfirm(bytes32 channelId) external virtual onlyIbcDispatcher {
// do logic.
}
/**
* @notice Handles channel close callback on the dest chain
* @param channelId The unique identifier of the channel
* @dev Make sure to validate channelId and counterpartyVersion
*/
function onChanCloseConfirm(bytes32 channelId, string calldata, bytes32) external virtual onlyIbcDispatcher {
// logic to determine if the channel should be closed
bool channelFound = false;
for (uint256 i = 0; i < connectedChannels.length; i++) {
if (connectedChannels[i].channelId == channelId) {
delete connectedChannels[i];
channelFound = true;
break;
}
}
if (!channelFound) revert ChannelNotFound();
}
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) {
delete connectedChannels[i];
channelFound = true;
break;
}
}
if (!channelFound) revert ChannelNotFound();
}
function _connectChannel(bytes32 channelId, bytes32 counterpartyChannelId, string calldata counterpartyVersion)
internal
returns (string memory version)
{
// ensure negotiated version is supported
for (uint256 i = 0; i < supportedVersions.length; i++) {
if (keccak256(abi.encodePacked(counterpartyVersion)) == keccak256(abi.encodePacked(supportedVersions[i]))) {
ChannelMapping memory channelMapping =
ChannelMapping({channelId: channelId, cpChannelId: counterpartyChannelId});
connectedChannels.push(channelMapping);
return counterpartyVersion;
}
}
revert UnsupportedVersion();
}
function _openChannel(string calldata version) private view returns (string memory selectedVersion) {
for (uint256 i = 0; i < supportedVersions.length; i++) {
if (keccak256(abi.encodePacked(version)) == keccak256(abi.encodePacked(supportedVersions[i]))) {
return version;
}
}
revert UnsupportedVersion();
}
/**
* This func triggers channel closure from the dApp.
* Func args can be arbitary, as long as dispatcher.triggerChannelClose is invoked propperly.
*/
function triggerChannelClose(bytes32 channelId) external onlyOwner {
dispatcher.channelCloseInit(channelId);
}
}