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.
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.
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)
}