Skip to main content

How to Create an MPCTransaction

This guide assumes you have completed Create an MPCWallet and Sign a Message.

1. Fund Address

To broadcast a transaction, the originating address must be funded with the native gas token. For Ethereum’s Goerli test network, you can use a Goerli Faucet.

tip

Send some Goerli ETH to the address you created in the previous how-to guides.

2. Construct Transfer Transaction

The ProtocolService provides a convenience function to construct a TransferTransaction. Let’s use it so we don’t have to construct a native Ethereum transaction ourselves.

// Go code in your Proxy server

import protocols "github.com/coinbase/cloud/waas-client-library-go/gen/go/coinbase/cloud/protocols/v1"


...
protocolServiceClient, err := v1clients.NewProtocolServiceClient(ctx, authOpt)
if err != nil {
log.Fatalf("error instantiating protocol service client: %v", err)
}


sendAmount := big.NewInt(0.75 * params.Ether).String()
feeAmount := big.NewInt(10 * params.Gwei).String()



req := &protocols.ConstructTransferTransactionRequest{
Network: "networks/ethereum-goerli",
Asset: "networks/ethereum-goerli/assets/0c3569d3-b253-5128-a229-543e1e819430", // The resource name for the ETH Asset.
Sender: address.GetAddress(),
Recipient: "0x4ba6b8F1eBc3c14233F7917c721A64b2DA1b80C6", // whoever you want to send to
Amount: sendAmount,
Nonce: 0,
Fee: feeAmount,
}

tx, err := protocolServiceClient.ConstructTransferTransaction(ctx, req)

if err != nil {
log.Fatalf("error creating tx: %v", err)
}

// The transaction contains a transaction input that can be used in the request to
// CreateMPCTransaction.
input := &v1.TransactionInput{
Input: &v1.TransactionInput_EthereumRlpInput{
EthereumRlpInput: tx.Input.GetEthereumRlpInput(),
},
}

3. Initiate MPCTransaction Creation

Creating an MPCTransaction creates a MPCKeyService Signature under the hood, and requires MPC participation. Initiate this process with the CreateMPCTransaction API. The operation’s metadata will contain the DeviceGroup you can use to poll ListMPCOperations.

// Go code in your Proxy server
import mpcTransactions "github.com/coinbase/cloud/waas-client-library-go/gen/go/coinbase/cloud/mpc_transactions/v1"

mpcTxServiceClient, err := v1clients.NewMPCTransactionServiceClient(ctx, authOpt)
if err != nil {
log.Fatalf("error instantiating mpc transaction service client: %v", err)
}

req := &mpcTransactions.CreateMPCTransactionRequest{
Parent: mpcWallet.GetName(),
MpcTransaction: &v1pb.MPCTransaction{
Network: "networks/ethereum-goerli",
FromAddresses: []string{address.GetAddress()},
},
Input: input,
}


op, err := mpcTxServiceClient.CreateMPCTransaction(ctx, req)

if err != nil {
log.Fatalf("error initiating mpc transaction creation: %v", err)
}

metadata, _ := op.Metadata()

deviceGroupName := metadata.GetDeviceGroup()

4. Poll and Compute MPC Operation

The transaction signing is now underway, and will require MPC participation from your device. Poll for your pending DeviceGroup via your proxy server using ListMPCOperations.

tip

Alternatively, you can use the SDK method PollPendingSignatures, but the proxy server is more secure.

Now use the MPC data in the MPC operation to compute the MPC operation in the WaaS SDK.

// React Native code in your app
computeMPCOperation(mpcData)

5. Poll GetMPCTransaction and Confirm

Shortly after you compute the MPC operation, the MPCTransaction will be in state SIGNED. There are several more states to wait for:

  • CONFIRMING - The MPCTransaction was successfully broadcast on-chain and is awaiting confirmation.
  • CONFIRMED - The MPCTransaction was confirmed on-chain. It is possible for the MPCTransaction to revert to CONFIRMING if there is an on-chain reorganization.
  • FAILED - The MPCTransaction failed on-chain.

You can poll the GetMPCTransaction API to be continually updated about its state.

// Go code in your Proxy server

metadata, _ := newOp.Metadata()

mpcTxName := metadata.GetMpcTransaction()

mpcTx := &mpcTransactions.MPCTransaction{}

var err error

for mpcTx.GetState() != mpcTransactions.MPCTransaction_CONFIRMED ||
mpcTx.GetState() != mpcTransactions.MPCTransaction_FAILED {
time.Sleep(500 * time.Millisecond)
mpcTx, err = mpcTxClient.GetMPCTransaction(ctx, &mpcTransactions.GetMPCTransactionRequest{
Name: mpcTxName,
})

if err != nil {
log.Fatalf("error getting mpc transaction: %v", err)
}

Was this helpful?