fixed fee direction
This commit is contained in:
@@ -47,7 +47,7 @@ func (e *gatewayCryptoExecutor) ExecuteCrypto(ctx context.Context, req sexec.Ste
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
destination, err := e.resolveDestination(req.Payment, action)
|
||||
destination, err := e.resolveDestination(ctx, client, req.Payment, action)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -223,7 +223,7 @@ func (e *gatewayCryptoExecutor) submitWalletFeeTransfer(
|
||||
return nil
|
||||
}
|
||||
|
||||
destination, err := e.resolveDestination(req.Payment, discovery.RailOperationFee)
|
||||
destination, err := e.resolveDestination(ctx, client, req.Payment, discovery.RailOperationFee)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -341,7 +341,12 @@ func isWalletDebitFeeLine(line *paymenttypes.FeeLine) bool {
|
||||
return strings.EqualFold(strings.TrimSpace(meta["fee_target"]), "wallet")
|
||||
}
|
||||
|
||||
func (e *gatewayCryptoExecutor) resolveDestination(payment *agg.Payment, action model.RailOperation) (*chainv1.TransferDestination, error) {
|
||||
func (e *gatewayCryptoExecutor) resolveDestination(
|
||||
ctx context.Context,
|
||||
client chainclient.Client,
|
||||
payment *agg.Payment,
|
||||
action model.RailOperation,
|
||||
) (*chainv1.TransferDestination, error) {
|
||||
if payment == nil {
|
||||
return nil, merrors.InvalidArgument("crypto send: payment is required")
|
||||
}
|
||||
@@ -367,7 +372,7 @@ func (e *gatewayCryptoExecutor) resolveDestination(payment *agg.Payment, action
|
||||
Memo: strings.TrimSpace(destination.ExternalChain.Memo),
|
||||
}, nil
|
||||
case model.EndpointTypeCard:
|
||||
address, err := e.resolveCardFundingAddress(payment, action)
|
||||
address, err := e.resolveCardFundingAddress(ctx, client, payment, action)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -381,7 +386,12 @@ func (e *gatewayCryptoExecutor) resolveDestination(payment *agg.Payment, action
|
||||
}
|
||||
}
|
||||
|
||||
func (e *gatewayCryptoExecutor) resolveCardFundingAddress(payment *agg.Payment, action model.RailOperation) (string, error) {
|
||||
func (e *gatewayCryptoExecutor) resolveCardFundingAddress(
|
||||
ctx context.Context,
|
||||
client chainclient.Client,
|
||||
payment *agg.Payment,
|
||||
action model.RailOperation,
|
||||
) (string, error) {
|
||||
if payment == nil {
|
||||
return "", merrors.InvalidArgument("crypto send: payment is required")
|
||||
}
|
||||
@@ -395,6 +405,13 @@ func (e *gatewayCryptoExecutor) resolveCardFundingAddress(payment *agg.Payment,
|
||||
}
|
||||
switch action {
|
||||
case discovery.RailOperationFee:
|
||||
if feeWalletRef := strings.TrimSpace(route.FeeWalletRef); feeWalletRef != "" {
|
||||
address, err := resolveManagedWalletDepositAddress(ctx, client, feeWalletRef)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
if feeAddress := strings.TrimSpace(route.FeeAddress); feeAddress != "" {
|
||||
return feeAddress, nil
|
||||
}
|
||||
@@ -406,6 +423,28 @@ func (e *gatewayCryptoExecutor) resolveCardFundingAddress(payment *agg.Payment,
|
||||
return address, nil
|
||||
}
|
||||
|
||||
func resolveManagedWalletDepositAddress(ctx context.Context, client chainclient.Client, walletRef string) (string, error) {
|
||||
if client == nil {
|
||||
return "", merrors.InvalidArgument("crypto send: gateway client is required to resolve fee wallet")
|
||||
}
|
||||
ref := strings.TrimSpace(walletRef)
|
||||
if ref == "" {
|
||||
return "", merrors.InvalidArgument("crypto send: fee wallet ref is required")
|
||||
}
|
||||
resp, err := client.GetManagedWallet(ctx, &chainv1.GetManagedWalletRequest{WalletRef: ref})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if resp == nil || resp.GetWallet() == nil {
|
||||
return "", merrors.Internal("crypto send: fee wallet response is missing")
|
||||
}
|
||||
address := strings.TrimSpace(resp.GetWallet().GetDepositAddress())
|
||||
if address == "" {
|
||||
return "", merrors.InvalidArgument("crypto send: fee wallet deposit address is required")
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
|
||||
func destinationCardGatewayKey(payment *agg.Payment) string {
|
||||
if payment == nil || payment.QuoteSnapshot == nil || payment.QuoteSnapshot.Route == nil {
|
||||
return ""
|
||||
|
||||
@@ -332,6 +332,135 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_SubmitsWalletFeeTransferOnSend(t *t
|
||||
}
|
||||
}
|
||||
|
||||
func TestGatewayCryptoExecutor_ExecuteCrypto_ResolvesFeeAddressFromFeeWalletRef(t *testing.T) {
|
||||
orgID := bson.NewObjectID()
|
||||
|
||||
submitRequests := make([]*chainv1.SubmitTransferRequest, 0, 2)
|
||||
var managedWalletReq *chainv1.GetManagedWalletRequest
|
||||
client := &chainclient.Fake{
|
||||
GetManagedWalletFn: func(_ context.Context, req *chainv1.GetManagedWalletRequest) (*chainv1.GetManagedWalletResponse, error) {
|
||||
managedWalletReq = req
|
||||
return &chainv1.GetManagedWalletResponse{
|
||||
Wallet: &chainv1.ManagedWallet{
|
||||
WalletRef: "fee-wallet-ref",
|
||||
DepositAddress: "TUA_FEE_FROM_WALLET",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
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))
|
||||
return nil, nil
|
||||
}
|
||||
},
|
||||
}
|
||||
resolver := &fakeGatewayInvokeResolver{client: client}
|
||||
registry := &fakeGatewayRegistry{
|
||||
items: []*model.GatewayInstanceDescriptor{
|
||||
{
|
||||
ID: "crypto_rail_gateway_arbitrum_sepolia",
|
||||
InstanceID: "crypto_rail_gateway_arbitrum_sepolia",
|
||||
Rail: discovery.RailCrypto,
|
||||
InvokeURI: "grpc://crypto-gateway",
|
||||
IsEnabled: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
executor := &gatewayCryptoExecutor{
|
||||
gatewayInvokeResolver: resolver,
|
||||
gatewayRegistry: registry,
|
||||
cardGatewayRoutes: map[string]CardGatewayRoute{
|
||||
paymenttypes.DefaultCardsGatewayID: {FundingAddress: "TUA_DEST", FeeWalletRef: "fee-wallet-ref"},
|
||||
},
|
||||
}
|
||||
|
||||
req := sexec.StepRequest{
|
||||
Payment: &agg.Payment{
|
||||
OrganizationBoundBase: pm.OrganizationBoundBase{OrganizationRef: orgID},
|
||||
PaymentRef: "payment-1",
|
||||
IdempotencyKey: "idem-1",
|
||||
IntentSnapshot: model.PaymentIntent{
|
||||
Ref: "intent-1",
|
||||
Source: model.PaymentEndpoint{
|
||||
Type: model.EndpointTypeManagedWallet,
|
||||
ManagedWallet: &model.ManagedWalletEndpoint{
|
||||
ManagedWalletRef: "wallet-src",
|
||||
},
|
||||
},
|
||||
Destination: model.PaymentEndpoint{
|
||||
Type: model.EndpointTypeCard,
|
||||
Card: &model.CardEndpoint{Pan: "4111111111111111"},
|
||||
},
|
||||
Amount: &paymenttypes.Money{Amount: "10", Currency: "USDT"},
|
||||
},
|
||||
QuoteSnapshot: &model.PaymentQuoteSnapshot{
|
||||
DebitAmount: &paymenttypes.Money{Amount: "10.000000", Currency: "USDT"},
|
||||
FeeLines: []*paymenttypes.FeeLine{
|
||||
{
|
||||
Money: &paymenttypes.Money{Amount: "0.70", Currency: "USDT"},
|
||||
LineType: paymenttypes.PostingLineTypeFee,
|
||||
Side: paymenttypes.EntrySideDebit,
|
||||
Meta: map[string]string{"fee_target": "wallet"},
|
||||
},
|
||||
},
|
||||
Route: &paymenttypes.QuoteRouteSpecification{
|
||||
Hops: []*paymenttypes.QuoteRouteHop{
|
||||
{Index: 1, Rail: "CRYPTO", Gateway: "crypto_rail_gateway_arbitrum_sepolia", InstanceID: "crypto_rail_gateway_arbitrum_sepolia", Role: paymenttypes.QuoteRouteHopRoleSource},
|
||||
{Index: 4, Rail: "CARD", Gateway: paymenttypes.DefaultCardsGatewayID, InstanceID: paymenttypes.DefaultCardsGatewayID, Role: paymenttypes.QuoteRouteHopRoleDestination},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Step: xplan.Step{
|
||||
StepRef: "hop_1_crypto_send",
|
||||
StepCode: "hop.1.crypto.send",
|
||||
Action: discovery.RailOperationSend,
|
||||
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",
|
||||
Attempt: 1,
|
||||
},
|
||||
}
|
||||
|
||||
_, err := executor.ExecuteCrypto(context.Background(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("ExecuteCrypto returned error: %v", err)
|
||||
}
|
||||
if managedWalletReq == nil {
|
||||
t.Fatal("expected managed wallet lookup request")
|
||||
}
|
||||
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 {
|
||||
t.Fatalf("submit transfer calls mismatch: got=%d want=%d", got, want)
|
||||
}
|
||||
feeReq := submitRequests[1]
|
||||
if got, want := feeReq.GetDestination().GetExternalAddress(), "TUA_FEE_FROM_WALLET"; got != want {
|
||||
t.Fatalf("fee destination mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGatewayCryptoExecutor_ExecuteCrypto_FeeActionUsesWalletFeeAmount(t *testing.T) {
|
||||
orgID := bson.NewObjectID()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user