diff --git a/mapper/pchain/types.go b/mapper/pchain/types.go index 15ef5dc6..b76b70bc 100644 --- a/mapper/pchain/types.go +++ b/mapper/pchain/types.go @@ -2,6 +2,7 @@ package pchain import ( "github.com/ava-labs/avalanchego/ids" + "github.com/coinbase/rosetta-sdk-go/parser" ) const ( @@ -44,6 +45,7 @@ const ( MetadataSigner = "signer" MetadataBaseFee = "base_fee" + MetadataMatches = "matches" MetadataValidatorRewards = "validator_rewards" MetadataValidatorRewardsOwner = "validator_rewards_owner" @@ -80,11 +82,11 @@ var ( // OperationMetadata contains metadata fields specific to individual Rosetta operations as opposed to transactions type OperationMetadata struct { - Type string `json:"type"` - SigIndices []uint32 `json:"sig_indices,omitempty"` - Locktime uint64 `json:"locktime"` - Threshold uint32 `json:"threshold,omitempty"` - BasicFee uint64 `json:"basic_fee,omitempty"` + Type string `json:"type"` + SigIndices []uint32 `json:"sig_indices,omitempty"` + Locktime uint64 `json:"locktime"` + Threshold uint32 `json:"threshold,omitempty"` + Matches []*parser.Match `json:"matches,omitempty"` } // ImportExportOptions contain response fields returned by /construction/preprocess for P-chain Import/Export transactions diff --git a/service/backend/pchain/construction.go b/service/backend/pchain/construction.go index a9f6876a..f6566d65 100644 --- a/service/backend/pchain/construction.go +++ b/service/backend/pchain/construction.go @@ -49,24 +49,8 @@ func (b *Backend) ConstructionPreprocess( reqMetadata = make(map[string]interface{}) } - // Build a dummy base tx to calculate the base fee - dummyBaseTx, _, err := pmapper.BuildTx( - pmapper.OpDummyBase, - matches, - pmapper.Metadata{BlockchainID: ids.Empty}, - b.codec, - b.avaxAssetID, - ) - if err != nil { - return nil, service.WrapError(service.ErrInternalError, err) - } - fee, err := b.calculateFee(ctx, dummyBaseTx, 0) - if err != nil { - return nil, service.WrapError(service.ErrInternalError, err) - } - reqMetadata[pmapper.MetadataOpType] = matches[0].Operations[0].Type - reqMetadata[pmapper.MetadataBaseFee] = fee.Value + reqMetadata[pmapper.MetadataMatches] = matches return &types.ConstructionPreprocessResponse{ Options: reqMetadata, @@ -83,8 +67,25 @@ func (b *Backend) ConstructionMetadata( return nil, service.WrapError(service.ErrInvalidInput, err) } - // Default to base fee for atomic txs - suggestedFee := mapper.AtomicAvaxAmount(big.NewInt(int64(opMetadata.BasicFee))) + if opMetadata.Matches == nil { + return nil, service.WrapError(service.ErrInvalidInput, errors.New("matches not found in options")) + } + + // Build a dummy base tx to calculate the base fee + dummyBaseTx, _, err := pmapper.BuildTx( + pmapper.OpDummyBase, + opMetadata.Matches, + pmapper.Metadata{BlockchainID: ids.Empty}, + b.codec, + b.avaxAssetID, + ) + if err != nil { + return nil, service.WrapError(service.ErrInternalError, err) + } + suggestedFee, err := b.calculateFee(ctx, dummyBaseTx) + if err != nil { + return nil, service.WrapError(service.ErrInternalError, err) + } var metadata *pmapper.Metadata switch opMetadata.Type { @@ -93,7 +94,7 @@ func (b *Backend) ConstructionMetadata( case pmapper.OpExportAvax: metadata, err = b.buildExportMetadata(ctx, req.Options) case pmapper.OpAddValidator, pmapper.OpAddDelegator, pmapper.OpAddPermissionlessDelegator, pmapper.OpAddPermissionlessValidator: - metadata, suggestedFee, err = b.buildStakingMetadata(ctx, req.Options, opMetadata.Type, opMetadata.BasicFee) + metadata, suggestedFee, err = b.buildStakingMetadata(ctx, req.Options, opMetadata.Matches, opMetadata.Type) metadata.Threshold = opMetadata.Threshold metadata.Locktime = opMetadata.Locktime @@ -120,9 +121,10 @@ func (b *Backend) ConstructionMetadata( return nil, service.WrapError(service.ErrInternalError, err) } + suggestedFeeAvax := mapper.AtomicAvaxAmount(big.NewInt(int64(suggestedFee))) return &types.ConstructionMetadataResponse{ Metadata: metadataMap, - SuggestedFee: []*types.Amount{suggestedFee}, + SuggestedFee: []*types.Amount{suggestedFeeAvax}, }, nil } @@ -172,11 +174,12 @@ func (b *Backend) buildExportMetadata( func (b *Backend) buildStakingMetadata( ctx context.Context, options map[string]interface{}, - opType string, baseFee uint64, -) (*pmapper.Metadata, *types.Amount, error) { + matches []*parser.Match, + opType string, +) (*pmapper.Metadata, uint64, error) { var preprocessOptions pmapper.StakingOptions if err := mapper.UnmarshalJSONMap(options, &preprocessOptions); err != nil { - return nil, nil, err + return nil, 0, err } stakingMetadata := &pmapper.Metadata{ @@ -198,32 +201,20 @@ func (b *Backend) buildStakingMetadata( // Build a dummy staking tx to calculate the staking related fee dummyStakingTx, _, err := pmapper.BuildTx( opType, - // `matches` must have 2 elements, one for inputs and one for outputs - []*parser.Match{ - { - // inputs match - Operations: []*types.Operation{}, - Amounts: []*big.Int{}, - }, - { - // outputs match - Operations: []*types.Operation{}, - Amounts: []*big.Int{}, - }, - }, + matches, *stakingMetadata, b.codec, b.avaxAssetID, ) if err != nil { - return nil, nil, err + return nil, 0, err } - fee, err := b.calculateFee(ctx, dummyStakingTx, baseFee) + suggestedFee, err := b.calculateFee(ctx, dummyStakingTx) if err != nil { - return nil, nil, err + return nil, 0, err } - return stakingMetadata, fee, nil + return stakingMetadata, suggestedFee, nil } // ConstructionPayloads implements /construction/payloads endpoint for P-chain @@ -302,16 +293,16 @@ func (*Backend) CombineTx(tx common.AvaxTx, signatures []*types.Signature) (comm return pTx, nil } -func (b *Backend) calculateFee(ctx context.Context, tx *txs.Tx, initialFee uint64) (*types.Amount, error) { +func (b *Backend) calculateFee(ctx context.Context, tx *txs.Tx) (uint64, error) { feeCalculator, err := b.PickFeeCalculator(ctx, time.Now()) if err != nil { - return nil, err + return 0, err } fee, err := feeCalculator.CalculateFee(tx.Unsigned) if err != nil { - return nil, err + return 0, err } - return mapper.AtomicAvaxAmount(big.NewInt(int64(fee + initialFee))), nil + return fee, nil } func (b *Backend) PickFeeCalculator(ctx context.Context, timestamp time.Time) (txfee.Calculator, error) {