powerdlz23 1.1.5 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/polymer/polymer-template/.env.example +27 -0
- package/polymer/polymer-template/.gitmodules +6 -0
- package/polymer/polymer-template/.gitpod.yml +10 -0
- package/polymer/polymer-template/Justfile +97 -0
- package/polymer/polymer-template/README.md +312 -0
- package/polymer/polymer-template/config/alt-config.json +42 -0
- package/polymer/polymer-template/config.json +42 -0
- package/polymer/polymer-template/contracts/XCounter.sol +89 -0
- package/polymer/polymer-template/contracts/XCounterUC.sol +100 -0
- package/polymer/polymer-template/contracts/arguments.js +7 -0
- package/polymer/polymer-template/contracts/base/CustomChanIbcApp.sol +205 -0
- package/polymer/polymer-template/contracts/base/GeneralMiddleware.sol +200 -0
- package/polymer/polymer-template/contracts/base/UniversalChanIbcApp.sol +93 -0
- package/polymer/polymer-template/foundry.toml +6 -0
- package/polymer/polymer-template/hardhat.config.js +66 -0
- package/polymer/polymer-template/ibc.json +26 -0
- package/polymer/polymer-template/img/gh_template.png +0 -0
- package/polymer/polymer-template/package-lock.json +7672 -0
- package/polymer/polymer-template/package.json +34 -0
- package/polymer/polymer-template/remappings.txt +5 -0
- package/polymer/polymer-template/scripts/deploy.js +51 -0
- package/polymer/polymer-template/scripts/private/_create-channel-config.js +62 -0
- package/polymer/polymer-template/scripts/private/_create-channel.js +96 -0
- package/polymer/polymer-template/scripts/private/_deploy-config.js +62 -0
- package/polymer/polymer-template/scripts/private/_events.js +241 -0
- package/polymer/polymer-template/scripts/private/_helpers.js +113 -0
- package/polymer/polymer-template/scripts/private/_sanity-check-custom.js +69 -0
- package/polymer/polymer-template/scripts/private/_sanity-check-universal.js +120 -0
- package/polymer/polymer-template/scripts/private/_sanity-check.js +21 -0
- package/polymer/polymer-template/scripts/private/_send-packet-config.js +53 -0
- package/polymer/polymer-template/scripts/private/_set-contracts-config.js +50 -0
- package/polymer/polymer-template/scripts/private/_switch-clients.js +90 -0
- package/polymer/polymer-template/scripts/private/_update-vibc-address.js +52 -0
- package/polymer/polymer-template/scripts/private/_vibc-helpers.js +118 -0
- package/polymer/polymer-template/scripts/send-packet.js +38 -0
- package/polymer/polymer-template/scripts/send-universal-packet.js +44 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
//SPDX-License-Identifier: UNLICENSED
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.9;
|
|
4
|
+
|
|
5
|
+
import "./base/UniversalChanIbcApp.sol";
|
|
6
|
+
|
|
7
|
+
contract XCounterUC is UniversalChanIbcApp {
|
|
8
|
+
// application specific state
|
|
9
|
+
uint64 public counter;
|
|
10
|
+
mapping(uint64 => address) public counterMap;
|
|
11
|
+
|
|
12
|
+
constructor(address _middleware) UniversalChanIbcApp(_middleware) {}
|
|
13
|
+
|
|
14
|
+
// application specific logic
|
|
15
|
+
function resetCounter() internal {
|
|
16
|
+
counter = 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function increment() internal {
|
|
20
|
+
counter++;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// IBC logic
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @dev Sends a packet with the caller's address over the universal channel.
|
|
27
|
+
* @param destPortAddr The address of the destination application.
|
|
28
|
+
* @param channelId The ID of the channel to send the packet to.
|
|
29
|
+
* @param timeoutSeconds The timeout in seconds (relative).
|
|
30
|
+
*/
|
|
31
|
+
function sendUniversalPacket(address destPortAddr, bytes32 channelId, uint64 timeoutSeconds) external {
|
|
32
|
+
increment();
|
|
33
|
+
bytes memory payload = abi.encode(msg.sender, counter);
|
|
34
|
+
|
|
35
|
+
uint64 timeoutTimestamp = uint64((block.timestamp + timeoutSeconds) * 1000000000);
|
|
36
|
+
|
|
37
|
+
IbcUniversalPacketSender(mw).sendUniversalPacket(
|
|
38
|
+
channelId, IbcUtils.toBytes32(destPortAddr), payload, timeoutTimestamp
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @dev Packet lifecycle callback that implements packet receipt logic and returns and acknowledgement packet.
|
|
44
|
+
* MUST be overriden by the inheriting contract.
|
|
45
|
+
*
|
|
46
|
+
* @param channelId the ID of the channel (locally) the packet was received on.
|
|
47
|
+
* @param packet the Universal packet encoded by the source and relayed by the relayer.
|
|
48
|
+
*/
|
|
49
|
+
function onRecvUniversalPacket(bytes32 channelId, UniversalPacket calldata packet)
|
|
50
|
+
external
|
|
51
|
+
override
|
|
52
|
+
onlyIbcMw
|
|
53
|
+
returns (AckPacket memory ackPacket)
|
|
54
|
+
{
|
|
55
|
+
recvedPackets.push(UcPacketWithChannel(channelId, packet));
|
|
56
|
+
|
|
57
|
+
(address payload, uint64 c) = abi.decode(packet.appData, (address, uint64));
|
|
58
|
+
counterMap[c] = payload;
|
|
59
|
+
|
|
60
|
+
increment();
|
|
61
|
+
|
|
62
|
+
return AckPacket(true, abi.encode(counter));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @dev Packet lifecycle callback that implements packet acknowledgment logic.
|
|
67
|
+
* MUST be overriden by the inheriting contract.
|
|
68
|
+
*
|
|
69
|
+
* @param channelId the ID of the channel (locally) the ack was received on.
|
|
70
|
+
* @param packet the Universal packet encoded by the source and relayed by the relayer.
|
|
71
|
+
* @param ack the acknowledgment packet encoded by the destination and relayed by the relayer.
|
|
72
|
+
*/
|
|
73
|
+
function onUniversalAcknowledgement(bytes32 channelId, UniversalPacket memory packet, AckPacket calldata ack)
|
|
74
|
+
external
|
|
75
|
+
override
|
|
76
|
+
onlyIbcMw
|
|
77
|
+
{
|
|
78
|
+
ackPackets.push(UcAckWithChannel(channelId, packet, ack));
|
|
79
|
+
|
|
80
|
+
// decode the counter from the ack packet
|
|
81
|
+
(uint64 _counter) = abi.decode(ack.data, (uint64));
|
|
82
|
+
|
|
83
|
+
if (_counter != counter) {
|
|
84
|
+
resetCounter();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @dev Packet lifecycle callback that implements packet receipt logic and return and acknowledgement packet.
|
|
90
|
+
* MUST be overriden by the inheriting contract.
|
|
91
|
+
* NOT SUPPORTED YET
|
|
92
|
+
*
|
|
93
|
+
* @param channelId the ID of the channel (locally) the timeout was submitted on.
|
|
94
|
+
* @param packet the Universal packet encoded by the counterparty and relayed by the relayer
|
|
95
|
+
*/
|
|
96
|
+
function onTimeoutUniversalPacket(bytes32 channelId, UniversalPacket calldata packet) external override onlyIbcMw {
|
|
97
|
+
timeoutPackets.push(UcPacketWithChannel(channelId, packet));
|
|
98
|
+
// do logic
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
"XCounter": [],
|
|
3
|
+
"XCounterUC": [],
|
|
4
|
+
// Add your contract types here, along with the list of custom constructor arguments
|
|
5
|
+
// DO NOT ADD THE DISPATCHER OR UNIVERSAL CHANNEL HANDLER ADDRESSES HERE!!!
|
|
6
|
+
// These will be added in the deploy script at $ROOT/scripts/deploy.js
|
|
7
|
+
};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
//SPDX-License-Identifier: UNLICENSED
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.9;
|
|
4
|
+
|
|
5
|
+
import '@open-ibc/vibc-core-smart-contracts/contracts/libs/Ibc.sol';
|
|
6
|
+
import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcReceiver.sol';
|
|
7
|
+
import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcDispatcher.sol';
|
|
8
|
+
import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/ProofVerifier.sol';
|
|
9
|
+
|
|
10
|
+
// CustomChanIbcApp is a contract that can be used as a base contract
|
|
11
|
+
// for IBC-enabled contracts that send packets over a custom IBC channel.
|
|
12
|
+
contract CustomChanIbcApp is IbcReceiverBase, IbcReceiver {
|
|
13
|
+
// received packet as chain B
|
|
14
|
+
IbcPacket[] public recvedPackets;
|
|
15
|
+
// received ack packet as chain A
|
|
16
|
+
AckPacket[] public ackPackets;
|
|
17
|
+
// received timeout packet as chain A
|
|
18
|
+
IbcPacket[] public timeoutPackets;
|
|
19
|
+
|
|
20
|
+
struct ChannelMapping {
|
|
21
|
+
bytes32 channelId;
|
|
22
|
+
bytes32 cpChannelId;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ChannelMapping array with the channel IDs of the connected channels
|
|
26
|
+
ChannelMapping[] public connectedChannels;
|
|
27
|
+
|
|
28
|
+
// add supported versions (format to be negotiated between apps)
|
|
29
|
+
string[] supportedVersions = ['1.0'];
|
|
30
|
+
|
|
31
|
+
constructor(IbcDispatcher _dispatcher) IbcReceiverBase(_dispatcher) {}
|
|
32
|
+
|
|
33
|
+
function updateDispatcher(IbcDispatcher _dispatcher) external onlyOwner {
|
|
34
|
+
dispatcher = _dispatcher;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getConnectedChannels() external view returns (ChannelMapping[] memory) {
|
|
38
|
+
return connectedChannels;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function updateSupportedVersions(string[] memory _supportedVersions) external onlyOwner {
|
|
42
|
+
supportedVersions = _supportedVersions;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @dev Implement a function to send a packet that calls the dispatcher.sendPacket function
|
|
47
|
+
* It has the following function handle:
|
|
48
|
+
* function sendPacket(bytes32 channelId, bytes calldata payload, uint64 timeoutTimestamp) external;
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @dev Packet lifecycle callback that implements packet receipt logic and returns and acknowledgement packet.
|
|
53
|
+
* MUST be overriden by the inheriting contract.
|
|
54
|
+
*
|
|
55
|
+
* @param packet the IBC packet encoded by the source and relayed by the relayer.
|
|
56
|
+
*/
|
|
57
|
+
function onRecvPacket(IbcPacket memory packet) external virtual onlyIbcDispatcher returns (AckPacket memory ackPacket) {
|
|
58
|
+
recvedPackets.push(packet);
|
|
59
|
+
// do logic
|
|
60
|
+
return AckPacket(true, abi.encodePacked('{ "account": "account", "reply": "got the message" }'));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @dev Packet lifecycle callback that implements packet acknowledgment logic.
|
|
65
|
+
* MUST be overriden by the inheriting contract.
|
|
66
|
+
*
|
|
67
|
+
* @param packet the IBC packet encoded by the source and relayed by the relayer.
|
|
68
|
+
* @param ack the acknowledgment packet encoded by the destination and relayed by the relayer.
|
|
69
|
+
*/
|
|
70
|
+
function onAcknowledgementPacket(IbcPacket calldata packet, AckPacket calldata ack) external virtual onlyIbcDispatcher {
|
|
71
|
+
ackPackets.push(ack);
|
|
72
|
+
// do logic
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @dev Packet lifecycle callback that implements packet receipt logic and return and acknowledgement packet.
|
|
77
|
+
* MUST be overriden by the inheriting contract.
|
|
78
|
+
* NOT SUPPORTED YET
|
|
79
|
+
*
|
|
80
|
+
* @param packet the IBC packet encoded by the counterparty and relayed by the relayer
|
|
81
|
+
*/
|
|
82
|
+
function onTimeoutPacket(IbcPacket calldata packet) external virtual onlyIbcDispatcher {
|
|
83
|
+
timeoutPackets.push(packet);
|
|
84
|
+
// do logic
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @dev Create a custom channel between two IbcReceiver contracts
|
|
89
|
+
* @param local a CounterParty struct with the local chain's portId and version (channelId can be empty)
|
|
90
|
+
* @param ordering the channel ordering (NONE, UNORDERED, ORDERED) equivalent to (0, 1, 2)
|
|
91
|
+
* @param feeEnabled in production, you'll want to enable this to avoid spamming create channel calls (costly for relayers)
|
|
92
|
+
* @param connectionHops 2 connection hops to connect to the destination via Polymer
|
|
93
|
+
* @param counterparty the address of the destination chain contract you want to connect to
|
|
94
|
+
* @param proof ICS23 proof struct with dummy data (only needed on ChanOpenTry)
|
|
95
|
+
*/
|
|
96
|
+
function createChannel(
|
|
97
|
+
CounterParty calldata local,
|
|
98
|
+
uint8 ordering,
|
|
99
|
+
bool feeEnabled,
|
|
100
|
+
string[] calldata connectionHops,
|
|
101
|
+
CounterParty calldata counterparty,
|
|
102
|
+
Ics23Proof calldata proof
|
|
103
|
+
) external virtual onlyOwner{
|
|
104
|
+
|
|
105
|
+
dispatcher.openIbcChannel(
|
|
106
|
+
IbcChannelReceiver(address(this)),
|
|
107
|
+
local,
|
|
108
|
+
ChannelOrder(ordering),
|
|
109
|
+
feeEnabled,
|
|
110
|
+
connectionHops,
|
|
111
|
+
counterparty,
|
|
112
|
+
proof
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function onOpenIbcChannel(
|
|
117
|
+
string calldata version,
|
|
118
|
+
ChannelOrder,
|
|
119
|
+
bool,
|
|
120
|
+
string[] calldata,
|
|
121
|
+
CounterParty calldata counterparty
|
|
122
|
+
) external view virtual onlyIbcDispatcher returns (string memory selectedVersion) {
|
|
123
|
+
if (bytes(counterparty.portId).length <= 8) {
|
|
124
|
+
revert invalidCounterPartyPortId();
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Version selection is determined by if the callback is invoked on behalf of ChanOpenInit or ChanOpenTry.
|
|
128
|
+
* ChanOpenInit: self version should be provided whereas the counterparty version is empty.
|
|
129
|
+
* ChanOpenTry: counterparty version should be provided whereas the self version is empty.
|
|
130
|
+
* In both cases, the selected version should be in the supported versions list.
|
|
131
|
+
*/
|
|
132
|
+
bool foundVersion = false;
|
|
133
|
+
selectedVersion = keccak256(abi.encodePacked(version)) == keccak256(abi.encodePacked(''))
|
|
134
|
+
? counterparty.version
|
|
135
|
+
: version;
|
|
136
|
+
for (uint256 i = 0; i < supportedVersions.length; i++) {
|
|
137
|
+
if (keccak256(abi.encodePacked(selectedVersion)) == keccak256(abi.encodePacked(supportedVersions[i]))) {
|
|
138
|
+
foundVersion = true;
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
require(foundVersion, 'Unsupported version');
|
|
143
|
+
// if counterpartyVersion is not empty, then it must be the same foundVersion
|
|
144
|
+
if (keccak256(abi.encodePacked(counterparty.version)) != keccak256(abi.encodePacked(''))) {
|
|
145
|
+
require(
|
|
146
|
+
keccak256(abi.encodePacked(counterparty.version)) == keccak256(abi.encodePacked(selectedVersion)),
|
|
147
|
+
'Version mismatch'
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// do logic
|
|
152
|
+
|
|
153
|
+
return selectedVersion;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function onConnectIbcChannel(
|
|
157
|
+
bytes32 channelId,
|
|
158
|
+
bytes32 counterpartyChannelId,
|
|
159
|
+
string calldata counterpartyVersion
|
|
160
|
+
) external virtual onlyIbcDispatcher {
|
|
161
|
+
// ensure negotiated version is supported
|
|
162
|
+
bool foundVersion = false;
|
|
163
|
+
for (uint256 i = 0; i < supportedVersions.length; i++) {
|
|
164
|
+
if (keccak256(abi.encodePacked(counterpartyVersion)) == keccak256(abi.encodePacked(supportedVersions[i]))) {
|
|
165
|
+
foundVersion = true;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
require(foundVersion, 'Unsupported version');
|
|
170
|
+
|
|
171
|
+
// do logic
|
|
172
|
+
|
|
173
|
+
ChannelMapping memory channelMapping = ChannelMapping({
|
|
174
|
+
channelId: channelId,
|
|
175
|
+
cpChannelId: counterpartyChannelId
|
|
176
|
+
});
|
|
177
|
+
connectedChannels.push(channelMapping);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function onCloseIbcChannel(bytes32 channelId, string calldata, bytes32) external virtual onlyIbcDispatcher {
|
|
181
|
+
// logic to determin if the channel should be closed
|
|
182
|
+
bool channelFound = false;
|
|
183
|
+
for (uint256 i = 0; i < connectedChannels.length; i++) {
|
|
184
|
+
if (connectedChannels[i].channelId == channelId) {
|
|
185
|
+
for (uint256 j = i; j < connectedChannels.length - 1; j++) {
|
|
186
|
+
connectedChannels[j] = connectedChannels[j + 1];
|
|
187
|
+
}
|
|
188
|
+
connectedChannels.pop();
|
|
189
|
+
channelFound = true;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
require(channelFound, 'Channel not found');
|
|
194
|
+
|
|
195
|
+
// do logic
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* This func triggers channel closure from the dApp.
|
|
200
|
+
* Func args can be arbitary, as long as dispatcher.closeIbcChannel is invoked propperly.
|
|
201
|
+
*/
|
|
202
|
+
function triggerChannelClose(bytes32 channelId) external virtual onlyOwner {
|
|
203
|
+
dispatcher.closeIbcChannel(channelId);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
//SPDX-License-Identifier: UNLICENSED
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.9;
|
|
4
|
+
|
|
5
|
+
import '@open-ibc/vibc-core-smart-contracts/contracts/libs/Ibc.sol';
|
|
6
|
+
import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcReceiver.sol';
|
|
7
|
+
import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcDispatcher.sol';
|
|
8
|
+
import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcMiddleware.sol';
|
|
9
|
+
|
|
10
|
+
contract GeneralMiddleware is IbcMwUser, IbcMiddleware, IbcMwEventsEmitter {
|
|
11
|
+
/**
|
|
12
|
+
* @dev MW_ID is the ID of MW contract on all supported virtual chains.
|
|
13
|
+
* MW_ID must:
|
|
14
|
+
* - be globally unique, ie. no two MWs should have the same MW_ID registered on Polymer chain.
|
|
15
|
+
* - be identical on all supported virtual chains.
|
|
16
|
+
* - be identical on one virtual chain across multiple deployed MW instances. Each MW instance belong exclusively to one MW stack.
|
|
17
|
+
* - be 1 << N, where N is a non-negative integer, and not in conflict with other MWs.
|
|
18
|
+
*/
|
|
19
|
+
uint256 public MW_ID;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param _middleware The middleware contract address this contract sends packets to and receives packets from.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
constructor(uint256 mwId, address _middleware) IbcMwUser(_middleware) {
|
|
26
|
+
MW_ID = mwId;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function sendUniversalPacket(
|
|
30
|
+
bytes32 channelId,
|
|
31
|
+
bytes32 destPortAddr,
|
|
32
|
+
bytes calldata appData,
|
|
33
|
+
uint64 timeoutTimestamp
|
|
34
|
+
) external override {
|
|
35
|
+
_sendPacket(channelId, IbcUtils.toBytes32(msg.sender), destPortAddr, 0, appData, timeoutTimestamp);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function sendMWPacket(
|
|
39
|
+
bytes32 channelId,
|
|
40
|
+
bytes32 srcPortAddr,
|
|
41
|
+
bytes32 destPortAddr,
|
|
42
|
+
uint256 srcMwIds,
|
|
43
|
+
bytes calldata appData,
|
|
44
|
+
uint64 timeoutTimestamp
|
|
45
|
+
) external override {
|
|
46
|
+
_sendPacket(channelId, srcPortAddr, destPortAddr, srcMwIds, appData, timeoutTimestamp);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function onRecvMWPacket(
|
|
50
|
+
bytes32 channelId,
|
|
51
|
+
UniversalPacket calldata ucPacket,
|
|
52
|
+
// address srcPortAddr,
|
|
53
|
+
// address destPortAddr,
|
|
54
|
+
// 0-based receiver middleware index in the MW stack.
|
|
55
|
+
// 0 for the first MW directly called by UniversalChannel MW.
|
|
56
|
+
// `mwIndex-1` is the last MW that delivers the packet to the non-MW dApp.
|
|
57
|
+
// Each mw in the stack must increment mwIndex by 1 before calling the next MW.
|
|
58
|
+
uint256 mwIndex,
|
|
59
|
+
// bytes calldata appData,
|
|
60
|
+
address[] calldata mwAddrs
|
|
61
|
+
) external onlyIbcMw returns (AckPacket memory ackPacket) {
|
|
62
|
+
// extra MW custom logic here to process packet, eg. emit MW events, mutate state, etc.
|
|
63
|
+
// implementer can emit custom data fields suitable for their use cases.
|
|
64
|
+
// Here we use MW_ID as the custom MW data field.
|
|
65
|
+
emit RecvMWPacket(
|
|
66
|
+
channelId,
|
|
67
|
+
ucPacket.srcPortAddr,
|
|
68
|
+
ucPacket.destPortAddr,
|
|
69
|
+
MW_ID,
|
|
70
|
+
ucPacket.appData,
|
|
71
|
+
abi.encodePacked(MW_ID)
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (mwIndex == mwAddrs.length - 1) {
|
|
75
|
+
// last MW in the stack, deliver packet to dApp
|
|
76
|
+
return
|
|
77
|
+
IbcUniversalPacketReceiver(IbcUtils.toAddress(ucPacket.destPortAddr)).onRecvUniversalPacket(
|
|
78
|
+
channelId,
|
|
79
|
+
ucPacket
|
|
80
|
+
);
|
|
81
|
+
} else {
|
|
82
|
+
// send packet to next MW
|
|
83
|
+
return IbcMwPacketReceiver(mwAddrs[mwIndex + 1]).onRecvMWPacket(channelId, ucPacket, mwIndex + 1, mwAddrs);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function onRecvMWAck(
|
|
88
|
+
bytes32 channelId,
|
|
89
|
+
UniversalPacket calldata ucPacket,
|
|
90
|
+
// 0-based receiver middleware index in the MW stack.
|
|
91
|
+
// 0 for the first MW directly called by UniversalChannel MW.
|
|
92
|
+
// `mwIndex-1` is the last MW that delivers the packet to the non-MW dApp.
|
|
93
|
+
// Each mw in the stack must increment mwIndex by 1 before calling the next MW.
|
|
94
|
+
uint256 mwIndex,
|
|
95
|
+
address[] calldata mwAddrs,
|
|
96
|
+
AckPacket calldata ack
|
|
97
|
+
) external override onlyIbcMw {
|
|
98
|
+
// extra MW custom logic here to process packet, eg. emit MW events, mutate state, etc.
|
|
99
|
+
// implementer can emit custom data fields suitable for their use cases.
|
|
100
|
+
// Here we use MW_ID as the custom MW data field.
|
|
101
|
+
emit RecvMWAck(
|
|
102
|
+
channelId,
|
|
103
|
+
ucPacket.srcPortAddr,
|
|
104
|
+
ucPacket.destPortAddr,
|
|
105
|
+
MW_ID,
|
|
106
|
+
ucPacket.appData,
|
|
107
|
+
abi.encodePacked(MW_ID),
|
|
108
|
+
ack
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
if (mwIndex == mwAddrs.length - 1) {
|
|
112
|
+
// last MW in the stack, deliver ack to dApp
|
|
113
|
+
IbcUniversalPacketReceiver(IbcUtils.toAddress(ucPacket.srcPortAddr)).onUniversalAcknowledgement(
|
|
114
|
+
channelId,
|
|
115
|
+
ucPacket,
|
|
116
|
+
ack
|
|
117
|
+
);
|
|
118
|
+
} else {
|
|
119
|
+
// send ack to next MW
|
|
120
|
+
IbcMwPacketReceiver(mwAddrs[mwIndex + 1]).onRecvMWAck(channelId, ucPacket, mwIndex + 1, mwAddrs, ack);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function onRecvMWTimeout(
|
|
125
|
+
bytes32 channelId,
|
|
126
|
+
UniversalPacket calldata ucPacket,
|
|
127
|
+
uint256 mwIndex,
|
|
128
|
+
address[] calldata mwAddrs
|
|
129
|
+
) external override onlyIbcMw {
|
|
130
|
+
// extra MW custom logic here to process packet, eg. emit MW events, mutate state, etc.
|
|
131
|
+
// implementer can emit custom data fields suitable for their use cases.
|
|
132
|
+
// Here we use MW_ID as the custom MW data field.
|
|
133
|
+
emit RecvMWTimeout(
|
|
134
|
+
channelId,
|
|
135
|
+
ucPacket.srcPortAddr,
|
|
136
|
+
ucPacket.destPortAddr,
|
|
137
|
+
MW_ID,
|
|
138
|
+
ucPacket.appData,
|
|
139
|
+
abi.encodePacked(MW_ID)
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
if (mwIndex == mwAddrs.length - 1) {
|
|
143
|
+
// last MW in the stack, deliver timeout to dApp
|
|
144
|
+
IbcUniversalPacketReceiver(IbcUtils.toAddress(ucPacket.srcPortAddr)).onTimeoutUniversalPacket(
|
|
145
|
+
channelId,
|
|
146
|
+
ucPacket
|
|
147
|
+
);
|
|
148
|
+
} else {
|
|
149
|
+
// send timeout to next MW
|
|
150
|
+
IbcMwPacketReceiver(mwAddrs[mwIndex + 1]).onRecvMWTimeout(channelId, ucPacket, mwIndex + 1, mwAddrs);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function onRecvUniversalPacket(
|
|
155
|
+
bytes32 channelId,
|
|
156
|
+
UniversalPacket calldata ucPacket
|
|
157
|
+
) external override onlyIbcMw returns (AckPacket memory ackPacket) {}
|
|
158
|
+
|
|
159
|
+
function onUniversalAcknowledgement(
|
|
160
|
+
bytes32 channelId,
|
|
161
|
+
UniversalPacket memory packet,
|
|
162
|
+
AckPacket calldata ack
|
|
163
|
+
) external override onlyIbcMw {}
|
|
164
|
+
|
|
165
|
+
function onTimeoutUniversalPacket(bytes32 channelId, UniversalPacket calldata packet) external override onlyIbcMw {}
|
|
166
|
+
|
|
167
|
+
// internal function to send packet to next MW with MW Ids bit flipped
|
|
168
|
+
// param srcMwIds: MW ID bitmap excluding this MW's ID
|
|
169
|
+
function _sendPacket(
|
|
170
|
+
bytes32 channelId,
|
|
171
|
+
bytes32 srcPortAddr,
|
|
172
|
+
bytes32 destPortAddr,
|
|
173
|
+
uint256 srcMwIds,
|
|
174
|
+
bytes calldata appData,
|
|
175
|
+
uint64 timeoutTimestamp
|
|
176
|
+
) internal virtual {
|
|
177
|
+
// extra MW custom logic here to process packet, eg. emit MW events, mutate state, etc.
|
|
178
|
+
// implementer can emit custom data fields suitable for their use cases.
|
|
179
|
+
// Here we use MW_ID as the custom MW data field.
|
|
180
|
+
emit SendMWPacket(
|
|
181
|
+
channelId,
|
|
182
|
+
srcPortAddr,
|
|
183
|
+
destPortAddr,
|
|
184
|
+
MW_ID,
|
|
185
|
+
appData,
|
|
186
|
+
timeoutTimestamp,
|
|
187
|
+
abi.encodePacked(MW_ID)
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// send packet to next MW
|
|
191
|
+
IbcMwPacketSender(mw).sendMWPacket(
|
|
192
|
+
channelId,
|
|
193
|
+
srcPortAddr,
|
|
194
|
+
destPortAddr,
|
|
195
|
+
srcMwIds | MW_ID,
|
|
196
|
+
appData,
|
|
197
|
+
timeoutTimestamp
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
//SPDX-License-Identifier: UNLICENSED
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.9;
|
|
4
|
+
|
|
5
|
+
import '@open-ibc/vibc-core-smart-contracts/contracts/libs/Ibc.sol';
|
|
6
|
+
import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcReceiver.sol';
|
|
7
|
+
import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcDispatcher.sol';
|
|
8
|
+
import '@open-ibc/vibc-core-smart-contracts/contracts/interfaces/IbcMiddleware.sol';
|
|
9
|
+
|
|
10
|
+
// UniversalChanIbcApp is a contract that can be used as a base contract
|
|
11
|
+
// for IBC-enabled contracts that send packets over the universal channel.
|
|
12
|
+
contract UniversalChanIbcApp is IbcMwUser, IbcUniversalPacketReceiver {
|
|
13
|
+
struct UcPacketWithChannel {
|
|
14
|
+
bytes32 channelId;
|
|
15
|
+
UniversalPacket packet;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
struct UcAckWithChannel {
|
|
19
|
+
bytes32 channelId;
|
|
20
|
+
UniversalPacket packet;
|
|
21
|
+
AckPacket ack;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// received packet as chain B
|
|
25
|
+
UcPacketWithChannel[] public recvedPackets;
|
|
26
|
+
// received ack packet as chain A
|
|
27
|
+
UcAckWithChannel[] public ackPackets;
|
|
28
|
+
// received timeout packet as chain A
|
|
29
|
+
UcPacketWithChannel[] public timeoutPackets;
|
|
30
|
+
|
|
31
|
+
constructor(address _middleware) IbcMwUser(_middleware) {}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @dev Implement a function to send a packet that calls the IbcUniversalPacketSender(mw).sendUniversalPacket function
|
|
35
|
+
* It has the following function handle:
|
|
36
|
+
* function sendUniversalPacket(
|
|
37
|
+
bytes32 channelId,
|
|
38
|
+
bytes32 destPortAddr,
|
|
39
|
+
bytes calldata appData,
|
|
40
|
+
uint64 timeoutTimestamp
|
|
41
|
+
) external;
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @dev Packet lifecycle callback that implements packet receipt logic and returns and acknowledgement packet.
|
|
46
|
+
* MUST be overriden by the inheriting contract.
|
|
47
|
+
*
|
|
48
|
+
* @param channelId the ID of the channel (locally) the packet was received on.
|
|
49
|
+
* @param packet the Universal packet encoded by the source and relayed by the relayer.
|
|
50
|
+
*/
|
|
51
|
+
function onRecvUniversalPacket(
|
|
52
|
+
bytes32 channelId,
|
|
53
|
+
UniversalPacket calldata packet
|
|
54
|
+
) external virtual onlyIbcMw returns (AckPacket memory ackPacket) {
|
|
55
|
+
recvedPackets.push(UcPacketWithChannel(channelId, packet));
|
|
56
|
+
// 1. decode the packet.data
|
|
57
|
+
// 2. do logic
|
|
58
|
+
// 3. encode the ack packet (encoding format should be agreed between the two applications)
|
|
59
|
+
// below is an example, the actual ackpacket data should be implemented by the contract developer
|
|
60
|
+
return AckPacket(true, abi.encodePacked(address(this), IbcUtils.toAddress(packet.srcPortAddr), 'ack-', packet.appData));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @dev Packet lifecycle callback that implements packet acknowledgment logic.
|
|
65
|
+
* MUST be overriden by the inheriting contract.
|
|
66
|
+
*
|
|
67
|
+
* @param channelId the ID of the channel (locally) the ack was received on.
|
|
68
|
+
* @param packet the Universal packet encoded by the source and relayed by the relayer.
|
|
69
|
+
* @param ack the acknowledgment packet encoded by the destination and relayed by the relayer.
|
|
70
|
+
*/
|
|
71
|
+
function onUniversalAcknowledgement(
|
|
72
|
+
bytes32 channelId,
|
|
73
|
+
UniversalPacket memory packet,
|
|
74
|
+
AckPacket calldata ack
|
|
75
|
+
) external virtual onlyIbcMw {
|
|
76
|
+
ackPackets.push(UcAckWithChannel(channelId, packet, ack));
|
|
77
|
+
// 1. decode the ack.data
|
|
78
|
+
// 2. do logic
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @dev Packet lifecycle callback that implements packet receipt logic and return and acknowledgement packet.
|
|
83
|
+
* MUST be overriden by the inheriting contract.
|
|
84
|
+
* NOT SUPPORTED YET
|
|
85
|
+
*
|
|
86
|
+
* @param channelId the ID of the channel (locally) the timeout was submitted on.
|
|
87
|
+
* @param packet the Universal packet encoded by the counterparty and relayed by the relayer
|
|
88
|
+
*/
|
|
89
|
+
function onTimeoutUniversalPacket(bytes32 channelId, UniversalPacket calldata packet) external virtual onlyIbcMw {
|
|
90
|
+
timeoutPackets.push(UcPacketWithChannel(channelId, packet));
|
|
91
|
+
// do logic
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require("@nomicfoundation/hardhat-toolbox");
|
|
2
|
+
require("@nomicfoundation/hardhat-foundry");
|
|
3
|
+
|
|
4
|
+
require('dotenv').config();
|
|
5
|
+
|
|
6
|
+
/** @type import('hardhat/config').HardhatUserConfig */
|
|
7
|
+
module.exports = {
|
|
8
|
+
solidity: {
|
|
9
|
+
version: '0.8.23',
|
|
10
|
+
settings: {
|
|
11
|
+
optimizer: {
|
|
12
|
+
enabled: true,
|
|
13
|
+
runs: 200 // Optimize for a typical number of runs
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
networks: {
|
|
18
|
+
// for Base testnet
|
|
19
|
+
'base': {
|
|
20
|
+
url: 'https://sepolia.base.org',
|
|
21
|
+
accounts: [
|
|
22
|
+
process.env.PRIVATE_KEY_1
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
// for OP testnet
|
|
26
|
+
'optimism': {
|
|
27
|
+
url: 'https://sepolia.optimism.io',
|
|
28
|
+
accounts: [
|
|
29
|
+
process.env.PRIVATE_KEY_1
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
defaultNetwork: 'optimism',
|
|
34
|
+
paths: {
|
|
35
|
+
sources: './contracts',
|
|
36
|
+
tests: './test',
|
|
37
|
+
cache: './cache',
|
|
38
|
+
artifacts: './artifacts',
|
|
39
|
+
libraries: './lib',
|
|
40
|
+
},
|
|
41
|
+
etherscan: {
|
|
42
|
+
apiKey: {
|
|
43
|
+
optimism: process.env.OP_BLOCKSCOUT_API_KEY,
|
|
44
|
+
base: process.env.BASE_BLOCKSCOUT_API_KEY,
|
|
45
|
+
},
|
|
46
|
+
customChains: [
|
|
47
|
+
{
|
|
48
|
+
network: "base",
|
|
49
|
+
chainId: 84532,
|
|
50
|
+
urls: {
|
|
51
|
+
apiURL: "https://base-sepolia.blockscout.com/api",
|
|
52
|
+
browserURL: "https://base-sepolia.blockscout.com",
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
network: "optimism",
|
|
57
|
+
chainId: 11155420,
|
|
58
|
+
urls: {
|
|
59
|
+
apiURL: "https://optimism-sepolia.blockscout.com/api",
|
|
60
|
+
browserURL: "https://optimism-sepolia.blockscout.com",
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|