/
conversion_cosmos_native.go
107 lines (92 loc) · 3.43 KB
/
conversion_cosmos_native.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package keeper
import (
"fmt"
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/incubus-network/nemo/x/evmutil/types"
)
// ConvertCosmosCoinToERC20 locks the initiator's sdk.Coin in the module account
// and mints the receiver a corresponding amount of an ERC20 representing the Coin.
// If a conversion has never been made before and no contract exists, one will be deployed.
// Only denoms registered to the AllowedCosmosDenoms param may be converted.
func (k *Keeper) ConvertCosmosCoinToERC20(
ctx sdk.Context,
initiator sdk.AccAddress,
receiver types.InternalEVMAddress,
amount sdk.Coin,
) error {
// check that the conversion is allowed
tokenInfo, allowed := k.GetAllowedTokenMetadata(ctx, amount.Denom)
if !allowed {
return errorsmod.Wrapf(types.ErrSDKConversionNotEnabled, amount.Denom)
}
// send coins from initiator to the module account
// do this before possible contract deploy to prevent unnecessary store interactions
err := k.bankKeeper.SendCoinsFromAccountToModule(
ctx, initiator, types.ModuleName, sdk.NewCoins(amount),
)
if err != nil {
return err
}
// find deployed contract if it exits
contractAddress, err := k.GetOrDeployCosmosCoinERC20Contract(ctx, tokenInfo)
if err != nil {
return err
}
// mint erc20 tokens for the user
err = k.MintERC20(ctx, contractAddress, receiver, amount.Amount.BigInt())
if err != nil {
return err
}
ctx.EventManager().EmitEvent(sdk.NewEvent(
types.EventTypeConvertCosmosCoinToERC20,
sdk.NewAttribute(types.AttributeKeyInitiator, initiator.String()),
sdk.NewAttribute(types.AttributeKeyReceiver, receiver.String()),
sdk.NewAttribute(types.AttributeKeyERC20Address, contractAddress.Hex()),
sdk.NewAttribute(types.AttributeKeyAmount, amount.String()),
))
return nil
}
// ConvertCosmosCoinFromERC20 burns the ERC20 wrapper of the cosmos coin and
// sends the underlying sdk coin form the module account to the receiver.
func (k *Keeper) ConvertCosmosCoinFromERC20(
ctx sdk.Context,
initiator types.InternalEVMAddress,
receiver sdk.AccAddress,
coin sdk.Coin,
) error {
amount := coin.Amount.BigInt()
// get deployed contract
contractAddress, found := k.GetDeployedCosmosCoinContract(ctx, coin.Denom)
if !found {
// no contract deployed
return errorsmod.Wrapf(types.ErrInvalidCosmosDenom, fmt.Sprintf("no erc20 contract found for %s", coin.Denom))
}
// verify sufficient balance
balance, err := k.QueryERC20BalanceOf(ctx, contractAddress, initiator)
if err != nil {
return errorsmod.Wrapf(types.ErrEVMCall, "failed to retrieve balance %s", err.Error())
}
if balance.Cmp(amount) == -1 {
return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, "failed to convert to cosmos coins")
}
// burn initiator's ERC20 tokens
err = k.BurnERC20(ctx, contractAddress, initiator, amount)
if err != nil {
return err
}
// send sdk coins to receiver, unlocking them from the module account
err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, receiver, sdk.NewCoins(coin))
if err != nil {
return err
}
ctx.EventManager().EmitEvent(sdk.NewEvent(
types.EventTypeConvertCosmosCoinFromERC20,
sdk.NewAttribute(types.AttributeKeyInitiator, initiator.String()),
sdk.NewAttribute(types.AttributeKeyReceiver, receiver.String()),
sdk.NewAttribute(types.AttributeKeyERC20Address, contractAddress.Hex()),
sdk.NewAttribute(types.AttributeKeyAmount, coin.String()),
))
return nil
}