Skip to content

Commit

Permalink
finish erc20 feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Dreamer committed Apr 12, 2024
1 parent 63fa3f7 commit 72d978d
Show file tree
Hide file tree
Showing 14 changed files with 3,687 additions and 142 deletions.
2,175 changes: 2,175 additions & 0 deletions api/irismod/token/v1/event.pulsar.go

Large diffs are not rendered by default.

20 changes: 11 additions & 9 deletions api/irismod/token/v1/tx.pulsar.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion modules/token/depinject.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ type TokenInputs struct {

AccountKeeper types.AccountKeeper
BankKeeper types.BankKeeper
EVMKeeper types.EVMKeeper
EVMKeeper types.EVMKeeper
ICS20Keeper types.ICS20Keeper

// LegacySubspace is used solely for migration of x/params managed parameters
LegacySubspace exported.Subspace `optional:"true"`
Expand All @@ -71,6 +72,7 @@ func ProvideModule(in TokenInputs) TokenOutputs {
in.BankKeeper,
in.AccountKeeper,
in.EVMKeeper,
in.ICS20Keeper,
in.Config.FeeCollectorName,
authority.String(),
)
Expand Down
155 changes: 152 additions & 3 deletions modules/token/keeper/erc20.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,124 @@ import (
"math/big"

errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"

"github.com/irisnet/irismod/contracts"
"github.com/irisnet/irismod/modules/token/types"
v1 "github.com/irisnet/irismod/modules/token/types/v1"
)

// SwapToERC20 executes a swap from a native token to an ERC20 token
//
// 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 not deployed", 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.MintERC20(ctx, contract, receiver, amount.Amount.Uint64()); err != nil {
return err
}

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

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

// SwapFromERC20 executes a swap from an ERC20 token to a native token.
//
// Parameters:
//
// ctx - the context in which the swap is executed
// sender - the address of the sender
// receiver - the address of the receiver
// contract - the address of the ERC20 contract
// amount - the amount of tokens to swap
//
// Return type: error
func (k Keeper) SwapFromERC20(
ctx sdk.Context,
sender common.Address,
receiver sdk.AccAddress,
contract common.Address,
amount *big.Int,
) error {
token, err := k.getTokenByContract(ctx, contract.String())
if err != nil {
return err
}

balance := k.BalanceOf(ctx, contract, sender)
if r := balance.Cmp(amount); r < 0 {
return errorsmod.Wrapf(
sdkerrors.ErrInsufficientFunds,
"balance: %d, swap: %d",
balance,
amount,
)
}
if err := k.BurnERC20(ctx, contract, sender, amount.Uint64()); err != nil {
return err
}

amt := sdk.NewCoins(sdk.NewCoin(token.MinUnit, sdkmath.NewIntFromBigInt(amount)))
if err := k.bankKeeper.MintCoins(ctx, types.ModuleName, amt); err != nil {
return err
}

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

ctx.EventManager().EmitTypedEvent(&v1.EventSwapFromERC20{
Contract: contract.String(),
Amount: amount.Int64(),
Sender: sender.String(),
Receiver: receiver.String(),
})
return nil
}

