Skip to content

Commit 07f2a66

Browse files
vladjdkreecepbcupsjoelsmith-2019Eric-Warehime
authored
feat: add convert coin endpoint (#85)
* feat(erc20): add convert coin endpoint Co-Authored-By: Joel Smith <joelsmith.2019@gmail.com> * lints * Update proto/cosmos/evm/erc20/v1/tx.proto Co-authored-by: Eric Warehime <eric.warehime@gmail.com> * return error on negative amount * NewMsgConvertCoin * add negative error * msg validation testing * keeper call testing * lints --------- Co-authored-by: Reece Williams <reecepbcups@gmail.com> Co-authored-by: Joel Smith <joelsmith.2019@gmail.com> Co-authored-by: Eric Warehime <eric.warehime@gmail.com>
1 parent 3075d9f commit 07f2a66

File tree

11 files changed

+858
-149
lines changed

11 files changed

+858
-149
lines changed

api/cosmos/evm/erc20/v1/tx.pulsar.go

Lines changed: 110 additions & 96 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/cosmos/evm/erc20/v1/tx_grpc.pb.go

Lines changed: 41 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

proto/cosmos/evm/erc20/v1/tx.proto

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ package cosmos.evm.erc20.v1;
44

55
import "amino/amino.proto";
66
import "cosmos/base/v1beta1/coin.proto";
7+
import "cosmos/evm/erc20/v1/genesis.proto";
78
import "cosmos/msg/v1/msg.proto";
89
import "cosmos_proto/cosmos.proto";
910
import "gogoproto/gogo.proto";
1011
import "google/api/annotations.proto";
11-
import "cosmos/evm/erc20/v1/genesis.proto";
1212

1313
option go_package = "github.com/cosmos/evm/x/erc20/types";
1414

@@ -20,6 +20,11 @@ service Msg {
2020
rpc ConvertERC20(MsgConvertERC20) returns (MsgConvertERC20Response) {
2121
option (google.api.http).get = "/cosmos/evm/erc20/v1/tx/convert_erc20";
2222
};
23+
// ConvertCoin mints a ERC20 token representation of the native Cosmos coin
24+
// that is registered on the token mapping.
25+
rpc ConvertCoin(MsgConvertCoin) returns (MsgConvertCoinResponse) {
26+
option (google.api.http).get = "/cosmos/evm/erc20/v1/tx/convert_coin";
27+
}
2328
// UpdateParams defines a governance operation for updating the x/erc20 module
2429
// parameters. The authority is hard-coded to the Cosmos SDK x/gov module
2530
// account
@@ -60,6 +65,8 @@ message MsgConvertERC20Response {}
6065

6166
// MsgConvertCoin defines a Msg to convert a native Cosmos coin to a ERC20 token
6267
message MsgConvertCoin {
68+
option (amino.name) = "cosmos/evm/x/erc20/MsgConvertCoin";
69+
option (cosmos.msg.v1.signer) = "sender";
6370
// coin is a Cosmos coin whose denomination is registered in a token pair. The
6471
// coin amount defines the amount of coins to convert.
6572
cosmos.base.v1beta1.Coin coin = 1 [ (gogoproto.nullable) = false ];

x/erc20/client/cli/tx.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func NewTxCmd() *cobra.Command {
2828
}
2929

3030
txCmd.AddCommand(
31+
NewConvertCoinCmd(),
3132
NewConvertERC20Cmd(),
3233
)
3334
return txCmd
@@ -79,3 +80,50 @@ func NewConvertERC20Cmd() *cobra.Command {
7980
flags.AddTxFlagsToCmd(cmd)
8081
return cmd
8182
}
83+
84+
// NewConvertCoinCmd returns a CLI command handler for converting a Cosmos coin
85+
func NewConvertCoinCmd() *cobra.Command {
86+
cmd := &cobra.Command{
87+
Use: "convert-coin COIN [RECEIVER_HEX]",
88+
Short: "Convert a Cosmos coin to ERC20. When the receiver [optional] is omitted, the ERC20 tokens are transferred to the sender.",
89+
Args: cobra.RangeArgs(1, 2),
90+
RunE: func(cmd *cobra.Command, args []string) error {
91+
cliCtx, err := client.GetClientTxContext(cmd)
92+
if err != nil {
93+
return err
94+
}
95+
96+
coin, err := sdk.ParseCoinNormalized(args[0])
97+
if err != nil {
98+
return err
99+
}
100+
101+
var receiver string
102+
sender := cliCtx.GetFromAddress()
103+
104+
if len(args) == 2 {
105+
receiver = args[1]
106+
if err := cosmosevmtypes.ValidateAddress(receiver); err != nil {
107+
return fmt.Errorf("invalid receiver hex address %w", err)
108+
}
109+
} else {
110+
receiver = common.BytesToAddress(sender).Hex()
111+
}
112+
113+
msg := &types.MsgConvertCoin{
114+
Coin: coin,
115+
Receiver: receiver,
116+
Sender: sender.String(),
117+
}
118+
119+
if err := msg.ValidateBasic(); err != nil {
120+
return err
121+
}
122+
123+
return tx.GenerateOrBroadcastTxCLI(cliCtx, cmd.Flags(), msg)
124+
},
125+
}
126+
127+
flags.AddTxFlagsToCmd(cmd)
128+
return cmd
129+
}

x/erc20/keeper/msg_server.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,46 @@ func (k Keeper) convertERC20IntoCoinsForNativeToken(
186186
return &types.MsgConvertERC20Response{}, nil
187187
}
188188

189+
// ConvertCoin converts native Cosmos coins into ERC20 tokens for both
190+
// Cosmos-native and ERC20 TokenPair Owners
191+
func (k Keeper) ConvertCoin(
192+
goCtx context.Context,
193+
msg *types.MsgConvertCoin,
194+
) (*types.MsgConvertCoinResponse, error) {
195+
ctx := sdk.UnwrapSDKContext(goCtx)
196+
197+
// Error checked during msg validation
198+
sender := sdk.MustAccAddressFromBech32(msg.Sender)
199+
receiver := common.HexToAddress(msg.Receiver)
200+
201+
pair, err := k.MintingEnabled(ctx, sender, receiver.Bytes(), msg.Coin.Denom)
202+
if err != nil {
203+
return nil, err
204+
}
205+
206+
// Check ownership and execute conversion
207+
switch {
208+
case pair.IsNativeERC20():
209+
// Remove token pair if contract is suicided
210+
acc := k.evmKeeper.GetAccountWithoutBalance(ctx, pair.GetERC20Contract())
211+
if acc == nil || !acc.IsContract() {
212+
k.DeleteTokenPair(ctx, pair)
213+
k.Logger(ctx).Debug(
214+
"deleting selfdestructed token pair from state",
215+
"contract", pair.Erc20Address,
216+
)
217+
// NOTE: return nil error to persist the changes from the deletion
218+
return nil, nil
219+
}
220+
221+
return nil, k.ConvertCoinNativeERC20(ctx, pair, msg.Coin.Amount, receiver, sender)
222+
case pair.IsNativeCoin():
223+
return nil, types.ErrNativeConversionDisabled
224+
}
225+
226+
return nil, types.ErrUndefinedOwner
227+
}
228+
189229
// ConvertCoinNativeERC20 handles the coin conversion for a native ERC20 token
190230
// pair:
191231
// - escrow Coins on module account
@@ -201,7 +241,7 @@ func (k Keeper) ConvertCoinNativeERC20(
201241
sender sdk.AccAddress,
202242
) error {
203243
if !amount.IsPositive() {
204-
return nil
244+
return sdkerrors.Wrap(types.ErrNegativeToken, "converted coin amount must be positive")
205245
}
206246

207247
erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI

0 commit comments

Comments
 (0)