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

Can sign and send transactions to moonbeam? #399

Open
YuXiaoCoder opened this issue Aug 26, 2024 · 2 comments
Open

Can sign and send transactions to moonbeam? #399

YuXiaoCoder opened this issue Aug 26, 2024 · 2 comments

Comments

@YuXiaoCoder
Copy link

How do I sign and send a transaction to Moonbeam, I understand Polkadot's method!

// Transfer 转账 https://kusama.subscan.io/extrinsic?module=Balances&call=transfer_keep_alive
func (service *Service) Transfer(signer Signer, to types.AccountID, amount uint64) *types.Hash {
	// 获取元数据信息
	metadata := service.GetMetadata()
	if metadata == nil {
		return nil
	}

	toMultiAddress, err := types.NewMultiAddressFromAccountID(to.ToBytes())
	if err != nil {
		zap.S().Warnf("failed to get to [%v] multi address, err: %v", to.ToHexString(), err)
		return nil
	}

	// Transfer Call
	transferCall, err := types.NewCall(metadata, "Balances.transfer_keep_alive", toMultiAddress, types.NewUCompactFromUInt(amount))
	if err != nil {
		zap.S().Warnf("failed to new Balances.transfer_keep_alive call, err: %v", err)
		return nil
	}

	// Extrinsic
	extrinsic := types.NewExtrinsic(transferCall)

	if signer.IsProxy {
		proxyType := signer.ProxyType
		realAccount := signer.RealAccountId

		// 获取被代理账户的账户 ID
		realMultiAddress, err := types.NewMultiAddressFromAccountID(realAccount.ToBytes())
		if err != nil {
			zap.S().Warnf("failed to get real [%v] multi address, err: %v", realAccount.ToHexString(), err)
			return nil
		}

		call, err := types.NewCall(metadata, "Proxy.proxy", realMultiAddress, types.NewOption[byte](byte(proxyType)), transferCall)
		if err != nil {
			zap.S().Warnf("failed to new Proxy.proxy call, err: %v", err)
			return nil
		}

		// Extrinsic
		extrinsic = types.NewExtrinsic(call)
	}
	extrinsicByte, err := extrinsic.MarshalJSON()
	if err != nil {
		zap.S().Warnf("failed to unmarshal extrinsic, err: %v", err)
		return nil
	}
	zap.S().Infof("singer: %v, real account: %v, extrinsic: %v\n", signer.Origin.Address, signer.RealAccountId.ToHexString(), FormatExtrinsic(extrinsicByte))

	// 获取签名配置
	options := service.GetSignatureOptions(signer.Origin.PublicKey)
	if options == nil {
		return nil
	}

	// 对交易体签名
	err = extrinsic.Sign(signer.Origin, *options)
	if err != nil {
		zap.S().Warnf("failed to sign extrinsic, err: %v", err)
		return nil
	}

	// 提交交易并返回交易哈希
	txHash, err := service.Api.RPC.Author.SubmitExtrinsic(extrinsic)
	if err != nil {
		zap.S().Warnf("failed to submit extrinsic, err: %v", err)
		return nil
	}
	return &txHash
}

// 从助记词派生出密钥对
keypair, err := signature.KeyringPairFromSecret(mnemonic+derivationPath, uint16(networkId))
if err != nil {
        zap.S().Warnf("failed to derive key pair: %v", err)
        return nil
}
@cdamian
Copy link
Contributor

cdamian commented Aug 26, 2024

@YuXiaoCoder Please check the following issue for more details about signing and submitting transactions:
#389

@centrifuge centrifuge deleted a comment Aug 26, 2024
@YuXiaoCoder
Copy link
Author

YuXiaoCoder commented Aug 26, 2024

@YuXiaoCoder Please check the following issue for more details about signing and submitting transactions: #389

