explicit fee execution
This commit is contained in:
@@ -103,6 +103,108 @@ func TestCompile_ExternalToExternal_BridgeExpansion(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompile_ExternalToExternal_WithWalletFee_InsertsFeeStep(t *testing.T) {
|
||||
compiler := New()
|
||||
|
||||
graph, err := compiler.Compile(Input{
|
||||
IntentSnapshot: testIntent(model.PaymentKindPayout),
|
||||
QuoteSnapshot: &model.PaymentQuoteSnapshot{
|
||||
DebitAmount: &paymenttypes.Money{Amount: "5.842042", Currency: "USDT"},
|
||||
FeeLines: []*paymenttypes.FeeLine{
|
||||
{
|
||||
Money: &paymenttypes.Money{Amount: "0.42", Currency: "USDT"},
|
||||
Side: paymenttypes.EntrySideDebit,
|
||||
Meta: map[string]string{"fee_target": "wallet"},
|
||||
},
|
||||
},
|
||||
Route: &paymenttypes.QuoteRouteSpecification{
|
||||
RouteRef: "route-1",
|
||||
Hops: []*paymenttypes.QuoteRouteHop{
|
||||
{
|
||||
Index: 10,
|
||||
Rail: "CRYPTO",
|
||||
Gateway: "gw-crypto",
|
||||
InstanceID: "crypto-1",
|
||||
Role: paymenttypes.QuoteRouteHopRoleSource,
|
||||
},
|
||||
{
|
||||
Index: 20,
|
||||
Rail: "CARD",
|
||||
Gateway: "gw-card",
|
||||
InstanceID: "card-1",
|
||||
Role: paymenttypes.QuoteRouteHopRoleDestination,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Compile returned error: %v", err)
|
||||
}
|
||||
if graph == nil {
|
||||
t.Fatal("expected graph")
|
||||
}
|
||||
if got, want := len(graph.Steps), 10; got != want {
|
||||
t.Fatalf("expected 10 steps, got %d", got)
|
||||
}
|
||||
|
||||
assertStep(t, graph.Steps[0], "hop.10.crypto.send", discovery.RailOperationSend, discovery.RailCrypto, model.ReportVisibilityBackoffice)
|
||||
assertStep(t, graph.Steps[1], "hop.10.crypto.fee", discovery.RailOperationFee, discovery.RailCrypto, model.ReportVisibilityHidden)
|
||||
assertStep(t, graph.Steps[2], "hop.10.crypto.observe", discovery.RailOperationObserveConfirm, discovery.RailCrypto, model.ReportVisibilityBackoffice)
|
||||
assertStep(t, graph.Steps[3], "edge.10_20.ledger.credit", discovery.RailOperationExternalCredit, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||
assertStep(t, graph.Steps[4], "edge.10_20.ledger.block", discovery.RailOperationBlock, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||
assertStep(t, graph.Steps[5], "hop.20.card_payout.send", discovery.RailOperationSend, discovery.RailCardPayout, model.ReportVisibilityUser)
|
||||
assertStep(t, graph.Steps[6], "hop.20.card_payout.observe", discovery.RailOperationObserveConfirm, discovery.RailCardPayout, model.ReportVisibilityUser)
|
||||
assertStep(t, graph.Steps[7], "edge.10_20.ledger.debit", discovery.RailOperationExternalDebit, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||
assertStep(t, graph.Steps[8], "edge.10_20.ledger.release", discovery.RailOperationRelease, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||
assertStep(t, graph.Steps[9], "edge.10_20.ledger.release", discovery.RailOperationRelease, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||
|
||||
if got, want := graph.Steps[1].DependsOn, []string{graph.Steps[0].StepRef}; !equalStringSlice(got, want) {
|
||||
t.Fatalf("fee deps mismatch: got=%v want=%v", got, want)
|
||||
}
|
||||
if got, want := graph.Steps[2].DependsOn, []string{graph.Steps[1].StepRef}; !equalStringSlice(got, want) {
|
||||
t.Fatalf("observe deps mismatch: got=%v want=%v", got, want)
|
||||
}
|
||||
if got, want := graph.Steps[3].DependsOn, []string{graph.Steps[2].StepRef}; !equalStringSlice(got, want) {
|
||||
t.Fatalf("credit deps mismatch: got=%v want=%v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompile_ExternalToExternal_IgnoresNonWalletFeeLines(t *testing.T) {
|
||||
compiler := New()
|
||||
|
||||
graph, err := compiler.Compile(Input{
|
||||
IntentSnapshot: testIntent(model.PaymentKindPayout),
|
||||
QuoteSnapshot: &model.PaymentQuoteSnapshot{
|
||||
DebitAmount: &paymenttypes.Money{Amount: "5.842042", Currency: "USDT"},
|
||||
FeeLines: []*paymenttypes.FeeLine{
|
||||
{
|
||||
Money: &paymenttypes.Money{Amount: "0.42", Currency: "USDT"},
|
||||
Side: paymenttypes.EntrySideDebit,
|
||||
Meta: map[string]string{"fee_target": "ledger"},
|
||||
},
|
||||
},
|
||||
Route: &paymenttypes.QuoteRouteSpecification{
|
||||
Hops: []*paymenttypes.QuoteRouteHop{
|
||||
{Index: 10, Rail: "CRYPTO", Role: paymenttypes.QuoteRouteHopRoleSource},
|
||||
{Index: 20, Rail: "CARD", Role: paymenttypes.QuoteRouteHopRoleDestination},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Compile returned error: %v", err)
|
||||
}
|
||||
if got, want := len(graph.Steps), 9; got != want {
|
||||
t.Fatalf("expected 9 steps, got %d", got)
|
||||
}
|
||||
for i := range graph.Steps {
|
||||
if got := graph.Steps[i].Action; got == discovery.RailOperationFee {
|
||||
t.Fatalf("unexpected fee step at index %d: %+v", i, graph.Steps[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompile_InternalToExternal_UsesHoldAndSettlementBranches(t *testing.T) {
|
||||
compiler := New()
|
||||
|
||||
|
||||
@@ -51,6 +51,30 @@ func (e *expansion) nextRef(base string) string {
|
||||
return token + "_" + itoa(count+1)
|
||||
}
|
||||
|
||||
func (e *expansion) needsWalletFeeStep(hop normalizedHop) bool {
|
||||
if e == nil || len(e.walletFeeHops) == 0 {
|
||||
return false
|
||||
}
|
||||
key := observedKey(hop)
|
||||
if _, ok := e.walletFeeHops[key]; !ok {
|
||||
return false
|
||||
}
|
||||
if _, emitted := e.walletFeeEmitted[key]; emitted {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *expansion) markWalletFeeEmitted(hop normalizedHop) {
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
if e.walletFeeEmitted == nil {
|
||||
e.walletFeeEmitted = map[string]struct{}{}
|
||||
}
|
||||
e.walletFeeEmitted[observedKey(hop)] = struct{}{}
|
||||
}
|
||||
|
||||
func normalizeStep(step Step) Step {
|
||||
step.StepRef = strings.TrimSpace(step.StepRef)
|
||||
step.StepCode = strings.TrimSpace(step.StepCode)
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package xplan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/tech/sendico/pkg/discovery"
|
||||
|
||||
"github.com/tech/sendico/payments/storage/model"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
||||
)
|
||||
|
||||
func planWalletFeeHops(
|
||||
hops []normalizedHop,
|
||||
intent model.PaymentIntent,
|
||||
quote *model.PaymentQuoteSnapshot,
|
||||
) (map[string]struct{}, error) {
|
||||
_, hasWalletFee, err := walletFeeAmountFromSnapshots(intent, quote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !hasWalletFee {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sourceHop, ok := sourceCryptoHop(hops)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
return map[string]struct{}{
|
||||
observedKey(sourceHop): {},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func sourceCryptoHop(hops []normalizedHop) (normalizedHop, bool) {
|
||||
for i := range hops {
|
||||
if hops[i].rail == discovery.RailCrypto && hops[i].role == paymenttypes.QuoteRouteHopRoleSource {
|
||||
return hops[i], true
|
||||
}
|
||||
}
|
||||
if len(hops) > 0 && hops[0].rail == discovery.RailCrypto {
|
||||
return hops[0], true
|
||||
}
|
||||
return normalizedHop{}, false
|
||||
}
|
||||
|
||||
func walletFeeAmountFromSnapshots(
|
||||
intent model.PaymentIntent,
|
||||
quote *model.PaymentQuoteSnapshot,
|
||||
) (*paymenttypes.Money, bool, error) {
|
||||
if quote == nil || len(quote.FeeLines) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
sourceCurrency := ""
|
||||
if source := feePlanningSourceAmount(intent, quote); source != nil {
|
||||
sourceCurrency = strings.TrimSpace(source.Currency)
|
||||
}
|
||||
|
||||
total := decimal.Zero
|
||||
currency := ""
|
||||
for i, line := range quote.FeeLines {
|
||||
if !isWalletDebitFeeLine(line) {
|
||||
continue
|
||||
}
|
||||
money := line.GetMoney()
|
||||
if money == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
lineCurrency := strings.TrimSpace(money.GetCurrency())
|
||||
if lineCurrency == "" {
|
||||
return nil, false, merrors.InvalidArgument(fmt.Sprintf("quote_snapshot.fee_lines[%d].money.currency is required", i))
|
||||
}
|
||||
if sourceCurrency != "" && !strings.EqualFold(sourceCurrency, lineCurrency) {
|
||||
continue
|
||||
}
|
||||
if currency == "" {
|
||||
currency = lineCurrency
|
||||
} else if !strings.EqualFold(currency, lineCurrency) {
|
||||
return nil, false, merrors.InvalidArgument("quote_snapshot.fee_lines wallet fee currency mismatch")
|
||||
}
|
||||
|
||||
amountRaw := strings.TrimSpace(money.GetAmount())
|
||||
amount, err := decimal.NewFromString(amountRaw)
|
||||
if err != nil {
|
||||
return nil, false, merrors.InvalidArgument(fmt.Sprintf("quote_snapshot.fee_lines[%d].money.amount is invalid", i))
|
||||
}
|
||||
if amount.Sign() < 0 {
|
||||
amount = amount.Neg()
|
||||
}
|
||||
if amount.Sign() == 0 {
|
||||
continue
|
||||
}
|
||||
total = total.Add(amount)
|
||||
}
|
||||
|
||||
if total.Sign() <= 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
return &paymenttypes.Money{
|
||||
Amount: total.String(),
|
||||
Currency: strings.ToUpper(strings.TrimSpace(currency)),
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
func feePlanningSourceAmount(intent model.PaymentIntent, quote *model.PaymentQuoteSnapshot) *paymenttypes.Money {
|
||||
if quote != nil && quote.DebitAmount != nil {
|
||||
return quote.DebitAmount
|
||||
}
|
||||
return intent.Amount
|
||||
}
|
||||
|
||||
func isWalletDebitFeeLine(line *paymenttypes.FeeLine) bool {
|
||||
if line == nil {
|
||||
return false
|
||||
}
|
||||
if line.GetSide() != paymenttypes.EntrySideDebit {
|
||||
return false
|
||||
}
|
||||
meta := line.Meta
|
||||
if len(meta) == 0 {
|
||||
return false
|
||||
}
|
||||
return strings.EqualFold(strings.TrimSpace(meta["fee_target"]), "wallet")
|
||||
}
|
||||
@@ -30,12 +30,16 @@ type expansion struct {
|
||||
lastMainRef string
|
||||
refSeq map[string]int
|
||||
externalObserved map[string]string
|
||||
walletFeeHops map[string]struct{}
|
||||
walletFeeEmitted map[string]struct{}
|
||||
}
|
||||
|
||||
func newExpansion() *expansion {
|
||||
func newExpansion(walletFeeHops map[string]struct{}) *expansion {
|
||||
return &expansion{
|
||||
refSeq: map[string]int{},
|
||||
externalObserved: map[string]string{},
|
||||
walletFeeHops: walletFeeHops,
|
||||
walletFeeEmitted: map[string]struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +90,12 @@ func (s *svc) Compile(in Input) (graph *Graph, err error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ex := newExpansion()
|
||||
walletFeeHops, err := planWalletFeeHops(hops, in.IntentSnapshot, in.QuoteSnapshot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ex := newExpansion(walletFeeHops)
|
||||
appendGuards(ex, conditions)
|
||||
|
||||
if len(hops) == 1 {
|
||||
|
||||
@@ -147,10 +147,19 @@ func (s *svc) ensureExternalObserved(ex *expansion, hop normalizedHop, intent mo
|
||||
|
||||
sendStep := makeRailSendStep(hop, intent)
|
||||
sendRef := ex.appendMain(sendStep)
|
||||
observeDependency := sendRef
|
||||
if ex.needsWalletFeeStep(hop) {
|
||||
feeStep := makeRailFeeStep(hop)
|
||||
if observeDependency != "" {
|
||||
feeStep.DependsOn = []string{observeDependency}
|
||||
}
|
||||
observeDependency = ex.appendMain(feeStep)
|
||||
ex.markWalletFeeEmitted(hop)
|
||||
}
|
||||
|
||||
observeStep := makeRailObserveStep(hop, intent)
|
||||
if sendRef != "" {
|
||||
observeStep.DependsOn = []string{sendRef}
|
||||
if observeDependency != "" {
|
||||
observeStep.DependsOn = []string{observeDependency}
|
||||
}
|
||||
observeRef := ex.appendMain(observeStep)
|
||||
|
||||
@@ -158,6 +167,20 @@ func (s *svc) ensureExternalObserved(ex *expansion, hop normalizedHop, intent mo
|
||||
return observeRef, nil
|
||||
}
|
||||
|
||||
func makeRailFeeStep(hop normalizedHop) Step {
|
||||
return Step{
|
||||
StepCode: singleHopCode(hop, "fee"),
|
||||
Kind: StepKindRailSend,
|
||||
Action: discovery.RailOperationFee,
|
||||
Rail: hop.rail,
|
||||
Gateway: hop.gateway,
|
||||
InstanceID: hop.instanceID,
|
||||
HopIndex: hop.index,
|
||||
HopRole: hop.role,
|
||||
Visibility: model.ReportVisibilityHidden,
|
||||
}
|
||||
}
|
||||
|
||||
func appendGuards(ex *expansion, conditions *paymenttypes.QuoteExecutionConditions) {
|
||||
if conditions == nil {
|
||||
return
|
||||
|
||||
@@ -235,7 +235,7 @@ func resolveStepContext(
|
||||
|
||||
func kindForAction(action model.RailOperation) StepKind {
|
||||
switch action {
|
||||
case discovery.RailOperationSend, discovery.RailOperationFXConvert:
|
||||
case discovery.RailOperationSend, discovery.RailOperationFXConvert, discovery.RailOperationFee:
|
||||
return StepKindRailSend
|
||||
case discovery.RailOperationObserveConfirm:
|
||||
return StepKindRailObserve
|
||||
|
||||
@@ -99,11 +99,6 @@ func (e *gatewayCryptoExecutor) ExecuteCrypto(ctx context.Context, req sexec.Ste
|
||||
}
|
||||
step.ConvertedMoney = nil
|
||||
|
||||
if action == discovery.RailOperationSend {
|
||||
if err := e.submitWalletFeeTransfer(ctx, req, client, gateway, sourceWalletRef, operationRef, idempotencyKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
step.State = agg.StepStateCompleted
|
||||
step.FailureCode = ""
|
||||
step.FailureMsg = ""
|
||||
@@ -207,64 +202,6 @@ func sourceAmount(payment *agg.Payment, action model.RailOperation) (*moneyv1.Mo
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *gatewayCryptoExecutor) submitWalletFeeTransfer(
|
||||
ctx context.Context,
|
||||
req sexec.StepRequest,
|
||||
client chainclient.Client,
|
||||
gateway *model.GatewayInstanceDescriptor,
|
||||
sourceWalletRef string,
|
||||
operationRef string,
|
||||
idempotencyKey string,
|
||||
) error {
|
||||
if req.Payment == nil {
|
||||
return merrors.InvalidArgument("crypto send: payment is required")
|
||||
}
|
||||
|
||||
feeAmount, ok, err := walletFeeAmount(req.Payment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
destination, err := e.resolveDestination(ctx, client, req.Payment, discovery.RailOperationFee)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
feeMoney := &moneyv1.Money{
|
||||
Amount: strings.TrimSpace(feeAmount.GetAmount()),
|
||||
Currency: strings.TrimSpace(feeAmount.GetCurrency()),
|
||||
}
|
||||
|
||||
resp, err := client.SubmitTransfer(ctx, &chainv1.SubmitTransferRequest{
|
||||
IdempotencyKey: strings.TrimSpace(idempotencyKey) + ":fee",
|
||||
OrganizationRef: req.Payment.OrganizationRef.Hex(),
|
||||
SourceWalletRef: sourceWalletRef,
|
||||
Destination: destination,
|
||||
Amount: feeMoney,
|
||||
OperationRef: strings.TrimSpace(operationRef) + ":fee",
|
||||
IntentRef: strings.TrimSpace(req.Payment.IntentSnapshot.Ref),
|
||||
PaymentRef: strings.TrimSpace(req.Payment.PaymentRef),
|
||||
Metadata: transferMetadata(req.Step),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp == nil || resp.GetTransfer() == nil {
|
||||
return merrors.Internal("crypto send: fee transfer response is missing")
|
||||
}
|
||||
if _, err := transferExternalRefs(resp.GetTransfer(), firstNonEmpty(
|
||||
strings.TrimSpace(req.Step.InstanceID),
|
||||
strings.TrimSpace(gateway.InstanceID),
|
||||
strings.TrimSpace(req.Step.Gateway),
|
||||
strings.TrimSpace(gateway.ID),
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func effectiveSourceAmount(payment *agg.Payment) *paymenttypes.Money {
|
||||
if payment == nil {
|
||||
return nil
|
||||
|
||||
@@ -204,32 +204,19 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_MissingCardRoute(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGatewayCryptoExecutor_ExecuteCrypto_SubmitsWalletFeeTransferOnSend(t *testing.T) {
|
||||
func TestGatewayCryptoExecutor_ExecuteCrypto_SendDoesNotSubmitWalletFeeTransfer(t *testing.T) {
|
||||
orgID := bson.NewObjectID()
|
||||
|
||||
submitRequests := make([]*chainv1.SubmitTransferRequest, 0, 2)
|
||||
submitRequests := make([]*chainv1.SubmitTransferRequest, 0, 1)
|
||||
client := &chainclient.Fake{
|
||||
SubmitTransferFn: func(_ context.Context, req *chainv1.SubmitTransferRequest) (*chainv1.SubmitTransferResponse, error) {
|
||||
submitRequests = append(submitRequests, req)
|
||||
switch len(submitRequests) {
|
||||
case 1:
|
||||
return &chainv1.SubmitTransferResponse{
|
||||
Transfer: &chainv1.Transfer{
|
||||
TransferRef: "trf-principal",
|
||||
OperationRef: "op-principal",
|
||||
},
|
||||
}, nil
|
||||
case 2:
|
||||
return &chainv1.SubmitTransferResponse{
|
||||
Transfer: &chainv1.Transfer{
|
||||
TransferRef: "trf-fee",
|
||||
OperationRef: "op-fee",
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected transfer submission call %d", len(submitRequests))
|
||||
panic("unreachable")
|
||||
}
|
||||
return &chainv1.SubmitTransferResponse{
|
||||
Transfer: &chainv1.Transfer{
|
||||
TransferRef: "trf-principal",
|
||||
OperationRef: "op-principal",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
resolver := &fakeGatewayInvokeResolver{client: client}
|
||||
@@ -311,7 +298,7 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_SubmitsWalletFeeTransferOnSend(t *t
|
||||
if out == nil {
|
||||
t.Fatal("expected output")
|
||||
}
|
||||
if got, want := len(submitRequests), 2; got != want {
|
||||
if got, want := len(submitRequests), 1; got != want {
|
||||
t.Fatalf("submit transfer calls mismatch: got=%d want=%d", got, want)
|
||||
}
|
||||
|
||||
@@ -322,29 +309,12 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_SubmitsWalletFeeTransferOnSend(t *t
|
||||
if got, want := principalReq.GetDestination().GetExternalAddress(), "TUA_DEST"; got != want {
|
||||
t.Fatalf("principal destination mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
|
||||
feeReq := submitRequests[1]
|
||||
if got, want := feeReq.GetAmount().GetAmount(), "0.7"; got != want {
|
||||
t.Fatalf("fee amount mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
if got, want := feeReq.GetAmount().GetCurrency(), "USDT"; got != want {
|
||||
t.Fatalf("fee currency mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
if got, want := feeReq.GetDestination().GetExternalAddress(), "TUA_FEE"; got != want {
|
||||
t.Fatalf("fee destination mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
if got, want := feeReq.GetOperationRef(), "payment-1:hop_1_crypto_send:fee"; got != want {
|
||||
t.Fatalf("fee operation_ref mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
if got, want := feeReq.GetIdempotencyKey(), "idem-1:hop_1_crypto_send:fee"; got != want {
|
||||
t.Fatalf("fee idempotency_key mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGatewayCryptoExecutor_ExecuteCrypto_ResolvesFeeAddressFromFeeWalletRef(t *testing.T) {
|
||||
func TestGatewayCryptoExecutor_ExecuteCrypto_FeeActionResolvesFeeAddressFromFeeWalletRef(t *testing.T) {
|
||||
orgID := bson.NewObjectID()
|
||||
|
||||
submitRequests := make([]*chainv1.SubmitTransferRequest, 0, 2)
|
||||
submitRequests := make([]*chainv1.SubmitTransferRequest, 0, 1)
|
||||
var managedWalletReq *chainv1.GetManagedWalletRequest
|
||||
client := &chainclient.Fake{
|
||||
GetManagedWalletFn: func(_ context.Context, req *chainv1.GetManagedWalletRequest) (*chainv1.GetManagedWalletResponse, error) {
|
||||
@@ -358,25 +328,12 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_ResolvesFeeAddressFromFeeWalletRef(
|
||||
},
|
||||
SubmitTransferFn: func(_ context.Context, req *chainv1.SubmitTransferRequest) (*chainv1.SubmitTransferResponse, error) {
|
||||
submitRequests = append(submitRequests, req)
|
||||
switch len(submitRequests) {
|
||||
case 1:
|
||||
return &chainv1.SubmitTransferResponse{
|
||||
Transfer: &chainv1.Transfer{
|
||||
TransferRef: "trf-principal",
|
||||
OperationRef: "op-principal",
|
||||
},
|
||||
}, nil
|
||||
case 2:
|
||||
return &chainv1.SubmitTransferResponse{
|
||||
Transfer: &chainv1.Transfer{
|
||||
TransferRef: "trf-fee",
|
||||
OperationRef: "op-fee",
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected transfer submission call %d", len(submitRequests))
|
||||
panic("unreachable")
|
||||
}
|
||||
return &chainv1.SubmitTransferResponse{
|
||||
Transfer: &chainv1.Transfer{
|
||||
TransferRef: "trf-fee",
|
||||
OperationRef: "op-fee",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
resolver := &fakeGatewayInvokeResolver{client: client}
|
||||
@@ -437,16 +394,16 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_ResolvesFeeAddressFromFeeWalletRef(
|
||||
},
|
||||
},
|
||||
Step: xplan.Step{
|
||||
StepRef: "hop_1_crypto_send",
|
||||
StepCode: "hop.1.crypto.send",
|
||||
Action: discovery.RailOperationSend,
|
||||
StepRef: "hop_1_crypto_fee",
|
||||
StepCode: "hop.1.crypto.fee",
|
||||
Action: discovery.RailOperationFee,
|
||||
Rail: discovery.RailCrypto,
|
||||
Gateway: "crypto_rail_gateway_arbitrum_sepolia",
|
||||
InstanceID: "crypto_rail_gateway_arbitrum_sepolia",
|
||||
},
|
||||
StepExecution: agg.StepExecution{
|
||||
StepRef: "hop_1_crypto_send",
|
||||
StepCode: "hop.1.crypto.send",
|
||||
StepRef: "hop_1_crypto_fee",
|
||||
StepCode: "hop.1.crypto.fee",
|
||||
Attempt: 1,
|
||||
},
|
||||
}
|
||||
@@ -461,10 +418,13 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_ResolvesFeeAddressFromFeeWalletRef(
|
||||
if got, want := managedWalletReq.GetWalletRef(), "fee-wallet-ref"; got != want {
|
||||
t.Fatalf("fee wallet ref lookup mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
if got, want := len(submitRequests), 2; got != want {
|
||||
if got, want := len(submitRequests), 1; got != want {
|
||||
t.Fatalf("submit transfer calls mismatch: got=%d want=%d", got, want)
|
||||
}
|
||||
feeReq := submitRequests[1]
|
||||
feeReq := submitRequests[0]
|
||||
if got, want := feeReq.GetAmount().GetAmount(), "0.7"; got != want {
|
||||
t.Fatalf("fee amount mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
if got, want := feeReq.GetDestination().GetExternalAddress(), "TUA_FEE_FROM_WALLET"; got != want {
|
||||
t.Fatalf("fee destination mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user