Skip to content

Commit 5cfe96d

Browse files
AdityaSripalvladjdkEric-Warehimezsystm
authored
EVM Callbacks (#150)
* start building * do packet callbacks * push change sources side to have specific callbacks * cleanup * add documentation * start testing framework * finish up test framework * better error testing * success test cases * fix address issue * Allow multihop tokens Create ERC20 precompiles for tokens that have more than 1 hop * set allowance on erc20 - checks if erc20 exists (should exist because this callback is after the erc20 callback) - sets allowance for ibc'd erc20 for the full transfer amount to the contract on behalf of the isolated address * fail if contract address is not contract fails out if the contract address provided has no code. this prevents silent successes if the contract address is an EOA * fix initialization in app.go include erc20 keeper * check for contract in timeout and ack and fix tests * use remaining context gas as max gas * re-add compiled contracts * lints * md lints * wip: happy path testing * change middleware order * update erc20 gas * fix call * wip: tests, failing out of gas every evm call * wip: tests, failing out of gas every evm call * Happy path testing + lints * uncomment previous test * clean up * gas handling and docs * add tests for recv, ack, timeout * lints * small fixes * fix errors in testcase after update * Rename approve output decoding * Check receiver tokens instead of callback contract * Don't panic in ibc middleware * Apply suggestions from code review Co-authored-by: Eric Warehime <[email protected]> Co-authored-by: Hyunwoo Lee <[email protected]> --------- Co-authored-by: Vlad <[email protected]> Co-authored-by: Eric Warehime <[email protected]> Co-authored-by: Hyunwoo Lee <[email protected]>
1 parent 0e7fd50 commit 5cfe96d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+3499
-217
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-License-Identifier: LGPL-3.0-only
2+
pragma solidity >=0.8.18;
3+
4+
interface ICallbacks {
5+
/// @dev Callback function to be called on the source chain
6+
/// after the packet life cycle is completed and acknowledgement is processed
7+
/// by source chain. The contract address is passed the packet information and acknowledgmeent
8+
/// to execute the callback logic.
9+
/// @param channelId the channnel identifier of the packet
10+
/// @param portId the port identifier of the packet
11+
/// @param sequence the sequence number of the packet
12+
/// @param data the data of the packet
13+
/// @param acknowledgement the acknowledgement of the packet
14+
function onPacketAcknowledgement(
15+
string memory channelId,
16+
string memory portId,
17+
uint64 sequence,
18+
bytes memory data,
19+
bytes memory acknowledgement
20+
) external;
21+
22+
/// @dev Callback function to be called on the source chain
23+
/// after the packet life cycle is completed and the packet is timed out
24+
/// by source chain. The contract address is passed the packet information
25+
/// to execute the callback logic.
26+
/// @param channelId the channnel identifier of the packet
27+
/// @param portId the port identifier of the packet
28+
/// @param sequence the sequence number of the packet
29+
/// @param data the data of the packet
30+
function onPacketTimeout(
31+
string memory channelId,
32+
string memory portId,
33+
uint64 sequence,
34+
bytes memory data
35+
) external;
36+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
pragma solidity ^0.8.20;
2+
3+
import "../../../../precompiles/callbacks/ICallbacks.sol";
4+
import "../../../../precompiles/erc20/IERC20.sol";
5+
6+
contract CounterWithCallbacks is ICallbacks {
7+
// State variables
8+
int public counter;
9+
10+
// Mapping: user address => token address => balance
11+
mapping(address => mapping(address => uint256)) public userTokenBalances;
12+
13+
// Events
14+
event CounterIncremented(int newValue, address indexed user);
15+
event TokensDeposited(
16+
address indexed user,
17+
address indexed token,
18+
uint256 amount,
19+
uint256 newBalance
20+
);
21+
event PacketAcknowledged(
22+
string indexed channelId,
23+
string indexed portId,
24+
uint64 sequence,
25+
bytes data,
26+
bytes acknowledgement
27+
);
28+
event PacketTimedOut(
29+
string indexed channelId,
30+
string indexed portId,
31+
uint64 sequence,
32+
bytes data
33+
);
34+
35+
/**
36+
* @dev Increment the counter and deposit ERC20 tokens
37+
* @param token The address of the ERC20 token
38+
* @param amount The amount of tokens to deposit
39+
*/
40+
function add(address token, uint256 amount)
41+
external
42+
{
43+
// Transfer tokens from user to this contract
44+
IERC20(token).transferFrom(msg.sender, address(this), amount);
45+
46+
// Increment counter
47+
counter += 1;
48+
49+
// Add to user's token balance
50+
userTokenBalances[msg.sender][token] += amount;
51+
52+
// Emit events
53+
emit CounterIncremented(counter, msg.sender);
54+
emit TokensDeposited(msg.sender, token, amount, userTokenBalances[msg.sender][token]);
55+
}
56+
57+
/**
58+
* @dev Get the current counter value
59+
* @return The current counter value
60+
*/
61+
function getCounter() external view returns (int) {
62+
return counter;
63+
}
64+
65+
/**
66+
* @dev Get a user's balance for a specific token
67+
* @param user The address of the user
68+
* @param token The address of the token
69+
* @return The user's token balance
70+
*/
71+
function getTokenBalance(address user, address token) external view returns (uint256) {
72+
return userTokenBalances[user][token];
73+
}
74+
75+
/**
76+
* @dev Implementation of ICallbacks interface
77+
* Called when a packet acknowledgement is received
78+
*/
79+
function onPacketAcknowledgement(
80+
string memory channelId,
81+
string memory portId,
82+
uint64 sequence,
83+
bytes memory data,
84+
bytes memory acknowledgement
85+
) external override {
86+
// Emit event when packet is acknowledged
87+
emit PacketAcknowledged(channelId, portId, sequence, data, acknowledgement);
88+
89+
counter += 1; // Increment counter on acknowledgement
90+
}
91+
92+
/**
93+
* @dev Implementation of ICallbacks interface
94+
* Called when a packet times out
95+
*/
96+
function onPacketTimeout(
97+
string memory channelId,
98+
string memory portId,
99+
uint64 sequence,
100+
bytes memory data
101+
) external override {
102+
// Emit event when packet times out
103+
emit PacketTimedOut(channelId, portId, sequence, data);
104+
counter -= 1; // Increment counter on acknowledgement
105+
}
106+
107+
/**
108+
* @dev Reset the counter
109+
*/
110+
function resetCounter() external {
111+
counter = 0;
112+
}
113+
}

evmd/app.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ import (
3232
"github.com/cosmos/evm/x/feemarket"
3333
feemarketkeeper "github.com/cosmos/evm/x/feemarket/keeper"
3434
feemarkettypes "github.com/cosmos/evm/x/feemarket/types"
35-
"github.com/cosmos/evm/x/ibc/transfer" // NOTE: override ICS20 keeper to support IBC transfers of ERC20 tokens
35+
ibccallbackskeeper "github.com/cosmos/evm/x/ibc/callbacks/keeper"
36+
// NOTE: override ICS20 keeper to support IBC transfers of ERC20 tokens
37+
"github.com/cosmos/evm/x/ibc/transfer"
3638
transferkeeper "github.com/cosmos/evm/x/ibc/transfer/keeper"
3739
transferv2 "github.com/cosmos/evm/x/ibc/transfer/v2"
3840
"github.com/cosmos/evm/x/precisebank"
@@ -42,6 +44,7 @@ import (
4244
evmkeeper "github.com/cosmos/evm/x/vm/keeper"
4345
evmtypes "github.com/cosmos/evm/x/vm/types"
4446
"github.com/cosmos/gogoproto/proto"
47+
ibccallbacks "github.com/cosmos/ibc-go/v10/modules/apps/callbacks"
4548
ibctransfer "github.com/cosmos/ibc-go/v10/modules/apps/transfer"
4649
ibctransfertypes "github.com/cosmos/ibc-go/v10/modules/apps/transfer/types"
4750
ibc "github.com/cosmos/ibc-go/v10/modules/core"
@@ -206,6 +209,7 @@ type EVMD struct {
206209
// IBC keepers
207210
IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly
208211
TransferKeeper transferkeeper.Keeper
212+
CallbackKeeper ibccallbackskeeper.ContractKeeper
209213

210214
// Cosmos EVM keepers
211215
FeeMarketKeeper feemarketkeeper.Keeper
@@ -544,21 +548,29 @@ func NewExampleApp(
544548
Create Transfer Stack
545549
546550
transfer stack contains (from bottom to top):
551+
- IBC Callbacks Middleware (with EVM ContractKeeper)
547552
- ERC-20 Middleware
548553
- IBC Transfer
549554
550555
SendPacket, since it is originating from the application to core IBC:
551-
transferKeeper.SendPacket -> erc20.SendPacket -> channel.SendPacket
556+
transferKeeper.SendPacket -> erc20.SendPacket -> callbacks.SendPacket -> channel.SendPacket
552557
553558
RecvPacket, message that originates from core IBC and goes down to app, the flow is the other way
554-
channel.RecvPacket -> erc20.OnRecvPacket -> transfer.OnRecvPacket
559+
channel.RecvPacket -> callbacks.OnRecvPacket -> erc20.OnRecvPacket -> transfer.OnRecvPacket
555560
*/
556561

557562
// create IBC module from top to bottom of stack
558563
var transferStack porttypes.IBCModule
559564

560565
transferStack = transfer.NewIBCModule(app.TransferKeeper)
566+
maxCallbackGas := uint64(1_000_000)
561567
transferStack = erc20.NewIBCMiddleware(app.Erc20Keeper, transferStack)
568+
app.CallbackKeeper = ibccallbackskeeper.NewKeeper(
569+
app.AccountKeeper,
570+
app.EVMKeeper,
571+
app.Erc20Keeper,
572+
)
573+
transferStack = ibccallbacks.NewIBCMiddleware(transferStack, app.IBCKeeper.ChannelKeeper, app.CallbackKeeper, maxCallbackGas)
562574

563575
var transferStackV2 ibcapi.IBCModule
564576
transferStackV2 = transferv2.NewIBCModule(app.TransferKeeper)

go.mod

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require (
77
cosmossdk.io/client/v2 v2.0.0-beta.7
88
cosmossdk.io/core v0.11.3
99
cosmossdk.io/errors v1.0.2
10-
cosmossdk.io/log v1.5.1
10+
cosmossdk.io/log v1.6.0
1111
cosmossdk.io/math v1.5.3
1212
cosmossdk.io/store v1.1.2
1313
cosmossdk.io/tools/confix v0.1.2
@@ -24,10 +24,10 @@ require (
2424
github.com/cosmos/go-bip39 v1.0.0
2525
github.com/cosmos/gogoproto v1.7.0
2626
github.com/cosmos/ibc-go/modules/capability v1.0.1
27-
github.com/cosmos/ibc-go/v10 v10.2.0
27+
github.com/cosmos/ibc-go/v10 v10.0.0-beta.0.0.20250528142215-7d579b91ac6b
2828
github.com/creachadair/tomledit v0.0.24
2929
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
30-
github.com/ethereum/go-ethereum v1.15.10
30+
github.com/ethereum/go-ethereum v1.15.11
3131
github.com/golang/protobuf v1.5.4
3232
github.com/gorilla/mux v1.8.1
3333
github.com/gorilla/websocket v1.5.3
@@ -68,7 +68,7 @@ require (
6868
cloud.google.com/go/iam v1.2.2 // indirect
6969
cloud.google.com/go/monitoring v1.21.2 // indirect
7070
cloud.google.com/go/storage v1.49.0 // indirect
71-
cosmossdk.io/collections v1.2.0 // indirect
71+
cosmossdk.io/collections v1.2.1 // indirect
7272
cosmossdk.io/depinject v1.2.0 // indirect
7373
cosmossdk.io/schema v1.1.0 // indirect
7474
filippo.io/edwards25519 v1.1.0 // indirect
@@ -82,7 +82,7 @@ require (
8282
github.com/Microsoft/go-winio v0.6.2 // indirect
8383
github.com/StackExchange/wmi v1.2.1 // indirect
8484
github.com/VictoriaMetrics/fastcache v1.12.2 // indirect
85-
github.com/aws/aws-sdk-go v1.44.224 // indirect
85+
github.com/aws/aws-sdk-go v1.49.0 // indirect
8686
github.com/beorn7/perks v1.0.1 // indirect
8787
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
8888
github.com/bgentry/speakeasy v0.2.0 // indirect
@@ -97,8 +97,8 @@ require (
9797
github.com/cloudwego/base64x v0.1.5 // indirect
9898
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
9999
github.com/cockroachdb/apd/v2 v2.0.2 // indirect
100-
github.com/cockroachdb/errors v1.11.3 // indirect
101-
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
100+
github.com/cockroachdb/errors v1.12.0 // indirect
101+
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect
102102
github.com/cockroachdb/pebble v1.1.5 // indirect
103103
github.com/cockroachdb/redact v1.1.6 // indirect
104104
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
@@ -131,7 +131,7 @@ require (
131131
github.com/fatih/color v1.17.0 // indirect
132132
github.com/felixge/httpsnoop v1.0.4 // indirect
133133
github.com/fsnotify/fsnotify v1.9.0 // indirect
134-
github.com/getsentry/sentry-go v0.28.1 // indirect
134+
github.com/getsentry/sentry-go v0.32.0 // indirect
135135
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
136136
github.com/go-kit/kit v0.13.0 // indirect
137137
github.com/go-kit/log v0.2.1 // indirect
@@ -148,7 +148,7 @@ require (
148148
github.com/gogo/protobuf v1.3.2 // indirect
149149
github.com/golang/glog v1.2.4 // indirect
150150
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
151-
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
151+
github.com/golang/snappy v0.0.5-0.20231225225746-43d5d4cd4e0e // indirect
152152
github.com/google/btree v1.1.3 // indirect
153153
github.com/google/flatbuffers v24.3.25+incompatible // indirect
154154
github.com/google/go-cmp v0.7.0 // indirect
@@ -167,7 +167,6 @@ require (
167167
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
168168
github.com/hashicorp/go-plugin v1.6.3 // indirect
169169
github.com/hashicorp/go-safetemp v1.0.0 // indirect
170-
github.com/hashicorp/go-uuid v1.0.3 // indirect
171170
github.com/hashicorp/go-version v1.7.0 // indirect
172171
github.com/hashicorp/golang-lru v1.0.2 // indirect
173172
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
@@ -176,6 +175,7 @@ require (
176175
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
177176
github.com/huandu/skiplist v1.2.1 // indirect
178177
github.com/huin/goupnp v1.3.0 // indirect
178+
github.com/iancoleman/orderedmap v0.3.0 // indirect
179179
github.com/iancoleman/strcase v0.3.0 // indirect
180180
github.com/inconshreveable/mousetrap v1.1.0 // indirect
181181
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
@@ -244,7 +244,7 @@ require (
244244
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
245245
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
246246
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect
247-
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
247+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
248248
go.opentelemetry.io/otel v1.34.0 // indirect
249249
go.opentelemetry.io/otel/metric v1.34.0 // indirect
250250
go.opentelemetry.io/otel/sdk v1.34.0 // indirect

0 commit comments

Comments
 (0)