Skip to content

Commit

Permalink
[ioctl][ioid][#1] ioid sub commands (#4284)
Browse files Browse the repository at this point in the history
* feat(ioctl/ioid): ioctl support ioid interactions

* chore: remove redundent abis

* fix: transfer amount flag type change to string
  • Loading branch information
saitofun authored and dustinxie committed May 30, 2024
1 parent 9245f0a commit c39e507
Show file tree
Hide file tree
Showing 12 changed files with 644 additions and 18 deletions.
222 changes: 222 additions & 0 deletions ioctl/cmd/ioid/applyioid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package ioid

import (
"encoding/hex"
"math/big"
"strings"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/iotexproject/iotex-address/address"
"github.com/iotexproject/iotex-core/ioctl/cmd/action"
"github.com/iotexproject/iotex-core/ioctl/config"
"github.com/iotexproject/iotex-core/ioctl/output"
"github.com/spf13/cobra"
)

// Multi-language support
var (
_applyUsages = map[config.Language]string{
config.English: "apply",
config.Chinese: "apply",
}
_applyShorts = map[config.Language]string{
config.English: "Apply ioIDs",
config.Chinese: "申请 ioID",
}
_ioIDStoreUsages = map[config.Language]string{
config.English: "ioID store contract address",
config.Chinese: "ioID Store 合约地址",
}
_projectIdUsages = map[config.Language]string{
config.English: "project id",
config.Chinese: "项目Id",
}
_amountUsages = map[config.Language]string{
config.English: "amount",
config.Chinese: "数量",
}
)

// _applyCmd represents the ioID apply command
var _applyCmd = &cobra.Command{
Use: config.TranslateInLang(_applyUsages, config.UILanguage),
Short: config.TranslateInLang(_applyShorts, config.UILanguage),
Args: cobra.MinimumNArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
err := apply()
return output.PrintError(err)
},
}

var (
ioIDStore string
projectId uint64
amount uint64
ioIDStoreABI = `[
{
"inputs": [],
"name": "price",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "project",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "projectAppliedAmount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "projectActivedAmount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "projectDeviceContract",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_projectId",
"type": "uint256"
},
{
"internalType": "address",
"name": "_contract",
"type": "address"
}
],
"name": "setDeviceContract",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_projectId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "applyIoIDs",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}
]`
)

func init() {
_applyCmd.Flags().StringVarP(
&ioIDStore, "ioIDStore", "i",
"0xA0C9f9A884cdAE649a42F16b057735Bc4fE786CD",
config.TranslateInLang(_ioIDStoreUsages, config.UILanguage),
)
_applyCmd.Flags().Uint64VarP(
&projectId, "projectId", "p", 0,
config.TranslateInLang(_projectIdUsages, config.UILanguage),
)
_applyCmd.Flags().Uint64VarP(
&amount, "amount", "a", 1,
config.TranslateInLang(_amountUsages, config.UILanguage),
)
}

func apply() error {
ioioIDStore, err := address.FromHex(ioIDStore)
if err != nil {
return output.NewError(output.AddressError, "failed to convert ioIDStore address", err)
}

ioIDStoreAbi, err := abi.JSON(strings.NewReader(ioIDStoreABI))
if err != nil {
return output.NewError(output.SerializationError, "failed to unmarshal abi", err)
}

data, err := ioIDStoreAbi.Pack("price")
if err != nil {
return output.NewError(output.ConvertError, "failed to pack price arguments", err)
}
priceHex, err := action.Read(ioioIDStore, "0", data)
if err != nil {
return output.NewError(output.APIError, "failed to read contract", err)
}
price, _ := hex.DecodeString(priceHex)
total := new(big.Int).Mul(new(big.Int).SetBytes(price), new(big.Int).SetUint64(amount))

data, err = ioIDStoreAbi.Pack("applyIoIDs", new(big.Int).SetUint64(projectId), new(big.Int).SetUint64(amount))
if err != nil {
return output.NewError(output.ConvertError, "failed to pack apply arguments", err)
}

return action.Execute(ioioIDStore.String(), total, data)
}
83 changes: 83 additions & 0 deletions ioctl/cmd/ioid/ioid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package ioid

import (
"context"
"time"

"github.com/cenkalti/backoff"
"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
"github.com/iotexproject/iotex-core/ioctl/config"
"github.com/iotexproject/iotex-core/ioctl/output"
"github.com/iotexproject/iotex-core/ioctl/util"
"github.com/iotexproject/iotex-proto/golang/iotexapi"
"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

// Multi-language support
var (
_ioIDCmdShorts = map[config.Language]string{
config.English: "Manage ioID",
config.Chinese: "管理 ioID",
}
_flagInsEndPointUsages = map[config.Language]string{
config.English: "set endpoint for once",
config.Chinese: "一次设置端点", // this translation
}
_flagInsInsecureUsages = map[config.Language]string{
config.English: "insecure connection for once",
config.Chinese: "一次不安全连接", // this translation
}
)

// IoIDCmd represents the ins command
var IoIDCmd = &cobra.Command{
Use: "ioid",
Short: config.TranslateInLang(_ioIDCmdShorts, config.UILanguage),
}

func init() {
IoIDCmd.AddCommand(_projectRegisterCmd)
IoIDCmd.AddCommand(_applyCmd)
IoIDCmd.AddCommand(_projectCmd)
IoIDCmd.AddCommand(_deviceCmd)
IoIDCmd.PersistentFlags().StringVar(&config.ReadConfig.Endpoint, "endpoint",
config.ReadConfig.Endpoint, config.TranslateInLang(_flagInsEndPointUsages,
config.UILanguage))
IoIDCmd.PersistentFlags().BoolVar(&config.Insecure, "insecure", config.Insecure,
config.TranslateInLang(_flagInsInsecureUsages, config.UILanguage))
}

func waitReceiptByActionHash(h string) (*iotexapi.GetReceiptByActionResponse, error) {
conn, err := util.ConnectToEndpoint(config.ReadConfig.SecureConnect && !config.Insecure)
if err != nil {
return nil, output.NewError(output.NetworkError, "failed to connect to endpoint", err)
}
defer conn.Close()
cli := iotexapi.NewAPIServiceClient(conn)
ctx := context.Background()

jwtMD, err := util.JwtAuth()
if err == nil {
ctx = metautils.NiceMD(jwtMD).ToOutgoing(ctx)
}

var rsp *iotexapi.GetReceiptByActionResponse
err = backoff.Retry(func() error {
rsp, err = cli.GetReceiptByAction(ctx, &iotexapi.GetReceiptByActionRequest{
ActionHash: h,
})
return err
}, backoff.WithMaxRetries(backoff.NewConstantBackOff(30*time.Second), 3))
if err != nil {
sta, ok := status.FromError(err)
if ok && sta.Code() == codes.NotFound {
return nil, output.NewError(output.APIError, "not found", nil)
} else if ok {
return nil, output.NewError(output.APIError, sta.Message(), nil)
}
return nil, output.NewError(output.NetworkError, "failed to invoke GetReceiptByAction api", err)
}
return rsp, nil
}
Loading

0 comments on commit c39e507

Please sign in to comment.