Hello! I just started learning Polymer in the last few days, and trying to develop a simple smart contract based on the provided example so that I can have a better understanding.
Take this contract as an example:
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
import "./base/UniversalChanIbcApp.sol";
contract SendPointUC is UniversalChanIbcApp {
// application specific state
address public deployer;
int64 public points = 1000;
constructor(address _middleware) UniversalChanIbcApp(_middleware) {
deployer = msg.sender;
}
// application specific logic
function setPoints(int64 _points) public {
if (msg.sender != deployer) {
revert("Only deployer can set points");
}
points = _points;
}
// IBC logic
/**
* @dev Sends a packet with the caller's address over the universal channel.
* @param destPortAddr The address of the destination application.
* @param channelId The ID of the channel to send the packet to.
* @param timeoutSeconds The timeout in seconds (relative).
*/
function sendUniversalPacket(
address destPortAddr,
bytes32 channelId,
uint64 timeoutSeconds,
uint64 _nonce,
int64 _points
) external {
bytes memory payload = abi.encode(msg.sender, _nonce, _points);
uint64 timeoutTimestamp = uint64(
(block.timestamp + timeoutSeconds) * 1000000000
);
IbcUniversalPacketSender(mw).sendUniversalPacket(
channelId,
IbcUtils.toBytes32(destPortAddr),
payload,
timeoutTimestamp
);
}
/**
* @dev Packet lifecycle callback that implements packet receipt logic and returns and acknowledgement packet.
* MUST be overriden by the inheriting contract.
*
* @param channelId the ID of the channel (locally) the packet was received on.
* @param packet the Universal packet encoded by the source and relayed by the relayer.
*/
function onRecvUniversalPacket(
bytes32 channelId,
UniversalPacket calldata packet
) external override onlyIbcMw returns (AckPacket memory ackPacket) {
recvedPackets.push(UcPacketWithChannel(channelId, packet));
(address _sender, uint64 _nonce, int64 _points) = abi.decode(
packet.appData,
(address, uint64, int64)
);
points += _points;
return AckPacket(true, abi.encode(_sender, _points));
}
/**
* @dev Packet lifecycle callback that implements packet acknowledgment logic.
* MUST be overriden by the inheriting contract.
*
* @param channelId the ID of the channel (locally) the ack was received on.
* @param packet the Universal 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 onUniversalAcknowledgement(
bytes32 channelId,
UniversalPacket memory packet,
AckPacket calldata ack
) external override onlyIbcMw {
ackPackets.push(UcAckWithChannel(channelId, packet, ack));
// decode the counter from the ack packet
(address _sender, uint64 _nonce, int64 _points) = abi.decode(
ack.data,
(address, uint64, int64)
);
points -= _points;
}
/**
* @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 channelId the ID of the channel (locally) the timeout was submitted on.
* @param packet the Universal packet encoded by the counterparty and relayed by the relayer
*/
function onTimeoutUniversalPacket(
bytes32 channelId,
UniversalPacket calldata packet
) external override onlyIbcMw {
timeoutPackets.push(UcPacketWithChannel(channelId, packet));
// do logic
}
}
This is using the Universal Channel of Polymer, and what it does is really simple: sending 1 point from one chain (Optimism) to other chain (Base), so as a result:
Optimism - 1 = Base + 1
Side node: why am I adding the msg.sender and a nonce into the payload because I am trying to identify my transaction from the event emitted by the Dispatcher.
I have several questions regarding this:
- This contract is deployed via
just deploy-contract optimism base
command provided in the example project, that worked when I send from optimism to base (by calling thesendUniversalPacket()
contract method), but does it work the way around - sending back from base to optimism? Or I need to deploy again withjust deploy-contract base optimism
? - If we need to deploy twice, so let’s say I have deployed using
just deploy-contract optimism base
, does it meanonRecvUniversalPacket()
will only be called on base, andonUniversalAcknowledgement()
will only be called on optimism? - Now I am adding points on method
onRecvUniversalPacket()
(base) and deducting points ononUniversalAcknowledgement()
(optimism), but I found that sometimes the points get added on base but not deducted on optimism, I guess it was caused by the delay of acknowledgement. AsonTimeoutUniversalPacket()
is not available yet, what is the best practice to make sure the state on both chain are consistent? Should I change the logic to this instead:- Deduct points in
sendUniversalPacket()
(optimism) → Add points inonRecvUniversalPacket()
(base) → Add back the points on Optimism IF we cannot validate the correct result fromonUniversalAcknowledgement()
oronTimeoutUniversalPacket()
?
- Deduct points in
Sorry for my long question, and thank you very much for replying!