Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support ibc to crc20 #55

Merged
merged 4 commits into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
### Features

- [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

Expand Down
2 changes: 1 addition & 1 deletion x/cronos/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (suite *CronosTestSuite) TestMsgConvertVouchers() {
"Correct address with non supported coin denom",
types.NewMsgConvertVouchers(suite.address.String(), sdk.NewCoins(sdk.NewCoin("fake", sdk.NewInt(1)))),
func() {},
errors.New("coin fake is not supported"),
errors.New("coin fake is not supported for wrapping"),
},
}

Expand Down
6 changes: 3 additions & 3 deletions x/cronos/keeper/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,15 @@ func (k Keeper) DeployModuleCRC20(ctx sdk.Context, denom string) (common.Address
// ConvertCoinFromNativeToCRC20 convert native token to erc20 token
func (k Keeper) ConvertCoinFromNativeToCRC20(ctx sdk.Context, sender common.Address, coin sdk.Coin, autoDeploy bool) error {
if !types.IsValidDenomToWrap(coin.Denom) {
return errors.New("denom is not supported for wrapping")
return fmt.Errorf("coin %s is not supported for wrapping", coin.Denom)
}

var err error
// external contract is returned in preference to auto-deployed ones
contract, found := k.GetContractByDenom(ctx, coin.Denom)
if !found {
if !autoDeploy {
return errors.New("no contract found for the denom")
return fmt.Errorf("no contract found for the denom %s", coin.Denom)
}
contract, err = k.DeployModuleCRC20(ctx, coin.Denom)
if err != nil {
Expand All @@ -126,7 +126,7 @@ func (k Keeper) ConvertCoinFromNativeToCRC20(ctx sdk.Context, sender common.Addr
func (k Keeper) ConvertCoinFromCRC20ToNative(ctx sdk.Context, contract common.Address, receiver common.Address, amount sdk.Int) error {
denom, found := k.GetDenomByContract(ctx, contract)
if !found {
return errors.New("the contract address is not mapped to native token")
return fmt.Errorf("the contract address %s is not mapped to native token", contract.String())
}

err := k.bankKeeper.SendCoins(
Expand Down
59 changes: 39 additions & 20 deletions x/cronos/keeper/ibc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"errors"
"fmt"

"github.com/ethereum/go-ethereum/common"

"github.com/armon/go-metrics"
"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -52,7 +54,11 @@ func (k Keeper) ConvertVouchersToEvmCoins(ctx sdk.Context, from string, coins sd
}

default:
return fmt.Errorf("coin %s is not supported", c.Denom)
// TODO use autoDeploy boolean in Params.go
err := k.ConvertCoinFromNativeToCRC20(ctx, common.BytesToAddress(acc.Bytes()), c, true)
if err != nil {
return err
}
}
}
defer func() {
Expand Down Expand Up @@ -115,31 +121,20 @@ func (k Keeper) IbcTransferCoins(ctx sdk.Context, from, destination string, coin
return err
}

channelID, err := k.GetSourceChannelID(ctx, params.IbcCroDenom)
err = k.ibcSendTransfer(ctx, acc, destination, ibcCoin)
if err != nil {
return err
}
// Transfer coins to receiver through IBC
// We use current time for timeout timestamp and zero height for timeoutHeight
// it means it can never fail by timeout
// TODO Might need to consider add timeout option in configuration.
timeoutTimestamp := ctx.BlockTime().UnixNano()
timeoutHeight := ibcclienttypes.ZeroHeight()
err = k.transferKeeper.SendTransfer(
ctx,
ibctransfertypes.PortID,
channelID,
ibcCoin,
acc,
destination,
timeoutHeight,
uint64(timeoutTimestamp))

default:
_, found := k.GetContractByDenom(ctx, c.Denom)
if !found {
return fmt.Errorf("coin %s is not supported", c.Denom)
}
err = k.ibcSendTransfer(ctx, acc, destination, c)
if err != nil {
return err
}

default:
return fmt.Errorf("coin %s is not supported", c.Denom)
}
}

Expand All @@ -156,3 +151,27 @@ func (k Keeper) IbcTransferCoins(ctx sdk.Context, from, destination string, coin
}()
return nil
}

func (k Keeper) ibcSendTransfer(ctx sdk.Context, sender sdk.AccAddress, destination string, coin sdk.Coin) error {
// Coin needs to be a voucher so that we can extract the channel id from the denom
channelID, err := k.GetSourceChannelID(ctx, coin.Denom)
if err != nil {
return err
}

// Transfer coins to receiver through IBC
// We use current time for timeout timestamp and zero height for timeoutHeight
// it means it can never fail by timeout
// TODO Might need to consider add timeout option in configuration.
timeoutTimestamp := ctx.BlockTime().UnixNano()
timeoutHeight := ibcclienttypes.ZeroHeight()
return k.transferKeeper.SendTransfer(
ctx,
ibctransfertypes.PortID,
channelID,
coin,
sender,
destination,
timeoutHeight,
uint64(timeoutTimestamp))
}
70 changes: 67 additions & 3 deletions x/cronos/keeper/ibc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ package keeper_test

import (
"errors"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/crypto-org-chain/cronos/app"
cronosmodulekeeper "github.com/crypto-org-chain/cronos/x/cronos/keeper"
keepertest "github.com/crypto-org-chain/cronos/x/cronos/keeper/mock"
"github.com/crypto-org-chain/cronos/x/cronos/types"
"github.com/ethereum/go-ethereum/common"
"github.com/tharsis/ethermint/crypto/ethsecp256k1"
"math/big"
)

const CorrectIbcDenom = "ibc/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"

func (suite *KeeperTestSuite) TestConvertVouchersToEvmCoins() {

privKey, err := ethsecp256k1.GenerateKey()
Expand Down Expand Up @@ -45,7 +50,7 @@ func (suite *KeeperTestSuite) TestConvertVouchersToEvmCoins() {
address.String(),
sdk.NewCoins(sdk.NewCoin("fake", sdk.NewInt(1))),
func() {},
errors.New("coin fake is not supported"),
errors.New("coin fake is not supported for wrapping"),
func() {},
},
{
Expand All @@ -57,7 +62,7 @@ func (suite *KeeperTestSuite) TestConvertVouchersToEvmCoins() {
func() {},
},
{
"Correct address with enough IBC CRO token",
"Correct address with enough IBC CRO token : Should receive CRO tokens",
address.String(),
sdk.NewCoins(sdk.NewCoin(types.IbcCroDenomDefaultValue, sdk.NewInt(123))),
func() {
Expand All @@ -79,6 +84,37 @@ func (suite *KeeperTestSuite) TestConvertVouchersToEvmCoins() {
suite.Require().Equal(sdk.NewInt(1230000000000), evmCoin.Amount)
},
},
{
"Correct address with not enough IBC token",
address.String(),
sdk.NewCoins(sdk.NewCoin(CorrectIbcDenom, sdk.NewInt(1))),
func() {},
fmt.Errorf("0%s is smaller than 1%s: insufficient funds", CorrectIbcDenom, CorrectIbcDenom),
func() {},
},
{
"Correct address with IBC token : Should receive CRC20 tokens",
address.String(),
sdk.NewCoins(sdk.NewCoin(CorrectIbcDenom, sdk.NewInt(123))),
func() {
suite.MintCoins(address, sdk.NewCoins(sdk.NewCoin(CorrectIbcDenom, sdk.NewInt(123))))
// Verify balance IBC coin pre operation
ibcCroCoin := suite.GetBalance(address, CorrectIbcDenom)
suite.Require().Equal(sdk.NewInt(123), ibcCroCoin.Amount)
},
nil,
func() {
// Verify balance IBC coin post operation
ibcCroCoin := suite.GetBalance(address, CorrectIbcDenom)
suite.Require().Equal(sdk.NewInt(0), ibcCroCoin.Amount)
// Verify CRC20 balance post operation
contract, found := suite.app.CronosKeeper.GetContractByDenom(suite.ctx, CorrectIbcDenom)
suite.Require().True(found)
ret, err := suite.app.CronosKeeper.CallModuleCRC20(suite.ctx, contract, "balanceOf", common.BytesToAddress(address.Bytes()))
suite.Require().NoError(err)
suite.Require().Equal(big.NewInt(123), big.NewInt(0).SetBytes(ret))
},
},
}

for _, tc := range testCases {
Expand Down Expand Up @@ -158,7 +194,7 @@ func (suite *KeeperTestSuite) TestIbcTransferCoins() {
func() {},
},
{
"Correct address with enough EVM token",
"Correct address with enough EVM token : Should receive IBC CRO token",
address.String(),
"to",
sdk.NewCoins(sdk.NewCoin(suite.evmParam.EvmDenom, sdk.NewInt(1230000000000))),
Expand All @@ -183,6 +219,34 @@ func (suite *KeeperTestSuite) TestIbcTransferCoins() {
suite.Require().Equal(sdk.NewInt(0), evmCoin.Amount)
},
},
{
"Correct address with non correct IBC token denom",
address.String(),
"to",
sdk.NewCoins(sdk.NewCoin("incorrect", sdk.NewInt(123))),
func() {
// Add support for the IBC token
suite.app.CronosKeeper.SetAutoContractForDenom(suite.ctx, "incorrect", common.HexToAddress("0x11"))
},
errors.New("incorrect is invalid: ibc cro denom is invalid"),
func() {
},
},
{
"Correct address with correct IBC token denom",
address.String(),
"to",
sdk.NewCoins(sdk.NewCoin(CorrectIbcDenom, sdk.NewInt(123))),
func() {
// Mint IBC token for user
suite.MintCoins(address, sdk.NewCoins(sdk.NewCoin(CorrectIbcDenom, sdk.NewInt(123))))
// Add support for the IBC token
suite.app.CronosKeeper.SetAutoContractForDenom(suite.ctx, CorrectIbcDenom, common.HexToAddress("0x11"))
},
nil,
func() {
},
},
}

for _, tc := range testCases {
Expand Down
6 changes: 6 additions & 0 deletions x/cronos/keeper/mock/ibckeeper_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,11 @@ func (i IbcKeeperMock) GetDenomTrace(ctx sdk.Context, denomTraceHash tmbytes.Hex
BaseDenom: "basetcro",
}, true
}
if denomTraceHash.String() == "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" {
return types.DenomTrace{
Path: "transfer/channel-0",
BaseDenom: "correctIBCToken",
}, true
}
return types.DenomTrace{}, false
}