From 7dcec0dc7ac1d757f7f88b15333541081c68a206 Mon Sep 17 00:00:00 2001 From: Bill Sahin Date: Tue, 26 Jul 2022 14:37:53 -0700 Subject: [PATCH] Added genesis block transactions (#36) --- mapper/pchain/transaction.go | 47 +- mapper/pchain/types.go | 6 + service/backend/pchain/block.go | 10 +- service/backend/pchain/indexer/parser.go | 91 ++- service/backend/pchain/indexer/parser_test.go | 30 +- .../testdata/outs/genesis_fuji_rosetta.json | 603 ++++++++++++++++++ 6 files changed, 776 insertions(+), 11 deletions(-) create mode 100644 service/backend/pchain/indexer/testdata/outs/genesis_fuji_rosetta.json diff --git a/mapper/pchain/transaction.go b/mapper/pchain/transaction.go index 06e44963..7abf680d 100644 --- a/mapper/pchain/transaction.go +++ b/mapper/pchain/transaction.go @@ -11,6 +11,7 @@ import ( "github.com/ava-labs/avalanchego/utils/formatting/address" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/platformvm" + "github.com/ava-labs/avalanchego/vms/platformvm/stakeable" "github.com/ava-labs/avalanchego/vms/platformvm/validator" "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/coinbase/rosetta-sdk-go/parser" @@ -106,6 +107,8 @@ func ParseTx(tx platformvm.UnsignedTx, isConstruction bool) (*types.Transaction, id = v.ID() case *platformvm.UnsignedCreateChainTx: id = v.ID() + ops = createChainToOperation(v) + case *platformvm.UnsignedAddSubnetValidatorTx: id = v.ID() default: @@ -137,20 +140,34 @@ func outToOperation(txOut []*avax.TransferableOutput, startIndex int, opType str outs := make([]*types.Operation, 0) for _, out := range txOut { - outAddrID := out.Out.(*secp256k1fx.TransferOutput).Addrs[0] - //TODO: [NM] use variables form somewhere - outAddrFormat, err := address.Format("P", "fuji", outAddrID[:]) - if err != nil { - return nil, err - } - metadata := &OperationMetadata{ Type: metaType, } + var outAddrFormat string + var transferOut avax.TransferableOut if transferOutput, ok := out.Out.(*secp256k1fx.TransferOutput); ok { metadata.Threshold = transferOutput.OutputOwners.Threshold metadata.Locktime = transferOutput.OutputOwners.Locktime + transferOut = out.Out + tfOut, ok := out.Out.(*secp256k1fx.TransferOutput) + if ok { + transferOut = tfOut + } + } else if lockOut, ok := out.Out.(*stakeable.LockOut); ok { + metadata.Locktime = lockOut.Locktime + transferOut = lockOut.TransferableOut + } + transferOutput, ok := transferOut.(*secp256k1fx.TransferOutput) + if ok && transferOutput.Addrs != nil { + outAddrID := transferOutput.Addrs[0] + + var err error + //TODO: [NM] use variables form somewhere + outAddrFormat, err = address.Format("P", "fuji", outAddrID[:]) + if err != nil { + return nil, err + } } opMetadata, err := mapper.MarshalJSONMap(metadata) @@ -236,6 +253,22 @@ func baseTxToOperations(tx *platformvm.BaseTx, txType string, isConstruction boo return ins, outs, nil } +func createChainToOperation(v *platformvm.UnsignedCreateChainTx) []*types.Operation { + return []*types.Operation{ + { + OperationIdentifier: &types.OperationIdentifier{Index: 0}, + Type: OpCreateChain, + Status: types.String(mapper.StatusSuccess), + Metadata: map[string]interface{}{ + MetadataSubnetID: v.SubnetID.String(), + MetadataChainName: v.ChainName, + MetadataVMID: v.VMID, + MetadataMemo: v.Memo, + }, + }, + } +} + func rewardValidatorToOperation(v *platformvm.UnsignedRewardValidatorTx) []*types.Operation { return []*types.Operation{ { diff --git a/mapper/pchain/types.go b/mapper/pchain/types.go index e79ca5c8..b773038a 100644 --- a/mapper/pchain/types.go +++ b/mapper/pchain/types.go @@ -6,6 +6,7 @@ const ( OpAddValidator = "ADD_VALIDATOR" OpAddDelegator = "ADD_DELEGATOR" OpRewardValidator = "REWARD_VALIDATOR" + OpCreateChain = "CREATE_CHAIN" OpTypeImport = "IMPORT" OpTypeExport = "EXPORT" @@ -15,6 +16,11 @@ const ( MetadataOpType = "type" MetadataStakingTxId = "staking_tx" + MetadataSubnetID = "subnet_id" + MetadataChainName = "chain_name" + MetadataVMID = "vmid" + MetadataMemo = "memo" + MetadataMessage = "message" ) var ( diff --git a/service/backend/pchain/block.go b/service/backend/pchain/block.go index c43b1464..9cd6bc02 100644 --- a/service/backend/pchain/block.go +++ b/service/backend/pchain/block.go @@ -98,13 +98,21 @@ func (b *Backend) buildGenesisBlockResponse(ctx context.Context) (*types.BlockRe return nil, err } + transactions, err := b.indexerParser.GenesisToTransactions(genesisBlock) + if err != nil { + return nil, err + } + genesisBlockIdentifier := b.buildGenesisBlockIdentifier(genesisBlock) return &types.BlockResponse{ Block: &types.Block{ BlockIdentifier: genesisBlockIdentifier, ParentBlockIdentifier: genesisBlockIdentifier, - Transactions: []*types.Transaction{}, + Transactions: transactions, Timestamp: mapper.UnixToUnixMilli(genesisBlock.Timestamp), + Metadata: map[string]interface{}{ + pmapper.MetadataMessage: genesisBlock.Message, + }, }, }, err } diff --git a/service/backend/pchain/indexer/parser.go b/service/backend/pchain/indexer/parser.go index 9df853f3..ee89fefb 100644 --- a/service/backend/pchain/indexer/parser.go +++ b/service/backend/pchain/indexer/parser.go @@ -3,6 +3,7 @@ package indexer import ( "context" "encoding/base64" + "errors" "fmt" "time" @@ -23,12 +24,18 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/stakeable" "github.com/ava-labs/avalanchego/vms/proposervm/block" "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/coinbase/rosetta-sdk-go/types" "github.com/ava-labs/avalanche-rosetta/client" "github.com/ava-labs/avalanche-rosetta/mapper" + pmapper "github.com/ava-labs/avalanche-rosetta/mapper/pchain" "github.com/ava-labs/avalanche-rosetta/service/backend/common" ) +var ( + errUnexpectedGenesisTx = errors.New("unexpected genesis tx") +) + type Parser struct { networkID uint32 avaxAssetID ids.ID @@ -155,11 +162,18 @@ func (p *Parser) Initialize(ctx context.Context) (*ParsedGenesisBlock, error) { utxo.UTXO.Out.InitCtx(p.ctx) } + // Genesis commit block's parent ID is the hash of genesis state + var genesisParentID ids.ID = hashing.ComputeHash256Array(bytes) + + // Genesis Block is not indexed by the indexer, but its block ID can be accessed from block 0's parent id + genesisChildBlock, _ := p.ParseBlockAtIndex(ctx, 1) + genesisBlockID := genesisChildBlock.ParentID + return &ParsedGenesisBlock{ ParsedBlock: ParsedBlock{ - ParentID: ids.Empty, + ParentID: genesisParentID, Height: 0, - BlockID: ids.Empty, + BlockID: genesisBlockID, BlockType: "GenesisBlock", Timestamp: p.readTime(), Txs: txs, @@ -562,3 +576,76 @@ func (p *Parser) parseTx(ctx context.Context, blkID ids.ID, tx platformvm.Tx, ge return nil, errs.Err } + +func (p *Parser) GenesisToTransactions(genesisBlock *ParsedGenesisBlock) ([]*types.Transaction, error) { + var unsignedTx platformvm.UnsignedTx + var transactionHash string + + transactions := make([]*types.Transaction, 0) + for i := range genesisBlock.Txs { + switch avTx := genesisBlock.Txs[i].(type) { + case *ParsedAddValidatorTx: + unsignedTx = &platformvm.UnsignedAddValidatorTx{ + BaseTx: platformvm.BaseTx{ + BaseTx: avax.BaseTx{ + NetworkID: avTx.NetworkID, + BlockchainID: avTx.BlockchainID, + Outs: utxoToTransferableOutput(avTx.Outs), + Ins: avTx.Ins, + Memo: avTx.Memo, + }, + }, + Validator: avTx.Validator, + Stake: avTx.Stake, + RewardsOwner: avTx.RewardsOwner, + Shares: avTx.Shares, + } + transactionHash = avTx.TxID.String() + + case *ParsedCreateChainTx: + unsignedTx = &platformvm.UnsignedCreateChainTx{ + BaseTx: platformvm.BaseTx{ + BaseTx: avax.BaseTx{ + NetworkID: avTx.NetworkID, + BlockchainID: avTx.BlockchainID, + Outs: utxoToTransferableOutput(avTx.Outs), + Ins: avTx.Ins, + Memo: avTx.Memo, + }, + }, + SubnetID: avTx.SubnetID, + ChainName: avTx.ChainName, + VMID: avTx.VMID, + FxIDs: avTx.FxIDs, + GenesisData: avTx.GenesisData, + SubnetAuth: avTx.SubnetAuth, + } + transactionHash = avTx.TxID.String() + default: + return nil, errUnexpectedGenesisTx + } + + transaction, err := pmapper.ParseTx(unsignedTx, false) + if err != nil { + return nil, err + } + + transaction.TransactionIdentifier = &types.TransactionIdentifier{ + Hash: transactionHash, + } + transactions = append(transactions, transaction) + } + return transactions, nil +} + +func utxoToTransferableOutput(utxos []*avax.UTXO) []*avax.TransferableOutput { + var outputs []*avax.TransferableOutput + for _, utxo := range utxos { + output := &avax.TransferableOutput{ + Asset: avax.Asset{ID: utxo.AssetID()}, + Out: utxo.Out.(avax.TransferableOut), + } + outputs = append(outputs, output) + } + return outputs +} diff --git a/service/backend/pchain/indexer/parser_test.go b/service/backend/pchain/indexer/parser_test.go index 3056dfd1..666a0a57 100644 --- a/service/backend/pchain/indexer/parser_test.go +++ b/service/backend/pchain/indexer/parser_test.go @@ -8,6 +8,8 @@ import ( "testing" "time" + "github.com/ava-labs/avalanchego/utils/constants" + mocks "github.com/ava-labs/avalanche-rosetta/mocks/client" "github.com/ava-labs/avalanchego/api" @@ -44,7 +46,7 @@ func TestMain(m *testing.M) { pchainClient := &mocks.PChainClient{} - pchainClient.On("GetNetworkID", mock.Anything).Return(uint32(1), nil).Once() + pchainClient.On("GetNetworkID", mock.Anything).Return(constants.MainnetID, nil).Once() for _, idx := range idxs { ret := readFixture("ins/%v.json", idx) @@ -112,6 +114,32 @@ func TestGenesisBlockCreateChainTxs(t *testing.T) { a.JSONEq(string(ret), string(j)) } +func TestGenesisBlockParseTxs(t *testing.T) { + a := assert.New(t) + + pchainClient := &mocks.PChainClient{} + pchainClient.On("GetNetworkID", mock.Anything).Return(constants.FujiID, nil).Once() + p, err := NewParser(pchainClient) + if err != nil { + panic(err) + } + + ctx := context.Background() + g, err := p.Initialize(ctx) + p.writeTime(time.Unix(0, 0)) + + rosettaTransactions, err := p.GenesisToTransactions(g) + assert.Nil(t, err) + + j, err := stdjson.Marshal(rosettaTransactions) + if err != nil { + panic(err) + } + + ret := readFixture("outs/genesis_fuji_rosetta.json") + a.JSONEq(string(ret), string(j)) +} + func TestFixtures(t *testing.T) { ctx := context.Background() a := assert.New(t) diff --git a/service/backend/pchain/indexer/testdata/outs/genesis_fuji_rosetta.json b/service/backend/pchain/indexer/testdata/outs/genesis_fuji_rosetta.json new file mode 100644 index 00000000..2a7cbead --- /dev/null +++ b/service/backend/pchain/indexer/testdata/outs/genesis_fuji_rosetta.json @@ -0,0 +1,603 @@ +[ + { + "transaction_identifier": { + "hash": "TdoZgvfDCs42jxihsb1coBY7rrhibDj56RH3UDFzkzFFQyGQo" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "2L1UKtRr61kpYUvGymnvjRhHdiASityfc3S1jtoG5n7Uc4sgm4" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "2RcmJyJFYNPSKPEoGMLzKJkeJrWxbQwgseU57fWWz2deRGA5nc" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "2eGGdfQNMa74nAfL2oP595suTuotfnbn76q9gCiysZMu2MvMfA" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "xTNV2MGT6UwRdE9e55jEjk1QNVCaujNA3ftyptvcGgDrs3bQa" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "2efFvnZQPNJcvFaor6JSawVX7679xgXFHRFTsruGY3rDPXazW7" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "NR8h3zYezKrfCW6ykYkYo9bHk6X6NtcUtaE8ZKVayDfUCCXFZ" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "gMf2xGJqueY5seAGHNs8sWFCDDe2oS4GGDEczEBPY27HYDMYF" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "xv3yCa3nkRcSk253mtEonhDkgxQqQ5CeevCJXGt9cNY9bL5yV" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "2Hsyxia2a1QrMjpVhFort3hWNzCjZwXkRu14PbmkfCzJjj1NWP" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "KfBfLm8DaErS7rJgdnobgSeeQM9CQnRj5YwbtP6rpMs4pvUJp" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "Uumyppd474jxw2KBt8RyXdQUGw1MKESJa19C2Aiir3ihYge5S" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "uSZ55F395FDepT7aY7pwUx9ePD8hjLdhUnC6frmsdPAmMqK1N" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "w6HpCJk38zwZvq5khyU2QmF83qL9gBTB3ggumVqfBJsWUa4jm" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "4qzP7ByUFWsjVAeVFzbBioLLuFyAAWCToGRfj5uomAbqpYGF7" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "QPaQMmD4xxCDDWBs9CjNMc1XvtfPwTMiKfKCNv2AP5xddbA3z" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "2hr6QaBeVqQfPYKYZvgb7Y2fzsUKUnkJNZ4UKYsa753jeuLVbG" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "2qzRvGxRSMpxWozdhgtpSQu9EBUG5jWqCULx9oh6EiQ6WaoPyd" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "27G2D9fvAMMvRAytKyWNgfXh77r4oySK9Hp3R9NFrintntdQF9" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "25yDypVdxt6qHs95TWpSLQLC7B4XKLgt8JCpx9d4FeVSfMW5fp" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "ADD_VALIDATOR", + "status": "SUCCESS", + "account": { + "address": "P-fuji1wycv8n7d2fg9aq6unp23pnj4q0arv03ysya8jw" + }, + "amount": { + "value": "2000000000000000", + "currency": { + "symbol": "AVAX", + "decimals": 18 + } + }, + "metadata": { + "locktime": 0, + "type": "STAKE" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "2JVSBoinj9C2J33VntvzYtVJNZdN2NKiwwKjcumHUWEb5DbBrm" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "CREATE_CHAIN", + "status": "SUCCESS", + "metadata": { + "chain_name": "X-Chain", + "memo": "0x", + "subnet_id": "11111111111111111111111111111111LpoYY", + "vmid": "jvYyfQTxGMJLuGWa55kdP2p2zSUYsQ5Raupu4TW34ZAUBAbtq" + } + } + ] + }, + { + "transaction_identifier": { + "hash": "yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp" + }, + "operations": [ + { + "operation_identifier": { + "index": 0 + }, + "type": "CREATE_CHAIN", + "status": "SUCCESS", + "metadata": { + "chain_name": "C-Chain", + "memo": "0x", + "subnet_id": "11111111111111111111111111111111LpoYY", + "vmid": "mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6" + } + } + ] + } +] +