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

feat(oracle): register new token and set up feeder through assets-module #159

Merged
Merged
Show file tree
Hide file tree
Changes from 3 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
3 changes: 2 additions & 1 deletion precompiles/assets/IAssets.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ interface IAssets {
uint8 decimals,
uint256 tvlLimit,
string calldata name,
string calldata metaData
string calldata metaData,
string calldata oracleInfo
) external returns (bool success, bool updated);

/// QUERIES
Expand Down
111 changes: 58 additions & 53 deletions precompiles/assets/abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,60 +156,65 @@
{
"name": "metaData",
"type": "string",
"internalType": "string"
}
],
"outputs": [
{
"name": "success",
"type": "bool",
"internalType": "bool"
},
{
"name": "updated",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
"internalType": "string"
},
{
"name": "oracleInfo",
"type": "string",
"internalType": "string"
}
],
"outputs": [
{
"name": "success",
"type": "bool",
"internalType": "bool"
},
{
"name": "updated",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "withdrawPrincipal",
"inputs": [
{
"name": "clientChainID",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "assetsAddress",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "withdrawAddress",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "opAmount",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "success",
"type": "bool",
"internalType": "bool"
},
{
"name": "latestAssetState",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "nonpayable"
"type": "function",
"name": "withdrawPrincipal",
"inputs": [
{
"name": "clientChainID",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "assetsAddress",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "withdrawAddress",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "opAmount",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "success",
"type": "bool",
"internalType": "bool"
},
{
"name": "latestAssetState",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "nonpayable"
}
]
2 changes: 1 addition & 1 deletion precompiles/assets/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (p Precompile) RequiredGas(input []byte) uint64 {
return p.Precompile.RequiredGas(input, p.IsTransaction(method.Name))
}

// Run executes the precompiled contract deposit methods defined in the ABI.
// Run executes the precompiled contract methods defined in the ABI.
func (p Precompile) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) (bz []byte, err error) {
ctx, stateDB, method, initialGas, args, err := p.RunSetup(evm, contract, readOnly, p.IsTransaction)
if err != nil {
Expand Down
33 changes: 25 additions & 8 deletions precompiles/assets/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const (
MethodIsRegisteredClientChain = "isRegisteredClientChain"
)

// var oracleInfoMatcher = regexp.MustCompile(`oracle_info:{chain:(.+),\s*token:(.+),\s*decimal:(\d+)(,\s*interval:(.+))?(,\s*contract:(.+))?}`)

// DepositOrWithdraw deposit and withdraw the client chain assets for the staker,
// that will change the state in assets module.
func (p Precompile) DepositOrWithdraw(
Expand Down Expand Up @@ -129,23 +131,38 @@ func (p Precompile) RegisterOrUpdateTokens(
}

// parse inputs
asset, err := p.TokenFromInputs(ctx, args)
asset, oInfo, err := p.TokenFromInputs(ctx, args)
if err != nil {
return nil, err
}

// the price feed must exist
_, assetID := assetstypes.GetStakeIDAndAssetIDFromStr(asset.LayerZeroChainID, "", asset.Address)
if _, err := p.assetsKeeper.GetSpecifiedAssetsPrice(ctx, assetID); err != nil {
return nil, err
oInfo.AssetID = assetID
updated := false
stakingAsset, _ := p.assetsKeeper.GetStakingAssetInfo(ctx, assetID)
if stakingAsset != nil {
// this is for update
if asset.TotalSupply.IsPositive() {
stakingAsset.AssetBasicInfo.TotalSupply = asset.TotalSupply
}
if len(asset.MetaInfo) > 0 {
stakingAsset.AssetBasicInfo.MetaInfo = asset.MetaInfo
}
updated = true
} else {
stakingAsset = &assetstypes.StakingAssetInfo{
AssetBasicInfo: &asset,
StakingTotalAmount: sdkmath.NewInt(0),
}

if err := p.assetsKeeper.RegisterNewTokenAndSetTokenFeeder(ctx, &oInfo); err != nil {
return nil, err
}
}

updated := p.assetsKeeper.IsStakingAsset(ctx, assetID)
// this is where the magic happens
if err := p.assetsKeeper.SetStakingAssetInfo(ctx, &assetstypes.StakingAssetInfo{
AssetBasicInfo: &asset,
StakingTotalAmount: sdkmath.NewInt(0),
}); err != nil {
if err := p.assetsKeeper.SetStakingAssetInfo(ctx, stakingAsset); err != nil {
return nil, err
}

Expand Down
56 changes: 42 additions & 14 deletions precompiles/assets/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package assets

import (
"encoding/json"
"errors"
"fmt"
"math/big"

Expand All @@ -10,6 +12,7 @@ import (
exocmn "github.com/ExocoreNetwork/exocore/precompiles/common"
assetskeeper "github.com/ExocoreNetwork/exocore/x/assets/keeper"
"github.com/ExocoreNetwork/exocore/x/assets/types"
oracletypes "github.com/ExocoreNetwork/exocore/x/oracle/types"
sdk "github.com/cosmos/cosmos-sdk/types"
cmn "github.com/evmos/evmos/v14/precompiles/common"
)
Expand Down Expand Up @@ -108,63 +111,88 @@ func (p Precompile) ClientChainInfoFromInputs(_ sdk.Context, args []interface{})
return &clientChain, nil
}

func (p Precompile) TokenFromInputs(ctx sdk.Context, args []interface{}) (types.AssetInfo, error) {
func (p Precompile) TokenFromInputs(ctx sdk.Context, args []interface{}) (asset types.AssetInfo, oInfo oracletypes.OracleInfo, err error) {
inputsLen := len(p.ABI.Methods[MethodRegisterOrUpdateTokens].Inputs)
if len(args) != inputsLen {
return types.AssetInfo{}, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, inputsLen, len(args))
err = fmt.Errorf(cmn.ErrInvalidNumberOfArgs, inputsLen, len(args))
return asset, oInfo, err
}
asset := types.AssetInfo{}
clientChainID, ok := args[0].(uint32)
if !ok {
return types.AssetInfo{}, fmt.Errorf(exocmn.ErrContractInputParaOrType, 0, "uint32", args[0])
err = fmt.Errorf(exocmn.ErrContractInputParaOrType, 0, "uint32", args[0])
return asset, oInfo, err
}
asset.LayerZeroChainID = uint64(clientChainID)
info, err := p.assetsKeeper.GetClientChainInfoByIndex(ctx, asset.LayerZeroChainID)
if err != nil {
return types.AssetInfo{}, err
return asset, oInfo, err
}
clientChainAddrLength := info.AddressLength

assetAddr, ok := args[1].([]byte)
if !ok || assetAddr == nil {
return types.AssetInfo{}, fmt.Errorf(exocmn.ErrContractInputParaOrType, 1, "[]byte", args[1])
err = fmt.Errorf(exocmn.ErrContractInputParaOrType, 1, "[]byte", args[1])
return asset, oInfo, err
}
if uint32(len(assetAddr)) < clientChainAddrLength {
return types.AssetInfo{}, fmt.Errorf(exocmn.ErrInvalidAddrLength, len(assetAddr), clientChainAddrLength)
err = fmt.Errorf(exocmn.ErrInvalidAddrLength, len(assetAddr), clientChainAddrLength)
return asset, oInfo, err
}
asset.Address = hexutil.Encode(assetAddr[:clientChainAddrLength])

decimal, ok := args[2].(uint8)
if !ok {
return types.AssetInfo{}, fmt.Errorf(exocmn.ErrContractInputParaOrType, 2, "uint8", args[2])
err = fmt.Errorf(exocmn.ErrContractInputParaOrType, 2, "uint8", args[2])
return asset, oInfo, err
}
asset.Decimals = uint32(decimal)

tvlLimit, ok := args[3].(*big.Int)
if !ok || tvlLimit == nil || !(tvlLimit.Cmp(big.NewInt(0)) == 1) {
return types.AssetInfo{}, fmt.Errorf(exocmn.ErrContractInputParaOrType, 3, "*big.Int", args[3])
err = fmt.Errorf(exocmn.ErrContractInputParaOrType, 3, "*big.Int", args[3])
return asset, oInfo, err
}
asset.TotalSupply = sdkmath.NewIntFromBigInt(tvlLimit)

name, ok := args[4].(string)
if !ok {
return types.AssetInfo{}, fmt.Errorf(exocmn.ErrContractInputParaOrType, 4, "string", args[4])
err = fmt.Errorf(exocmn.ErrContractInputParaOrType, 4, "string", args[4])
return asset, oInfo, err
}
if name == "" || len(name) > types.MaxChainTokenNameLength {
return types.AssetInfo{}, fmt.Errorf(exocmn.ErrInvalidNameLength, name, len(name), types.MaxChainTokenNameLength)
err = fmt.Errorf(exocmn.ErrInvalidNameLength, name, len(name), types.MaxChainTokenNameLength)
return asset, oInfo, err
}
asset.Name = name

metaInfo, ok := args[5].(string)
if !ok {
return types.AssetInfo{}, fmt.Errorf(exocmn.ErrContractInputParaOrType, 5, "string", args[5])
err = fmt.Errorf(exocmn.ErrContractInputParaOrType, 5, "string", args[5])
return asset, oInfo, err
}
if metaInfo == "" || len(metaInfo) > types.MaxChainTokenMetaInfoLength {
return types.AssetInfo{}, fmt.Errorf(exocmn.ErrInvalidMetaInfoLength, metaInfo, len(metaInfo), types.MaxChainTokenMetaInfoLength)
err = fmt.Errorf(exocmn.ErrInvalidMetaInfoLength, metaInfo, len(metaInfo), types.MaxChainTokenMetaInfoLength)
return asset, oInfo, err
}
asset.MetaInfo = metaInfo

return asset, nil
oInfoStr, ok := args[6].(string)
if !ok {
err = fmt.Errorf(exocmn.ErrContractInputParaOrType, 6, "string", args[6])
return asset, oInfo, err
}

if err = json.Unmarshal([]byte(oInfoStr), &oInfo); err != nil {
return asset, oInfo, err
}
if len(oInfo.Token.Name) == 0 ||
len(oInfo.Token.Chain.Name) == 0 ||
len(oInfo.Token.Decimal) == 0 {
err = errors.New(exocmn.ErrInvalidOracleInfo)
return asset, oInfo, err
}

return asset, oInfo, err
}

func (p Precompile) ClientChainIDFromInputs(_ sdk.Context, args []interface{}) (uint32, error) {
Expand Down
2 changes: 2 additions & 0 deletions precompiles/common/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ const (
ErrInvalidNameLength = "nil name or too long for chain or token,value:%s,actualLength:%d,max:%d"

ErrInvalidEVMAddr = "the address is an invalid EVM address, addr:%s"

ErrInvalidOracleInfo = "oracle info is invalid, need at least three fields not empty: token.Name, token.Chain.Name, token.Decimal"
)
1 change: 1 addition & 0 deletions x/assets/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ import (

type OracleKeeper interface {
GetSpecifiedAssetsPrice(ctx sdk.Context, assetID string) (oracletypes.Price, error)
RegisterNewTokenAndSetTokenFeeder(ctx sdk.Context, oInfo *oracletypes.OracleInfo) error
}
Loading
Loading