@cdamian

  1. How should I generate the KeyringPair for the moonbeam network, should I continue to use sr25519.Scheme{} or ecdsa..Scheme{}
  2. How should the derived path be written to correspond to the EVM address?('/m/44'/60'/0'/0/0')
  3. The account id for the moonbeam network is 20 bytes long, not 32, so it runs exceptionally: panic: invalid account ID bytes
  4. I'm a beginner, thanks a lot for being able to answer the stupid questions I'm asking, and if it's feasible, could you give me a full example? (including keypair generation, and example of signing and sending a transaction)
package main

import (
	"fmt"
	"math/big"

	gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4"
	"github.com/centrifuge/go-substrate-rpc-client/v4/signature"
	"github.com/centrifuge/go-substrate-rpc-client/v4/types"
	"github.com/centrifuge/go-substrate-rpc-client/v4/types/codec"
	"github.com/centrifuge/go-substrate-rpc-client/v4/types/extrinsic"
	"github.com/centrifuge/go-substrate-rpc-client/v4/types/extrinsic/extensions"
	"go.uber.org/zap"
)

func main() {
	mnemonic := "XXXXXX"

	// 从助记词派生出密钥对
	keypair, err := signature.KeyringPairFromSecret(mnemonic+"/m/44'/60'/0'/0/0", uint16(1284))
	if err != nil {
		zap.S().Warnf("failed to derive key pair: %v", err)
		return
	}
	fmt.Println(keypair.Address)

	// Instantiate the API
	api, err := gsrpc.NewSubstrateAPI("wss://moonriver-rpc.dwellir.com")
	if err != nil {
		panic(err)
	}

	meta, err := api.RPC.State.GetMetadataLatest()
	if err != nil {
		panic(err)
	}

	// Create a call, transferring 1e10 units to Bob
	bob, err := types.NewMultiAddressFromHexAccountID("0x80893554E58bBCCa39faA3d88573e88f94145d86")
	if err != nil {
		panic(err)
	}

	// 1 unit of transfer
	bal, ok := new(big.Int).SetString("1000000000000", 10)
	if !ok {
		panic(fmt.Errorf("failed to convert balance"))
	}

	c, err := types.NewCall(meta, "Balances.transfer_keep_alive", bob, types.NewUCompact(bal))
	if err != nil {
		panic(err)
	}

	ext := extrinsic.NewDynamicExtrinsic(&c)

	genesisHash, err := api.RPC.Chain.GetBlockHash(0)
	if err != nil {
		panic(err)
	}

	rv, err := api.RPC.State.GetRuntimeVersionLatest()
	if err != nil {
		panic(err)
	}

	key, err := types.CreateStorageKey(meta, "System", "Account", keypair.PublicKey)
	if err != nil {
		panic(err)
	}

	var accountInfo types.AccountInfo
	ok, err = api.RPC.State.GetStorageLatest(key, &accountInfo)
	if err != nil || !ok {
		panic(err)
	}

	err = ext.Sign(
		keypair,
		meta,
		extrinsic.WithEra(types.ExtrinsicEra{IsImmortalEra: true}, genesisHash),
		extrinsic.WithNonce(types.NewUCompactFromUInt(uint64(accountInfo.Nonce))),
		extrinsic.WithTip(types.NewUCompactFromUInt(0)),
		extrinsic.WithSpecVersion(rv.SpecVersion),
		extrinsic.WithTransactionVersion(rv.TransactionVersion),
		extrinsic.WithGenesisHash(genesisHash),
		extrinsic.WithMetadataMode(extensions.CheckMetadataModeDisabled, extensions.CheckMetadataHash{Hash: types.NewEmptyOption[types.H256]()}),
		extrinsic.WithAssetID(types.NewEmptyOption[types.AssetID]()),
	)

	if err != nil {
		panic(err)
	}

	encodedExt, err := codec.EncodeToHex(ext)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Ext - %s\n", encodedExt)

	sub, err := api.RPC.Author.SubmitAndWatchDynamicExtrinsic(ext)
	if err != nil {
		panic(err)
	}
	defer sub.Unsubscribe()

	for {
		select {
		case st := <-sub.Chan():
			extStatus, _ := st.MarshalJSON()
			fmt.Printf("Status for transaction - %s\n", string(extStatus))
		case err := <-sub.Err():
			panic(err)
		}
	}
}

@github-staff github-staff deleted a comment from cdamian Aug 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants
@cdamian @YuXiaoCoder and others