Skip to content

Commit

Permalink
implement SwapToERC20
Browse files Browse the repository at this point in the history
  • Loading branch information
dreamer-zq committed Apr 15, 2024
1 parent e1f4d7e commit 7554d5f
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 3 deletions.
98 changes: 98 additions & 0 deletions modules/token/keeper/erc20.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,104 @@ func (k Keeper) SwapFromERC20(
return nil
}

// SwapToERC20 executes a swap from a native token to its ERC20 token counterpart
//
// Parameters:
// - ctx: the context
// - sender: the sender of the amount
// - receiver: the receiver of the erc20 token
// - amount: the amount to be swapped
//
// Returns:
// - error: error if any.
func (k Keeper) SwapToERC20(
ctx sdk.Context,
sender sdk.AccAddress,
receiver common.Address,
amount sdk.Coin,
) error {
receiverAcc := k.accountKeeper.GetAccount(ctx, sdk.AccAddress(receiver.Bytes()))
if receiverAcc != nil {
if !k.evmKeeper.SupportedKey(receiverAcc.GetPubKey()) {
return errorsmod.Wrapf(types.ErrUnsupportedKey, "key %s", receiverAcc.GetPubKey())
}
}

token, err := k.getTokenByMinUnit(ctx, amount.Denom)
if err != nil {
return err
}
if len(token.Contract) == 0 {
return errorsmod.Wrapf(types.ErrERC20NotDeployed, "token: %s is not bound to the corresponding erc20 token", amount.Denom)
}
contract := common.HexToAddress(token.Contract)

amt := sdk.NewCoins(amount)
if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amt); err != nil {
return err
}

if err := k.bankKeeper.BurnCoins(ctx, types.ModuleName, amt); err != nil {
return err
}

if err := k.MintERC20(ctx, contract, receiver, amount.Amount.Uint64()); err != nil {
return err
}

ctx.EventManager().EmitTypedEvent(&v1.EventSwapToERC20{
Amount: amount,
Sender: sender.String(),
Receiver: receiver.String(),
ToContract: token.Contract,
})
return nil
}

// MintERC20 mints ERC20 tokens to an account.
//
// Parameters:
// - ctx: the sdk.Context for the function
// - contract: the address of the contract
// - to: the address of the receiver
// - amount: the amount to mint
//
// Returns:
// - err : error if any
func (k Keeper) MintERC20(
ctx sdk.Context,
contract, to common.Address,
amount uint64,
) error {
balanceBefore := k.BalanceOf(ctx, contract, to)

abi := contracts.ERC20TokenContract.ABI
res, err := k.CallEVM(ctx, abi, k.moduleAddress(), contract, true, contracts.MethodMint, to, amount)
if err != nil {
return err
}

if res.Failed() {
return errorsmod.Wrapf(
types.ErrVMExecution, "failed to mint contract: %s, reason: %s",
contract.String(),
res.Revert(),
)
}

balanceAfter := k.BalanceOf(ctx, contract, to)
expectBalance := big.NewInt(0).Add(balanceBefore, big.NewInt(int64(amount)))
if r := expectBalance.Cmp(balanceAfter); r != 0 {
return errorsmod.Wrapf(
types.ErrVMExecution, "failed to mint token correctly, expected after-mint amount is incorrect: %s, expected %d, actual %d",
contract.String(),
expectBalance.Int64(),
balanceAfter.Int64(),
)
}
return nil
}

// BurnERC20 burns a specific amount of ERC20 tokens from a given contract and address.
//
// Parameters:
Expand Down
23 changes: 21 additions & 2 deletions modules/token/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"context"
"encoding/hex"

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -325,6 +326,24 @@ func (m msgServer) SwapFromERC20(goCtx context.Context, msg *v1.MsgSwapFromERC20
}

// SwapToERC20 implements v1.MsgServer.
func (m msgServer) SwapToERC20(context.Context, *v1.MsgSwapToERC20) (*v1.MsgSwapToERC20Response, error) {
panic("unimplemented")
func (m msgServer) SwapToERC20(goCtx context.Context, msg *v1.MsgSwapToERC20) (*v1.MsgSwapToERC20Response, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
sender, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
return nil, err
}

receiver := common.BytesToAddress(sender.Bytes())
if len(msg.Receiver) > 0 {
bz, err := hex.DecodeString(msg.Receiver)
if err != nil {
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "expecting a hex address of 0x, got %s", msg.Receiver)
}
receiver = common.BytesToAddress(bz)
}

if err := m.k.SwapToERC20(ctx, sender, receiver, msg.Amount); err != nil {
return nil, err
}
return &v1.MsgSwapToERC20Response{}, nil
}
20 changes: 19 additions & 1 deletion modules/token/types/v1/msgs.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package v1

import (
"encoding/hex"
fmt "fmt"
"regexp"

Expand Down Expand Up @@ -418,7 +419,24 @@ func (m *MsgSwapFromERC20) GetSigners() []sdk.AccAddress {

// ValidateBasic implements Msg
func (m *MsgSwapToERC20) ValidateBasic() error {
// TODO
if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil {
return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid sender address (%s)", err)
}

if len(m.Receiver) > 0 {
_, err := hex.DecodeString(m.Receiver)
if err != nil {
return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "expecting a hex address of 0x, got %s", m.Receiver)
}
}

if !m.Amount.IsValid() {
return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, m.Amount.String())
}

if !m.Amount.IsPositive() {
return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, m.Amount.String())
}
return nil
}

Expand Down

0 comments on commit 7554d5f

Please sign in to comment.