// DeployERC20 deploys an ERC20 token contract.
//
// Parameters:
Expand All @@ -27,6 +137,7 @@ func (k Keeper) DeployERC20(
ctx sdk.Context,
name string,
symbol string,
minUnit string,
scale int8,
) (common.Address, error) {
contractArgs, err := contracts.ERC20TokenContract.ABI.Pack(
Expand All @@ -49,10 +160,21 @@ func (k Keeper) DeployERC20(
return common.Address{}, err
}
contractAddr := crypto.CreateAddress(deployer, nonce)
_, err = k.CallEVMWithData(ctx, deployer, nil, data, true)
result, err := k.CallEVMWithData(ctx, deployer, nil, data, true)
if err != nil {
return common.Address{}, errorsmod.Wrapf(err, "failed to deploy contract for %s", name)
}
if result.Failed() {
return common.Address{}, errorsmod.Wrapf(types.ErrVMExecution, "failed to deploy contract for %s, reason: %s", name, result.Revert())
}

ctx.EventManager().EmitTypedEvent(&v1.EventDeployERC20{
Symbol: symbol,
Name: name,
Scale: uint32(scale),
MinUnit: minUnit,
Contract: contractAddr.String(),
})
return contractAddr, nil
}

Expand Down Expand Up @@ -103,14 +225,30 @@ func (k Keeper) MintERC20(
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, "mint", to, amount)
if err != nil {
return err
}

if res.Failed() {
return errorsmod.Wrapf(types.ErrVMExecution, "failed to mint %d", amount)
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 contract: %s, expect %d, actual %d, ",
contract.String(),
expectBalance.Int64(),
balanceAfter.Int64(),
)
}
return nil
}
Expand All @@ -129,6 +267,7 @@ func (k Keeper) BurnERC20(
contract, from common.Address,
amount uint64,
) error {
balanceBefore := k.BalanceOf(ctx, contract, from)
abi := contracts.ERC20TokenContract.ABI
res, err := k.CallEVM(ctx, abi, k.moduleAddress(), contract, true, "burn", from, amount)
if err != nil {
Expand All @@ -138,10 +277,20 @@ func (k Keeper) BurnERC20(
if res.Failed() {
return errorsmod.Wrapf(types.ErrVMExecution, "failed to burn %d", amount)
}

balanceAfter := k.BalanceOf(ctx, contract, from)
expectBalance := big.NewInt(0).Sub(balanceBefore, big.NewInt(int64(amount)))
if r := expectBalance.Cmp(balanceAfter); r != 0 {
return errorsmod.Wrapf(
types.ErrVMExecution, "failed to burn contract: %s, expect %d, actual %d, ",
contract.String(),
expectBalance.Int64(),
balanceAfter.Int64(),
)
}
return nil
}


func (k Keeper) moduleAddress() common.Address {
moduleAddr := k.accountKeeper.GetModuleAddress(types.ModuleName)
return common.BytesToAddress(moduleAddr.Bytes())
Expand Down
16 changes: 16 additions & 0 deletions modules/token/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,39 @@ import (
v1 "github.com/irisnet/irismod/modules/token/types/v1"
)

// Keeper of the token store
type Keeper struct {
storeKey storetypes.StoreKey
cdc codec.Codec
bankKeeper types.BankKeeper
accountKeeper types.AccountKeeper
evmKeeper types.EVMKeeper
ics20Keeper types.ICS20Keeper
blockedAddrs map[string]bool
feeCollectorName string
authority string
registry v1.SwapRegistry
}

// NewKeeper creates a new instance of Keeper.
//
// Parameters:
// cdc: codec to marshal/unmarshal binary encoding/decoding.
// key: store key for the module's store.
// bankKeeper: bank Keeper module for interacting with accounts.
// accountKeeper: Account Keeper for interacting with accounts.
// evmKeeper: EVM Keeper module for interacting with Ethereum Virtual Machine transactions.
// ics20Keeper: ICS20 Keeper module for interacting with ICS20 transactions.
// feeCollectorName: name of the fee collector.
// authority: authority string.
// Return type: Keeper.
func NewKeeper(
cdc codec.Codec,
key storetypes.StoreKey,
bankKeeper types.BankKeeper,
accountKeeper types.AccountKeeper,
evmKeeper types.EVMKeeper,
ics20Keeper types.ICS20Keeper,
feeCollectorName string,
authority string,
) Keeper {
Expand All @@ -42,6 +57,7 @@ func NewKeeper(
bankKeeper: bankKeeper,
accountKeeper: accountKeeper,
evmKeeper: evmKeeper,
ics20Keeper: ics20Keeper,
feeCollectorName: feeCollectorName,
blockedAddrs: bankKeeper.GetBlockedAddresses(),
registry: make(v1.SwapRegistry),
Expand Down
Loading

0 comments on commit 72d978d

Please sign in to comment.