Skip to content

Commit

Permalink
Support SendToIbc event in smart contract (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomas-nguy authored Sep 13, 2021
1 parent 66d9e40 commit 0cfaaf9
Show file tree
Hide file tree
Showing 8 changed files with 518 additions and 47 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@

- [cronos#11](https://github.com/crypto-org-chain/cronos/pull/11) embed gravity bridge module
- [cronos#35](https://github.com/crypto-org-chain/cronos/pull/35) add support for ibc hook
- [cronos#55](https://github.com/crypto-org-chain/cronos/pull/55) add support for ibc token conversion to crc20
- [cronos#45](https://github.com/crypto-org-chain/cronos/pull/45) Allow evm contract to call bank send and gravity send
- [cronos#55](https://github.com/crypto-org-chain/cronos/pull/55) add support for ibc token conversion to crc20
- [cronos#45](https://github.com/crypto-org-chain/cronos/pull/45) allow evm contract to call bank send and gravity send
- [cronos#65](https://github.com/crypto-org-chain/cronos/pull/65) support SendToIbc in evm_log_handlers
- [cronos#59](https://github.com/crypto-org-chain/cronos/pull/59) gravity bridged tokens are converted to crc20
automatically

Expand Down
5 changes: 3 additions & 2 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,8 +427,9 @@ func New(
app.GravityKeeper = *gravityKeeper.SetHooks(app.CronosKeeper)

app.EvmKeeper.SetHooks(cronosmodulekeeper.NewLogProcessEvmHook(
cronosmodulekeeper.NewNativeTransferHandler(app.BankKeeper, app.CronosKeeper),
cronosmodulekeeper.NewEthereumTransferHandler(gravitykeeper.NewMsgServerImpl(app.GravityKeeper), app.CronosKeeper),
cronosmodulekeeper.NewSendToAccountHandler(app.BankKeeper, app.CronosKeeper),
cronosmodulekeeper.NewSendToEthereumHandler(gravitykeeper.NewMsgServerImpl(app.GravityKeeper), app.CronosKeeper),
cronosmodulekeeper.NewSendToIbcHandler(app.CronosKeeper),
))

// register the staking hooks
Expand Down
7 changes: 7 additions & 0 deletions contracts/src/ModuleCRC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ contract ModuleCRC20 is DSToken {
string denom;

event __CronosSendToEthereum(address recipient, uint256 amount, uint256 bridge_fee);
event __CronosSendToIbc(string recipient, uint256 amount);

constructor(string memory denom_, uint8 decimals_) DSToken(denom) public {
decimals = decimals_;
Expand Down Expand Up @@ -42,4 +43,10 @@ contract ModuleCRC20 is DSToken {
unsafe_burn(msg.sender, add(amount, bridge_fee));
emit __CronosSendToEthereum(recipient, amount, bridge_fee);
}

// send an "amount" of the contract token to recipient through IBC
function send_to_ibc(string memory recipient, uint amount) public {
burn(msg.sender, amount);
emit __CronosSendToIbc(recipient, amount);
}
}
12 changes: 12 additions & 0 deletions integration_tests/contracts/contracts/CroBridge.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pragma solidity ^0.6.6;

contract CroBridge {

event __CronosSendToIbc(string recipient, uint256 amount);

// Pay the contract a certain CRO amount and trigger a CRO transfer
// from the contract to recipient through IBC
function send_cro_to_crypto_org(string memory recipient) public payable {
emit __CronosSendToIbc(recipient, msg.value);
}
}
128 changes: 98 additions & 30 deletions x/cronos/keeper/evm_log_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,38 @@ import (
)

var (
_ types.EvmLogHandler = NativeTransferHandler{}
_ types.EvmLogHandler = EthereumTransferHandler{}
_ types.EvmLogHandler = SendToAccountHandler{}
_ types.EvmLogHandler = SendToEthereumHandler{}
_ types.EvmLogHandler = SendToIbcHandler{}
)

const (
NativeTransferEventName = "__CronosSendToAccount"
EthereumTransferEventName = "__CronosSendToEthereum"
SendToAccountEventName = "__CronosSendToAccount"
SendToEthereumEventName = "__CronosSendToEthereum"
SendToIbcEventName = "__CronosSendToIbc"
)

var (
// NativeTransferEvent represent the signature of
// SendToAccountEvent represent the signature of
// `event __CronosSendToAccount(address recipient, uint256 amount)`
NativeTransferEvent abi.Event
SendToAccountEvent abi.Event

// EthereumTransferEvent represent the signature of
// SendToEthereumEvent represent the signature of
// `event __CronosSendToEthereum(address recipient, uint256 amount, uint256 bridge_fee)`
EthereumTransferEvent abi.Event
SendToEthereumEvent abi.Event

// SendToIbcEvent represent the signature of
// `event __CronosSendToIbc(string recipient, uint256 amount)`
SendToIbcEvent abi.Event
)

func init() {
addressType, _ := abi.NewType("address", "", nil)
uint256Type, _ := abi.NewType("uint256", "", nil)
NativeTransferEvent = abi.NewEvent(
NativeTransferEventName,
NativeTransferEventName,
stringType, _ := abi.NewType("string", "", nil)
SendToAccountEvent = abi.NewEvent(
SendToAccountEventName,
SendToAccountEventName,
false,
abi.Arguments{abi.Argument{
Name: "recipient",
Expand All @@ -49,9 +56,9 @@ func init() {
Indexed: false,
}},
)
EthereumTransferEvent = abi.NewEvent(
EthereumTransferEventName,
EthereumTransferEventName,
SendToEthereumEvent = abi.NewEvent(
SendToEthereumEventName,
SendToEthereumEventName,
false,
abi.Arguments{abi.Argument{
Name: "recipient",
Expand All @@ -67,27 +74,41 @@ func init() {
Indexed: false,
}},
)
SendToIbcEvent = abi.NewEvent(
SendToIbcEventName,
SendToIbcEventName,
false,
abi.Arguments{abi.Argument{
Name: "recipient",
Type: stringType,
Indexed: false,
}, abi.Argument{
Name: "amount",
Type: uint256Type,
Indexed: false,
}},
)
}

// NativeTransferHandler handles `__CronosSendToAccount` log
type NativeTransferHandler struct {
// SendToAccountHandler handles `__CronosSendToAccount` log
type SendToAccountHandler struct {
bankKeeper types.BankKeeper
cronosKeeper Keeper
}

func NewNativeTransferHandler(bankKeeper types.BankKeeper, cronosKeeper Keeper) *NativeTransferHandler {
return &NativeTransferHandler{
func NewSendToAccountHandler(bankKeeper types.BankKeeper, cronosKeeper Keeper) *SendToAccountHandler {
return &SendToAccountHandler{
bankKeeper: bankKeeper,
cronosKeeper: cronosKeeper,
}
}

func (h NativeTransferHandler) EventID() common.Hash {
return NativeTransferEvent.ID
func (h SendToAccountHandler) EventID() common.Hash {
return SendToAccountEvent.ID
}

func (h NativeTransferHandler) Handle(ctx sdk.Context, contract common.Address, data []byte) error {
unpacked, err := NativeTransferEvent.Inputs.Unpack(data)
func (h SendToAccountHandler) Handle(ctx sdk.Context, contract common.Address, data []byte) error {
unpacked, err := SendToAccountEvent.Inputs.Unpack(data)
if err != nil {
// log and ignore
h.cronosKeeper.Logger(ctx).Error("log signature matches but failed to decode", "error", err)
Expand All @@ -110,25 +131,25 @@ func (h NativeTransferHandler) Handle(ctx sdk.Context, contract common.Address,
return nil
}

// EthereumTransferHandler handles `__CronosSendToEthereum` log
type EthereumTransferHandler struct {
// SendToEthereumHandler handles `__CronosSendToEthereum` log
type SendToEthereumHandler struct {
gravitySrv gravitytypes.MsgServer
cronosKeeper Keeper
}

func NewEthereumTransferHandler(gravitySrv gravitytypes.MsgServer, cronosKeeper Keeper) *EthereumTransferHandler {
return &EthereumTransferHandler{
func NewSendToEthereumHandler(gravitySrv gravitytypes.MsgServer, cronosKeeper Keeper) *SendToEthereumHandler {
return &SendToEthereumHandler{
gravitySrv: gravitySrv,
cronosKeeper: cronosKeeper,
}
}

func (h EthereumTransferHandler) EventID() common.Hash {
return EthereumTransferEvent.ID
func (h SendToEthereumHandler) EventID() common.Hash {
return SendToEthereumEvent.ID
}

func (h EthereumTransferHandler) Handle(ctx sdk.Context, contract common.Address, data []byte) error {
unpacked, err := EthereumTransferEvent.Inputs.Unpack(data)
func (h SendToEthereumHandler) Handle(ctx sdk.Context, contract common.Address, data []byte) error {
unpacked, err := SendToEthereumEvent.Inputs.Unpack(data)
if err != nil {
// log and ignore
h.cronosKeeper.Logger(ctx).Info("log signature matches but failed to decode")
Expand All @@ -140,6 +161,10 @@ func (h EthereumTransferHandler) Handle(ctx sdk.Context, contract common.Address
return fmt.Errorf("contract %s is not connected to native token", contract)
}

if !types.IsValidGravityDenom(denom) {
return fmt.Errorf("the native token associated with the contract %s is not a gravity voucher", contract)
}

contractAddr := sdk.AccAddress(contract.Bytes())
ethRecipient := unpacked[0].(common.Address)
amount := sdk.NewIntFromBigInt(unpacked[1].(*big.Int))
Expand All @@ -156,3 +181,46 @@ func (h EthereumTransferHandler) Handle(ctx sdk.Context, contract common.Address
}
return nil
}

// SendToIbcHandler handles `__CronosSendToIbc` log
type SendToIbcHandler struct {
cronosKeeper Keeper
}

func NewSendToIbcHandler(cronosKeeper Keeper) *SendToIbcHandler {
return &SendToIbcHandler{
cronosKeeper: cronosKeeper,
}
}

func (h SendToIbcHandler) EventID() common.Hash {
return SendToIbcEvent.ID
}

func (h SendToIbcHandler) Handle(ctx sdk.Context, contract common.Address, data []byte) error {
unpacked, err := SendToIbcEvent.Inputs.Unpack(data)
if err != nil {
// log and ignore
h.cronosKeeper.Logger(ctx).Info("log signature matches but failed to decode")
return nil
}

denom, found := h.cronosKeeper.GetDenomByContract(ctx, contract)
if !found {
return fmt.Errorf("contract %s is not connected to native token", contract)
}

if !types.IsValidIBCDenom(denom) {
return fmt.Errorf("the native token associated with the contract %s is not an ibc voucher", contract)
}

contractAddr := sdk.AccAddress(contract.Bytes())
recipient := unpacked[0].(string)
amount := sdk.NewIntFromBigInt(unpacked[1].(*big.Int))
coin := sdk.NewCoin(denom, amount)
err = h.cronosKeeper.IbcTransferCoins(ctx, contractAddr.String(), recipient, sdk.NewCoins(coin))
if err != nil {
return err
}
return nil
}
Loading

0 comments on commit 0cfaaf9

Please sign in to comment.