code.vegaprotocol.io/vega@v0.79.0/wallet/api/admin_send_raw_transaction_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package api_test 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 "testing" 23 "time" 24 25 "code.vegaprotocol.io/vega/libs/jsonrpc" 26 vgrand "code.vegaprotocol.io/vega/libs/rand" 27 apipb "code.vegaprotocol.io/vega/protos/vega/api/v1" 28 "code.vegaprotocol.io/vega/wallet/api" 29 "code.vegaprotocol.io/vega/wallet/api/mocks" 30 walletnode "code.vegaprotocol.io/vega/wallet/api/node" 31 nodemocks "code.vegaprotocol.io/vega/wallet/api/node/mocks" 32 33 "github.com/golang/mock/gomock" 34 "github.com/stretchr/testify/assert" 35 ) 36 37 func TestAdminSendRawTransaction(t *testing.T) { 38 t.Run("Documentation matches the code", testAdminSendRawTransactionSchemaCorrect) 39 t.Run("Sending transaction with invalid params fails", testAdminSendingRawTransactionWithInvalidParamsFails) 40 t.Run("Sending transaction with valid params succeeds", testAdminSendingRawTransactionWithValidParamsSucceeds) 41 t.Run("Sending transaction with network that doesn't exist fails", testAdminSendingRawTransactionWithNetworkThatDoesntExistFails) 42 t.Run("Sending transaction with network that fails existence check fails", testAdminSendingRawTransactionWithNetworkThatFailsExistenceCheckFails) 43 t.Run("Sending transaction with failure to get network", testAdminSendingRawTransactionWithFailureToGetNetworkFails) 44 t.Run("Getting internal error during node selector building fails", testAdminSendingRawTransactionGettingInternalErrorDuringNodeSelectorBuildingFails) 45 t.Run("Sending transaction without healthy node fails", testAdminSendingRawTransactionWithoutHealthyNodeFails) 46 t.Run("Sending transaction with failed sending fails", testAdminSendingRawTransactionWithFailedSendingFails) 47 } 48 49 func testAdminSendRawTransactionSchemaCorrect(t *testing.T) { 50 assertEqualSchema(t, "admin.send_raw_transaction", api.AdminSendRawTransactionParams{}, api.AdminSendRawTransactionResult{}) 51 } 52 53 func testAdminSendingRawTransactionWithInvalidParamsFails(t *testing.T) { 54 tcs := []struct { 55 name string 56 params interface{} 57 expectedError error 58 }{ 59 { 60 name: "with nil params", 61 params: nil, 62 expectedError: api.ErrParamsRequired, 63 }, 64 { 65 name: "with wrong type of params", 66 params: "test", 67 expectedError: api.ErrParamsDoNotMatch, 68 }, 69 { 70 name: "with empty sending mode", 71 params: api.AdminSendRawTransactionParams{ 72 Network: "fairground", 73 SendingMode: "", 74 }, 75 expectedError: api.ErrSendingModeIsRequired, 76 }, 77 { 78 name: "with empty transaction", 79 params: api.AdminSendRawTransactionParams{ 80 Network: "fairground", 81 SendingMode: "TYPE_SYNC", 82 EncodedTransaction: "", 83 }, 84 expectedError: api.ErrEncodedTransactionIsRequired, 85 }, 86 { 87 name: "with non-base64 transaction", 88 params: api.AdminSendRawTransactionParams{ 89 Network: "fairground", 90 SendingMode: "TYPE_SYNC", 91 EncodedTransaction: "1234567890", 92 }, 93 expectedError: api.ErrEncodedTransactionIsNotValidBase64String, 94 }, 95 { 96 name: "with network and node address", 97 params: api.AdminSendRawTransactionParams{ 98 Network: "fairground", 99 NodeAddress: "localhost:3002", 100 SendingMode: "TYPE_SYNC", 101 EncodedTransaction: "Cip0ZXN0bmV0LWU5MGU2NwAI85ubpLO4mOnIARCEyogByrsBBwgCEgN7fQoSkwEKgAFlY2E4YjQ1MzNhNGNiZmFkY2VlMGNhYmZlNjdmMWRjZTAwN2RlODFlZjFlMTE3YTM4ZWVhMDJmYTNlMTcxMWM5NzI3YTQ3MmM3ZmNiNzU3ZDJmNTE4MTIxZTg2MzNiNjNlNTNmMWZjNjY0MTA1NjhmYjI5ODBmNDc4NjhiOTIwNRIMdmVnYS9lZDI1NTE5GAGAfQPCuwFGCkAxMjUzOGU0OTQ0ZjhjOWQ4MmU4MDNlNDE2YjM0MGQ2YmE0Mzk0NDIyZWQ1YWVmYmM2ZDYwNzYyZTcxMGFhNzk0ENLQAtI+QDNmZDQyZmQ1Y2ViMjJkOTlhYzQ1MDg2ZjFkODJkNTE2MTE4YTVjYjdhZDlhMmUwOTZjZDc4Y2EyYzg5NjBjODA=", 102 }, 103 expectedError: api.ErrSpecifyingNetworkAndNodeAddressIsNotSupported, 104 }, 105 { 106 name: "with network and node address missing", 107 params: api.AdminSendRawTransactionParams{ 108 SendingMode: "TYPE_SYNC", 109 EncodedTransaction: "Cip0ZXN0bmV0LWU5MGU2NwAI85ubpLO4mOnIARCEyogByrsBBwgCEgN7fQoSkwEKgAFlY2E4YjQ1MzNhNGNiZmFkY2VlMGNhYmZlNjdmMWRjZTAwN2RlODFlZjFlMTE3YTM4ZWVhMDJmYTNlMTcxMWM5NzI3YTQ3MmM3ZmNiNzU3ZDJmNTE4MTIxZTg2MzNiNjNlNTNmMWZjNjY0MTA1NjhmYjI5ODBmNDc4NjhiOTIwNRIMdmVnYS9lZDI1NTE5GAGAfQPCuwFGCkAxMjUzOGU0OTQ0ZjhjOWQ4MmU4MDNlNDE2YjM0MGQ2YmE0Mzk0NDIyZWQ1YWVmYmM2ZDYwNzYyZTcxMGFhNzk0ENLQAtI+QDNmZDQyZmQ1Y2ViMjJkOTlhYzQ1MDg2ZjFkODJkNTE2MTE4YTVjYjdhZDlhMmUwOTZjZDc4Y2EyYzg5NjBjODA=", 110 }, 111 expectedError: api.ErrNetworkOrNodeAddressIsRequired, 112 }, 113 } 114 115 for _, tc := range tcs { 116 t.Run(tc.name, func(tt *testing.T) { 117 // given 118 ctx := context.Background() 119 120 // setup 121 handler := newAdminSendRawTransactionHandler(tt, unexpectedNodeSelectorCall(tt)) 122 123 // when 124 result, errorDetails := handler.handle(t, ctx, tc.params) 125 126 // then 127 assertInvalidParams(tt, errorDetails, tc.expectedError) 128 assert.Empty(tt, result) 129 }) 130 } 131 } 132 133 func testAdminSendingRawTransactionWithValidParamsSucceeds(t *testing.T) { 134 // given 135 ctx := context.Background() 136 sendingMode := "TYPE_SYNC" 137 network := newNetwork(t) 138 txHash := vgrand.RandomStr(64) 139 nodeHost := vgrand.RandomStr(5) 140 141 // setup 142 handler := newAdminSendRawTransactionHandler(t, func(hosts []string, _ uint64, _ time.Duration) (walletnode.Selector, error) { 143 ctrl := gomock.NewController(t) 144 nodeSelector := nodemocks.NewMockSelector(ctrl) 145 node := nodemocks.NewMockNode(ctrl) 146 nodeSelector.EXPECT().Node(ctx, gomock.Any()).Times(1).Return(node, nil) 147 node.EXPECT().Host().Times(1).Return(nodeHost) 148 node.EXPECT().SendTransaction(ctx, gomock.Any(), apipb.SubmitTransactionRequest_TYPE_SYNC).Times(1).Return(txHash, nil) 149 return nodeSelector, nil 150 }) 151 152 // -- expected calls 153 handler.networkStore.EXPECT().NetworkExists(network.Name).Times(1).Return(true, nil) 154 handler.networkStore.EXPECT().GetNetwork(network.Name).Times(1).Return(&network, nil) 155 156 // when 157 result, errorDetails := handler.handle(t, ctx, api.AdminSendRawTransactionParams{ 158 Network: network.Name, 159 SendingMode: sendingMode, 160 EncodedTransaction: "Cip0ZXN0bmV0LWU5MGU2NwAI85ubpLO4mOnIARCEyogByrsBBwgCEgN7fQoSkwEKgAFlY2E4YjQ1MzNhNGNiZmFkY2VlMGNhYmZlNjdmMWRjZTAwN2RlODFlZjFlMTE3YTM4ZWVhMDJmYTNlMTcxMWM5NzI3YTQ3MmM3ZmNiNzU3ZDJmNTE4MTIxZTg2MzNiNjNlNTNmMWZjNjY0MTA1NjhmYjI5ODBmNDc4NjhiOTIwNRIMdmVnYS9lZDI1NTE5GAGAfQPCuwFGCkAxMjUzOGU0OTQ0ZjhjOWQ4MmU4MDNlNDE2YjM0MGQ2YmE0Mzk0NDIyZWQ1YWVmYmM2ZDYwNzYyZTcxMGFhNzk0ENLQAtI+QDNmZDQyZmQ1Y2ViMjJkOTlhYzQ1MDg2ZjFkODJkNTE2MTE4YTVjYjdhZDlhMmUwOTZjZDc4Y2EyYzg5NjBjODA=", 161 }) 162 163 // then 164 assert.Nil(t, errorDetails) 165 assert.NotEmpty(t, result.Transaction) 166 assert.Equal(t, txHash, result.TransactionHash) 167 assert.NotEmpty(t, result.ReceivedAt) 168 assert.NotEmpty(t, result.SentAt) 169 } 170 171 func testAdminSendingRawTransactionWithNetworkThatDoesntExistFails(t *testing.T) { 172 // given 173 ctx := context.Background() 174 175 // setup 176 handler := newAdminSendRawTransactionHandler(t, unexpectedNodeSelectorCall(t)) 177 178 // -- expected calls 179 handler.networkStore.EXPECT().NetworkExists(gomock.Any()).Times(1).Return(false, nil) 180 181 // when 182 result, errorDetails := handler.handle(t, ctx, api.AdminSendRawTransactionParams{ 183 Network: "fairground", 184 SendingMode: "TYPE_SYNC", 185 EncodedTransaction: "Cip0ZXN0bmV0LWU5MGU2NwAI85ubpLO4mOnIARCEyogByrsBBwgCEgN7fQoSkwEKgAFlY2E4YjQ1MzNhNGNiZmFkY2VlMGNhYmZlNjdmMWRjZTAwN2RlODFlZjFlMTE3YTM4ZWVhMDJmYTNlMTcxMWM5NzI3YTQ3MmM3ZmNiNzU3ZDJmNTE4MTIxZTg2MzNiNjNlNTNmMWZjNjY0MTA1NjhmYjI5ODBmNDc4NjhiOTIwNRIMdmVnYS9lZDI1NTE5GAGAfQPCuwFGCkAxMjUzOGU0OTQ0ZjhjOWQ4MmU4MDNlNDE2YjM0MGQ2YmE0Mzk0NDIyZWQ1YWVmYmM2ZDYwNzYyZTcxMGFhNzk0ENLQAtI+QDNmZDQyZmQ1Y2ViMjJkOTlhYzQ1MDg2ZjFkODJkNTE2MTE4YTVjYjdhZDlhMmUwOTZjZDc4Y2EyYzg5NjBjODA=", 186 }) 187 188 // then 189 assertInvalidParams(t, errorDetails, api.ErrNetworkDoesNotExist) 190 assert.Empty(t, result) 191 } 192 193 func testAdminSendingRawTransactionWithNetworkThatFailsExistenceCheckFails(t *testing.T) { 194 // given 195 ctx := context.Background() 196 197 // setup 198 handler := newAdminSendRawTransactionHandler(t, unexpectedNodeSelectorCall(t)) 199 200 // -- expected calls 201 handler.networkStore.EXPECT().NetworkExists(gomock.Any()).Times(1).Return(false, assert.AnError) 202 203 // when 204 result, errorDetails := handler.handle(t, ctx, api.AdminSendRawTransactionParams{ 205 Network: "fairground", 206 SendingMode: "TYPE_SYNC", 207 EncodedTransaction: "Cip0ZXN0bmV0LWU5MGU2NwAI85ubpLO4mOnIARCEyogByrsBBwgCEgN7fQoSkwEKgAFlY2E4YjQ1MzNhNGNiZmFkY2VlMGNhYmZlNjdmMWRjZTAwN2RlODFlZjFlMTE3YTM4ZWVhMDJmYTNlMTcxMWM5NzI3YTQ3MmM3ZmNiNzU3ZDJmNTE4MTIxZTg2MzNiNjNlNTNmMWZjNjY0MTA1NjhmYjI5ODBmNDc4NjhiOTIwNRIMdmVnYS9lZDI1NTE5GAGAfQPCuwFGCkAxMjUzOGU0OTQ0ZjhjOWQ4MmU4MDNlNDE2YjM0MGQ2YmE0Mzk0NDIyZWQ1YWVmYmM2ZDYwNzYyZTcxMGFhNzk0ENLQAtI+QDNmZDQyZmQ1Y2ViMjJkOTlhYzQ1MDg2ZjFkODJkNTE2MTE4YTVjYjdhZDlhMmUwOTZjZDc4Y2EyYzg5NjBjODA=", 208 }) 209 210 // then 211 assertInternalError(t, errorDetails, fmt.Errorf("could not determine if the network exists: %w", assert.AnError)) 212 assert.Empty(t, result) 213 } 214 215 func testAdminSendingRawTransactionWithFailureToGetNetworkFails(t *testing.T) { 216 // given 217 ctx := context.Background() 218 network := "fairground" 219 220 // setup 221 handler := newAdminSendRawTransactionHandler(t, unexpectedNodeSelectorCall(t)) 222 223 // -- expected calls 224 handler.networkStore.EXPECT().NetworkExists(network).Times(1).Return(true, nil) 225 handler.networkStore.EXPECT().GetNetwork(network).Times(1).Return(nil, assert.AnError) 226 227 // when 228 result, errorDetails := handler.handle(t, ctx, api.AdminSendRawTransactionParams{ 229 Network: network, 230 SendingMode: "TYPE_SYNC", 231 EncodedTransaction: "Cip0ZXN0bmV0LWU5MGU2NwAI85ubpLO4mOnIARCEyogByrsBBwgCEgN7fQoSkwEKgAFlY2E4YjQ1MzNhNGNiZmFkY2VlMGNhYmZlNjdmMWRjZTAwN2RlODFlZjFlMTE3YTM4ZWVhMDJmYTNlMTcxMWM5NzI3YTQ3MmM3ZmNiNzU3ZDJmNTE4MTIxZTg2MzNiNjNlNTNmMWZjNjY0MTA1NjhmYjI5ODBmNDc4NjhiOTIwNRIMdmVnYS9lZDI1NTE5GAGAfQPCuwFGCkAxMjUzOGU0OTQ0ZjhjOWQ4MmU4MDNlNDE2YjM0MGQ2YmE0Mzk0NDIyZWQ1YWVmYmM2ZDYwNzYyZTcxMGFhNzk0ENLQAtI+QDNmZDQyZmQ1Y2ViMjJkOTlhYzQ1MDg2ZjFkODJkNTE2MTE4YTVjYjdhZDlhMmUwOTZjZDc4Y2EyYzg5NjBjODA=", 232 }) 233 234 // then 235 assertInternalError(t, errorDetails, fmt.Errorf("could not retrieve the network configuration: %w", assert.AnError)) 236 assert.Empty(t, result) 237 } 238 239 func testAdminSendingRawTransactionGettingInternalErrorDuringNodeSelectorBuildingFails(t *testing.T) { 240 // given 241 ctx := context.Background() 242 network := newNetwork(t) 243 244 // setup 245 handler := newAdminSendRawTransactionHandler(t, func(hosts []string, _ uint64, _ time.Duration) (walletnode.Selector, error) { 246 return nil, assert.AnError 247 }) 248 249 // -- expected calls 250 handler.networkStore.EXPECT().NetworkExists(network.Name).Times(1).Return(true, nil) 251 handler.networkStore.EXPECT().GetNetwork(network.Name).Times(1).Return(&network, nil) 252 253 // when 254 result, errorDetails := handler.handle(t, ctx, api.AdminSendRawTransactionParams{ 255 Network: network.Name, 256 SendingMode: "TYPE_SYNC", 257 EncodedTransaction: "Cip0ZXN0bmV0LWU5MGU2NwAI85ubpLO4mOnIARCEyogByrsBBwgCEgN7fQoSkwEKgAFlY2E4YjQ1MzNhNGNiZmFkY2VlMGNhYmZlNjdmMWRjZTAwN2RlODFlZjFlMTE3YTM4ZWVhMDJmYTNlMTcxMWM5NzI3YTQ3MmM3ZmNiNzU3ZDJmNTE4MTIxZTg2MzNiNjNlNTNmMWZjNjY0MTA1NjhmYjI5ODBmNDc4NjhiOTIwNRIMdmVnYS9lZDI1NTE5GAGAfQPCuwFGCkAxMjUzOGU0OTQ0ZjhjOWQ4MmU4MDNlNDE2YjM0MGQ2YmE0Mzk0NDIyZWQ1YWVmYmM2ZDYwNzYyZTcxMGFhNzk0ENLQAtI+QDNmZDQyZmQ1Y2ViMjJkOTlhYzQ1MDg2ZjFkODJkNTE2MTE4YTVjYjdhZDlhMmUwOTZjZDc4Y2EyYzg5NjBjODA=", 258 }) 259 260 // then 261 assertInternalError(t, errorDetails, fmt.Errorf("could not initialize the node selector: %w", assert.AnError)) 262 assert.Empty(t, result) 263 } 264 265 func testAdminSendingRawTransactionWithoutHealthyNodeFails(t *testing.T) { 266 // given 267 ctx := context.Background() 268 network := newNetwork(t) 269 270 // setup 271 handler := newAdminSendRawTransactionHandler(t, func(hosts []string, _ uint64, _ time.Duration) (walletnode.Selector, error) { 272 ctrl := gomock.NewController(t) 273 nodeSelector := nodemocks.NewMockSelector(ctrl) 274 nodeSelector.EXPECT().Node(ctx, gomock.Any()).Times(1).Return(nil, assert.AnError) 275 return nodeSelector, nil 276 }) 277 278 // -- expected calls 279 handler.networkStore.EXPECT().NetworkExists(network.Name).Times(1).Return(true, nil) 280 handler.networkStore.EXPECT().GetNetwork(network.Name).Times(1).Return(&network, nil) 281 282 // when 283 result, errorDetails := handler.handle(t, ctx, api.AdminSendRawTransactionParams{ 284 Network: network.Name, 285 SendingMode: "TYPE_SYNC", 286 EncodedTransaction: "Cip0ZXN0bmV0LWU5MGU2NwAI85ubpLO4mOnIARCEyogByrsBBwgCEgN7fQoSkwEKgAFlY2E4YjQ1MzNhNGNiZmFkY2VlMGNhYmZlNjdmMWRjZTAwN2RlODFlZjFlMTE3YTM4ZWVhMDJmYTNlMTcxMWM5NzI3YTQ3MmM3ZmNiNzU3ZDJmNTE4MTIxZTg2MzNiNjNlNTNmMWZjNjY0MTA1NjhmYjI5ODBmNDc4NjhiOTIwNRIMdmVnYS9lZDI1NTE5GAGAfQPCuwFGCkAxMjUzOGU0OTQ0ZjhjOWQ4MmU4MDNlNDE2YjM0MGQ2YmE0Mzk0NDIyZWQ1YWVmYmM2ZDYwNzYyZTcxMGFhNzk0ENLQAtI+QDNmZDQyZmQ1Y2ViMjJkOTlhYzQ1MDg2ZjFkODJkNTE2MTE4YTVjYjdhZDlhMmUwOTZjZDc4Y2EyYzg5NjBjODA=", 287 }) 288 289 // then 290 assertNetworkError(t, errorDetails, api.ErrNoHealthyNodeAvailable) 291 assert.Empty(t, result) 292 } 293 294 func testAdminSendingRawTransactionWithFailedSendingFails(t *testing.T) { 295 // given 296 ctx := context.Background() 297 sendingMode := "TYPE_SYNC" 298 network := newNetwork(t) 299 300 // setup 301 handler := newAdminSendRawTransactionHandler(t, func(hosts []string, _ uint64, _ time.Duration) (walletnode.Selector, error) { 302 ctrl := gomock.NewController(t) 303 nodeSelector := nodemocks.NewMockSelector(ctrl) 304 node := nodemocks.NewMockNode(ctrl) 305 nodeSelector.EXPECT().Node(ctx, gomock.Any()).Times(1).Return(node, nil) 306 node.EXPECT().SendTransaction(ctx, gomock.Any(), apipb.SubmitTransactionRequest_TYPE_SYNC).Times(1).Return("", assert.AnError) 307 return nodeSelector, nil 308 }) 309 310 // -- expected calls 311 handler.networkStore.EXPECT().NetworkExists(network.Name).Times(1).Return(true, nil) 312 handler.networkStore.EXPECT().GetNetwork(network.Name).Times(1).Return(&network, nil) 313 314 // when 315 result, errorDetails := handler.handle(t, ctx, api.AdminSendRawTransactionParams{ 316 Network: network.Name, 317 SendingMode: sendingMode, 318 EncodedTransaction: "Cip0ZXN0bmV0LWU5MGU2NwAI85ubpLO4mOnIARCEyogByrsBBwgCEgN7fQoSkwEKgAFlY2E4YjQ1MzNhNGNiZmFkY2VlMGNhYmZlNjdmMWRjZTAwN2RlODFlZjFlMTE3YTM4ZWVhMDJmYTNlMTcxMWM5NzI3YTQ3MmM3ZmNiNzU3ZDJmNTE4MTIxZTg2MzNiNjNlNTNmMWZjNjY0MTA1NjhmYjI5ODBmNDc4NjhiOTIwNRIMdmVnYS9lZDI1NTE5GAGAfQPCuwFGCkAxMjUzOGU0OTQ0ZjhjOWQ4MmU4MDNlNDE2YjM0MGQ2YmE0Mzk0NDIyZWQ1YWVmYmM2ZDYwNzYyZTcxMGFhNzk0ENLQAtI+QDNmZDQyZmQ1Y2ViMjJkOTlhYzQ1MDg2ZjFkODJkNTE2MTE4YTVjYjdhZDlhMmUwOTZjZDc4Y2EyYzg5NjBjODA=", 319 }) 320 321 // then 322 assertNetworkError(t, errorDetails, errors.New("the transaction failed: assert.AnError general error for testing")) 323 assert.Empty(t, result) 324 } 325 326 type adminSendRawTransactionHandler struct { 327 *api.AdminSendRawTransaction 328 ctrl *gomock.Controller 329 networkStore *mocks.MockNetworkStore 330 } 331 332 func (h *adminSendRawTransactionHandler) handle(t *testing.T, ctx context.Context, params jsonrpc.Params) (api.AdminSendRawTransactionResult, *jsonrpc.ErrorDetails) { 333 t.Helper() 334 335 rawResult, err := h.Handle(ctx, params) 336 if rawResult != nil { 337 result, ok := rawResult.(api.AdminSendRawTransactionResult) 338 if !ok { 339 t.Fatal("AdminUpdatePermissions handler result is not a AdminSignTransactionResult") 340 } 341 return result, err 342 } 343 return api.AdminSendRawTransactionResult{}, err 344 } 345 346 func newAdminSendRawTransactionHandler(t *testing.T, selectorBuilder api.NodeSelectorBuilder) *adminSendRawTransactionHandler { 347 t.Helper() 348 349 ctrl := gomock.NewController(t) 350 networkStore := mocks.NewMockNetworkStore(ctrl) 351 352 return &adminSendRawTransactionHandler{ 353 AdminSendRawTransaction: api.NewAdminSendRawTransaction(networkStore, selectorBuilder), 354 ctrl: ctrl, 355 networkStore: networkStore, 356 } 357 }