Skip to content

Commit

Permalink
implement adr-007
Browse files Browse the repository at this point in the history
  • Loading branch information
thomas-nguy committed Sep 5, 2022
1 parent f83260d commit b8f6202
Show file tree
Hide file tree
Showing 8 changed files with 400 additions and 31 deletions.
2 changes: 2 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,8 @@ func New(
evmhandlers.NewCancelSendToChainHandler(gravitySrv, app.CronosKeeper, app.GravityKeeper),
evmhandlers.NewSendToIbcHandler(app.BankKeeper, app.CronosKeeper),
evmhandlers.NewSendCroToIbcHandler(app.BankKeeper, app.CronosKeeper),
evmhandlers.NewSendToEvmChainHandler(gravitySrv, app.BankKeeper, app.CronosKeeper),
evmhandlers.NewCancelSendToEvmChainHandler(gravitySrv, app.CronosKeeper, app.GravityKeeper),
))

// register the staking hooks
Expand Down
18 changes: 9 additions & 9 deletions contracts/src/ModuleCRC21.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ contract ModuleCRC21 is DSToken {
string denom;
bool isSource;

event __CronosSendToIbc(address sender, string recipient, uint256 amount);
event __CronosSendToChain(address sender, address recipient, uint256 amount, uint256 bridge_fee, uint256 chain_id);
event __CronosCancelSendToChain(address sender, uint256 id);
event __CronosSendToIbc(address indexed sender, string indexed recipient, uint256 amount, string indexed channel_id, bytes extraData);
event __CronosSendToEvmChain(address indexed sender, address indexed recipient, uint256 indexed chain_id, uint256 amount, uint256 bridge_fee, bytes extraData);
event __CronosCancelSendToEvmChain(address indexed sender, uint256 id);

constructor(string memory denom_, uint8 decimals_, bool isSource_) DSToken(denom_) public {
decimals = decimals_;
Expand Down Expand Up @@ -57,28 +57,28 @@ contract ModuleCRC21 is DSToken {
**/

// send an "amount" of the contract token to recipient through IBC
function send_to_ibc(string memory recipient, uint amount) public {
function send_to_ibc(string memory recipient, uint amount, string memory channel_id, bytes memory extraData) public {
if (isSource) {
transferFrom(msg.sender, module_address, amount);
} else {
unsafe_burn(msg.sender, amount);
}
emit __CronosSendToIbc(msg.sender, recipient, amount);
emit __CronosSendToIbc(msg.sender, recipient, amount, channel_id, extraData);
}

// send to another chain through gravity bridge
function send_to_chain(address recipient, uint amount, uint bridge_fee, uint chain_id) external {
function send_to_evm_chain(address recipient, uint amount, uint chain_id, uint bridge_fee, bytes calldata extraData) external {
if (isSource) {
transferFrom(msg.sender, module_address, add(amount, bridge_fee));
} else {
unsafe_burn(msg.sender, add(amount, bridge_fee));
}
emit __CronosSendToChain(msg.sender, recipient, amount, bridge_fee, chain_id);
emit __CronosSendToEvmChain(msg.sender, recipient, chain_id, amount, bridge_fee, extraData);
}

// cancel a send to chain transaction considering if it hasnt been batched yet.
function cancel_send_to_chain(uint256 id) external {
emit __CronosCancelSendToChain(msg.sender, id);
function cancel_send_to_evm_chain(uint256 id) external {
emit __CronosCancelSendToEvmChain(msg.sender, id);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ To suggest an ADR, please make use of the [ADR template](./adr-template.md) prov
| [004](./adr-004.md) | Tokens conversion in Cronos | Accepted |
| [005](./adr-005.md) | Cross-chain Validation for Gravity Bridge | Rejected |
| [006](./adr-006.md) | Migrating CRC20 contract to CRC21 standard | Rejected |
| [007](./adr-007.md) | Generic event format for evm-hook actions | Proposed |
| [007](./adr-007.md) | Generic event format for evm-hook actions | Accepted |
| [008](./adr-008.md) | Denom and Contract Mapping Enhancement for Bi-Directional Token Conversion | Accepted |
5 changes: 3 additions & 2 deletions docs/architecture/adr-007.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* 2022-06-09: Initial Draft
* 2022-08-03: Update proposal to generic argument option
* 2022-09-05: Move to accepted


## Context
Expand Down Expand Up @@ -31,7 +32,7 @@ In this ADR, we define generic event argument that can be extended to accept new
event __CronosSendToIbc(address sender, string recipient, uint256 amount);
// Proposal
event __CronosSendToIbc(address sender, string recipient, uint256 amount, string channelId, bytes extraData);
event __CronosSendToIbc(address indexed sender, string indexed recipient, uint256 amount, string indexed channelId, bytes extraData);
```

#### Description
Expand All @@ -58,7 +59,7 @@ event __CronosSendToIbc(address sender, string recipient, uint256 amount, string
event __CronosSendToChain(address sender, address recipient, uint256 amount, uint256 bridge_fee, uint256 chain_id);
// Proposal
event __CronosSendToEVMChain(address sender, address recipient, uint256 amount, string chain_id, bytes extraData);
event __CronosSendToEvmChain(address indexed sender, address indexed recipient, uint256 indexed chain_id, uint256 amount, uint256 bridge_fee, bytes extraData);
```

#### Description
Expand Down
126 changes: 126 additions & 0 deletions x/cronos/keeper/evmhandlers/cancel_send_to_evm_chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package evmhandler

import (
"fmt"
"math/big"

gravitytypes "github.com/peggyjv/gravity-bridge/module/v2/x/gravity/types"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"

cronoskeeper "github.com/crypto-org-chain/cronos/x/cronos/keeper"
"github.com/crypto-org-chain/cronos/x/cronos/types"
)

var _ types.EvmLogHandler = CancelSendToChainHandler{}

const CancelSendToEvmChainEventName = "__CronosCancelSendToEvmChain"

// CancelSendToEvmChainEvent represent the signature of
// `event __CronosCancelSendToEvmChain(uint256 id)`
var CancelSendToEvmChainEvent abi.Event

func init() {
addressType, _ := abi.NewType("address", "", nil)
uint256Type, _ := abi.NewType("uint256", "", nil)

CancelSendToEvmChainEvent = abi.NewEvent(
CancelSendToEvmChainEventName,
CancelSendToEvmChainEventName,
false,
abi.Arguments{abi.Argument{
Name: "sender",
Type: addressType,
Indexed: false,
}, abi.Argument{
Name: "id",
Type: uint256Type,
Indexed: false,
}},
)
}

// CancelSendToEvmChainHandler handles `__CronosCancelSendToEvmChain` log
type CancelSendToEvmChainHandler struct {
gravitySrv gravitytypes.MsgServer
cronosKeeper cronoskeeper.Keeper
gravityKeeper types.GravityKeeper
}

func NewCancelSendToEvmChainHandler(
gravitySrv gravitytypes.MsgServer,
cronosKeeper cronoskeeper.Keeper,
gravityKeeper types.GravityKeeper,
) *CancelSendToEvmChainHandler {
return &CancelSendToEvmChainHandler{
gravitySrv: gravitySrv,
cronosKeeper: cronosKeeper,
gravityKeeper: gravityKeeper,
}
}

func (h CancelSendToEvmChainHandler) EventID() common.Hash {
return CancelSendToChainEvent.ID
}

// Handle `__CronosCancelSendToChain` log only if gravity is activated.
func (h CancelSendToEvmChainHandler) Handle(
ctx sdk.Context,
_ common.Address,
data []byte,
_ func(contractAddress common.Address, logSig common.Hash, logData []byte),
) error {
if h.gravitySrv == nil {
return fmt.Errorf("native action %s is not implemented", CancelSendToChainEventName)
}

unpacked, err := CancelSendToChainEvent.Inputs.Unpack(data)
if err != nil {
// log and ignore
h.cronosKeeper.Logger(ctx).Info("log signature matches but failed to decode")
return nil
}

senderCosmosAddr := sdk.AccAddress(unpacked[0].(common.Address).Bytes())
id := sdk.NewIntFromBigInt(unpacked[1].(*big.Int))

// Need to retrieve the batch to get the amount to refund
var unbatched []*gravitytypes.SendToEthereum
h.gravityKeeper.IterateUnbatchedSendToEthereums(ctx, func(ste *gravitytypes.SendToEthereum) bool {
unbatched = append(unbatched, ste)
return false
})

var send *gravitytypes.SendToEthereum
for _, ste := range unbatched {
if ste.Id == id.Uint64() {
send = ste
}
}
if send == nil {
return fmt.Errorf("id not found or the transaction is already included in a batch")
}

_, denom := h.gravityKeeper.ERC20ToDenomLookup(ctx, common.HexToAddress(send.Erc20Token.Contract))
if !types.IsValidGravityDenom(denom) {
return fmt.Errorf("the native token associated with the contract %s is not a gravity voucher", send.Erc20Token.Contract)
}

msg := gravitytypes.MsgCancelSendToEthereum{
Sender: senderCosmosAddr.String(),
Id: id.Uint64(),
}
_, err = h.gravitySrv.CancelSendToEthereum(sdk.WrapSDKContext(ctx), &msg)
if err != nil {
return err
}
refundAmount := sdk.NewCoins(sdk.NewCoin(denom, send.Erc20Token.Amount.Add(send.Erc20Fee.Amount)))
// If cancel has no error, we need to convert back the native token to evm tokens
err = h.cronosKeeper.ConvertVouchersToEvmCoins(ctx, senderCosmosAddr.String(), refundAmount)
if err != nil {
return err
}
return nil
}
170 changes: 170 additions & 0 deletions x/cronos/keeper/evmhandlers/send_to_evm_chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package evmhandler

import (
"fmt"
"math/big"

gravitytypes "github.com/peggyjv/gravity-bridge/module/v2/x/gravity/types"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"

cronoskeeper "github.com/crypto-org-chain/cronos/x/cronos/keeper"
"github.com/crypto-org-chain/cronos/x/cronos/types"
)

var _ types.EvmLogHandler = SendToEvmChainHandler{}

const (
SendToEvmChainEventName = "__CronosSendToEvmChain"
SendToEvmChainResponseEventName = "__CronosSendToEvmChainResponse"
)

var (
// SendToEvmChainEvent represent the signature of
// `event __CronosSendToEvmChain(address indexed sender, address indexed recipient, uint256 indexed chain_id, uint256 amount, uint256 bridge_fee, bytes extraData)`
SendToEvmChainEvent abi.Event

// SendToEvmChainResponseEvent represent the signature of
// `event __CronosSendToChainResponse(uint256 id)`
SendToEvmChainResponseEvent abi.Event
)

func init() {
addressType, _ := abi.NewType("address", "", nil)
uint256Type, _ := abi.NewType("uint256", "", nil)
bytesType, _ := abi.NewType("bytes", "", nil)

SendToEvmChainEvent = abi.NewEvent(
SendToEvmChainEventName,
SendToEvmChainEventName,
false,
abi.Arguments{abi.Argument{
Name: "sender",
Type: addressType,
Indexed: true,
}, abi.Argument{
Name: "recipient",
Type: addressType,
Indexed: true,
}, abi.Argument{
Name: "chain_id",
Type: uint256Type,
Indexed: true,
}, abi.Argument{
Name: "amount",
Type: uint256Type,
Indexed: false,
}, abi.Argument{
Name: "bridge_fee",
Type: uint256Type,
Indexed: false,
}, abi.Argument{
Name: "extraData",
Type: bytesType,
Indexed: false,
}},
)
SendToEvmChainResponseEvent = abi.NewEvent(
SendToEvmChainResponseEventName,
SendToEvmChainResponseEventName,
false,
abi.Arguments{abi.Argument{
Name: "id",
Type: uint256Type,
Indexed: false,
}},
)
}

// SendToEvmChainHandler handles `__CronosSendToEvmChain` log
type SendToEvmChainHandler struct {
gravitySrv gravitytypes.MsgServer
bankKeeper types.BankKeeper
cronosKeeper cronoskeeper.Keeper
}

func NewSendToEvmChainHandler(gravitySrv gravitytypes.MsgServer, bankKeeper types.BankKeeper, cronosKeeper cronoskeeper.Keeper) *SendToEvmChainHandler {
return &SendToEvmChainHandler{
gravitySrv: gravitySrv,
bankKeeper: bankKeeper,
cronosKeeper: cronosKeeper,
}
}

func (h SendToEvmChainHandler) EventID() common.Hash {
return SendToChainEvent.ID
}

// Handle `__CronosSendToChain` log only if gravity is activated.
func (h SendToEvmChainHandler) Handle(
ctx sdk.Context,
contract common.Address,
data []byte,
addLogToReceipt func(contractAddress common.Address, logSig common.Hash, logData []byte),
) error {
if h.gravitySrv == nil {
return fmt.Errorf("native action %s is not implemented", SendToChainEventName)
}

unpacked, err := SendToEvmChainEvent.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.IsValidGravityDenom(denom) && !types.IsValidCronosDenom(denom) {
return fmt.Errorf("the native token associated with the contract %s is neither a gravity voucher or a cronos token", contract)
}

contractCosmosAddr := sdk.AccAddress(contract.Bytes())
senderCosmosAddr := sdk.AccAddress(unpacked[0].(common.Address).Bytes())
ethRecipient := unpacked[1].(common.Address)
amount := sdk.NewIntFromBigInt(unpacked[2].(*big.Int))
bridgeFee := sdk.NewIntFromBigInt(unpacked[3].(*big.Int))
chainID := sdk.NewIntFromBigInt(unpacked[4].(*big.Int))

if !chainID.Equal(sdk.NewInt(1)) && !chainID.Equal(sdk.NewInt(3)) &&
!chainID.Equal(sdk.NewInt(4)) && !chainID.Equal(sdk.NewInt(5)) {
return fmt.Errorf("only ethereum network is not supported")
}

coins := sdk.NewCoins(sdk.NewCoin(denom, amount.Add(bridgeFee)))
if types.IsSourceCoin(denom) {
// it is a source token, we need to mint coins
if err = h.bankKeeper.MintCoins(ctx, types.ModuleName, coins); err != nil {
return err
}
// send the coin to the user
if err = h.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, senderCosmosAddr.Bytes(), coins); err != nil {
return err
}
} else {
// send coins from contract address to user address so that he will be able to cancel later on
if err = h.bankKeeper.SendCoins(ctx, contractCosmosAddr, senderCosmosAddr.Bytes(), coins); err != nil {
return err
}
}
// Initialize a gravity transfer
msg := gravitytypes.MsgSendToEthereum{
Sender: senderCosmosAddr.String(),
EthereumRecipient: ethRecipient.Hex(),
Amount: sdk.NewCoin(denom, amount),
BridgeFee: sdk.NewCoin(denom, bridgeFee),
}
resp, err := h.gravitySrv.SendToEthereum(sdk.WrapSDKContext(ctx), &msg)
if err != nil {
return err
}

logData, _ := SendToEvmChainResponseEvent.Inputs.Pack(big.NewInt(int64(resp.Id)))
addLogToReceipt(contract, SendToEvmChainResponseEvent.ID, logData)
return nil
}
Loading

0 comments on commit b8f6202

Please sign in to comment.