Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions pkg/chains/stellar/proto_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,105 @@ func ConvertReadContractRequestFromProto(p *ReadContractRequest) (stellar.ReadCo
LedgerSequence: p.GetLedgerSequence(),
}, nil
}

// ConvertSubmitTransactionRequestToProto converts a domain SubmitTransactionRequest to proto.
func ConvertSubmitTransactionRequestToProto(req stellar.SubmitTransactionRequest) (*SubmitTransactionRequest, error) {
if req.ContractID == "" {
return nil, errors.New("contract_id is required")
}
if req.Function == "" {
return nil, errors.New("function is required")
}
args := make([]*scval.ScVal, len(req.Args))
for i, sv := range req.Args {
psv, err := stellarcap.ScValToProto(sv)
if err != nil {
return nil, fmt.Errorf("args[%d]: %w", i, err)
}
args[i] = psv
}
return &SubmitTransactionRequest{
IdempotencyKey: req.IdempotencyKey,
FromAddress: req.FromAddress,
ContractId: req.ContractID,
Function: req.Function,
Args: args,
LedgerBoundsOffset: req.LedgerBoundsOffset,
}, nil
}

// ConvertSubmitTransactionRequestFromProto converts proto SubmitTransactionRequest to domain.
func ConvertSubmitTransactionRequestFromProto(p *SubmitTransactionRequest) (stellar.SubmitTransactionRequest, error) {
if p == nil {
return stellar.SubmitTransactionRequest{}, errors.New("submit transaction request is nil")
}
if p.GetContractId() == "" {
return stellar.SubmitTransactionRequest{}, errors.New("contract_id is required")
}
if p.GetFunction() == "" {
return stellar.SubmitTransactionRequest{}, errors.New("function is required")
}
pArgs := p.GetArgs()
var args []stellar.ScVal
if len(pArgs) > 0 {
args = make([]stellar.ScVal, len(pArgs))
for i, psv := range pArgs {
sv, err := stellarcap.ProtoToScVal(psv)
if err != nil {
return stellar.SubmitTransactionRequest{}, fmt.Errorf("args[%d]: %w", i, err)
}
args[i] = sv
}
}
return stellar.SubmitTransactionRequest{
IdempotencyKey: p.GetIdempotencyKey(),
FromAddress: p.GetFromAddress(),
ContractID: p.GetContractId(),
Function: p.GetFunction(),
Args: args,
LedgerBoundsOffset: p.GetLedgerBoundsOffset(),
}, nil
}

// ConvertSubmitTransactionResponseToProto converts a domain SubmitTransactionResponse to proto.
func ConvertSubmitTransactionResponseToProto(reply *stellar.SubmitTransactionResponse) (*SubmitTransactionResponse, error) {
if reply == nil {
return nil, errors.New("submit transaction reply is nil")
}
var resultXDR, resultMetaXDR []byte
if reply.ResultXDR != "" {
var err error
resultXDR, err = base64.StdEncoding.DecodeString(reply.ResultXDR)
if err != nil {
return nil, fmt.Errorf("invalid result xdr %q: %w", reply.ResultXDR, err)
}
}
if reply.ResultMetaXDR != "" {
var err error
resultMetaXDR, err = base64.StdEncoding.DecodeString(reply.ResultMetaXDR)
if err != nil {
return nil, fmt.Errorf("invalid result meta xdr %q: %w", reply.ResultMetaXDR, err)
}
}
return &SubmitTransactionResponse{
TxStatus: TxStatus(reply.TxStatus),
TxHash: reply.TxHash,
TxIdempotencyKey: reply.TxIdempotencyKey,
ResultXdr: resultXDR,
ResultMetaXdr: resultMetaXDR,
}, nil
}

// ConvertSubmitTransactionResponseFromProto converts proto SubmitTransactionResponse to domain.
func ConvertSubmitTransactionResponseFromProto(p *SubmitTransactionResponse) (*stellar.SubmitTransactionResponse, error) {
if p == nil {
return nil, errors.New("submit transaction reply is nil")
}
return &stellar.SubmitTransactionResponse{
TxStatus: stellar.TransactionStatus(p.GetTxStatus()),
TxHash: p.GetTxHash(),
TxIdempotencyKey: p.GetTxIdempotencyKey(),
ResultXDR: base64.StdEncoding.EncodeToString(p.GetResultXdr()),
ResultMetaXDR: base64.StdEncoding.EncodeToString(p.GetResultMetaXdr()),
}, nil
}
150 changes: 150 additions & 0 deletions pkg/chains/stellar/proto_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/require"

conv "github.com/smartcontractkit/chainlink-common/pkg/chains/stellar"
"github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/chain-capabilities/stellar/scval"
stellartypes "github.com/smartcontractkit/chainlink-common/pkg/types/chains/stellar"
)

Expand Down Expand Up @@ -308,6 +309,155 @@ func TestConvertGetLedgerEntriesResponseFromProto_NilEntry(t *testing.T) {

// ---- ConvertGetLatestLedgerResponseToProto error cases ----------------------

func TestConvertSubmitTransactionRequest_RoundTrip(t *testing.T) {
boolVal := true
u64 := uint64(42)
sym := "amount"
domain := stellartypes.SubmitTransactionRequest{
IdempotencyKey: "idem-123",
FromAddress: "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF",
ContractID: "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4",
Function: "transfer",
Args: []stellartypes.ScVal{
{Type: stellartypes.ScValTypeBool, Bool: &boolVal},
{Type: stellartypes.ScValTypeU64, U64: &u64},
{Type: stellartypes.ScValTypeSymbol, Symbol: &sym},
},
LedgerBoundsOffset: 10,
}

proto, err := conv.ConvertSubmitTransactionRequestToProto(domain)
require.NoError(t, err)
require.Equal(t, "idem-123", proto.GetIdempotencyKey())
require.Equal(t, "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4", proto.GetContractId())
require.Equal(t, "transfer", proto.GetFunction())
require.Len(t, proto.GetArgs(), 3)
require.Equal(t, uint32(10), proto.GetLedgerBoundsOffset())

got, err := conv.ConvertSubmitTransactionRequestFromProto(proto)
require.NoError(t, err)
require.Equal(t, domain, got)
}

func TestConvertSubmitTransactionRequest_NoArgs(t *testing.T) {
domain := stellartypes.SubmitTransactionRequest{
ContractID: "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4",
Function: "ping",
}

proto, err := conv.ConvertSubmitTransactionRequestToProto(domain)
require.NoError(t, err)
require.Empty(t, proto.GetArgs())

got, err := conv.ConvertSubmitTransactionRequestFromProto(proto)
require.NoError(t, err)
require.Equal(t, domain, got)
}

func TestConvertSubmitTransactionRequestToProto_MissingContractID(t *testing.T) {
_, err := conv.ConvertSubmitTransactionRequestToProto(stellartypes.SubmitTransactionRequest{Function: "fn"})
require.EqualError(t, err, "contract_id is required")
}

func TestConvertSubmitTransactionRequestToProto_MissingFunction(t *testing.T) {
_, err := conv.ConvertSubmitTransactionRequestToProto(stellartypes.SubmitTransactionRequest{ContractID: "C_X"})
require.EqualError(t, err, "function is required")
}

func TestConvertSubmitTransactionRequestToProto_BadArg(t *testing.T) {
_, err := conv.ConvertSubmitTransactionRequestToProto(stellartypes.SubmitTransactionRequest{
ContractID: "C_X",
Function: "fn",
Args: []stellartypes.ScVal{{Type: stellartypes.ScValTypeBool}}, // Bool is nil
})
require.Error(t, err)
require.Contains(t, err.Error(), "args[0]")
}

func TestConvertSubmitTransactionRequestFromProto_Nil(t *testing.T) {
_, err := conv.ConvertSubmitTransactionRequestFromProto(nil)
require.EqualError(t, err, "submit transaction request is nil")
}

func TestConvertSubmitTransactionRequestFromProto_MissingContractID(t *testing.T) {
_, err := conv.ConvertSubmitTransactionRequestFromProto(&conv.SubmitTransactionRequest{Function: "fn"})
require.EqualError(t, err, "contract_id is required")
}

func TestConvertSubmitTransactionRequestFromProto_MissingFunction(t *testing.T) {
_, err := conv.ConvertSubmitTransactionRequestFromProto(&conv.SubmitTransactionRequest{ContractId: "C_X"})
require.EqualError(t, err, "function is required")
}

func TestConvertSubmitTransactionResponse_RoundTrip(t *testing.T) {
domain := &stellartypes.SubmitTransactionResponse{
TxStatus: stellartypes.TxSuccess,
TxHash: "abc123hash",
TxIdempotencyKey: "idem-456",
ResultXDR: base64.StdEncoding.EncodeToString([]byte("result")),
ResultMetaXDR: base64.StdEncoding.EncodeToString([]byte("meta")),
}

proto, err := conv.ConvertSubmitTransactionResponseToProto(domain)
require.NoError(t, err)
require.Equal(t, conv.TxStatus_TX_STATUS_SUCCESS, proto.GetTxStatus())

got, err := conv.ConvertSubmitTransactionResponseFromProto(proto)
require.NoError(t, err)
require.Equal(t, domain, got)
}

func TestConvertSubmitTransactionResponse_RoundTrip_EmptyResultFields(t *testing.T) {
domain := &stellartypes.SubmitTransactionResponse{
TxStatus: stellartypes.TxFatal,
TxHash: "",
TxIdempotencyKey: "idem-789",
}

proto, err := conv.ConvertSubmitTransactionResponseToProto(domain)
require.NoError(t, err)

got, err := conv.ConvertSubmitTransactionResponseFromProto(proto)
require.NoError(t, err)
require.Equal(t, domain, got)
}

func TestConvertSubmitTransactionResponseToProto_Nil(t *testing.T) {
_, err := conv.ConvertSubmitTransactionResponseToProto(nil)
require.EqualError(t, err, "submit transaction reply is nil")
}

func TestConvertSubmitTransactionResponseFromProto_Nil(t *testing.T) {
_, err := conv.ConvertSubmitTransactionResponseFromProto(nil)
require.EqualError(t, err, "submit transaction reply is nil")
}

func TestConvertSubmitTransactionResponseToProto_InvalidResultXDR(t *testing.T) {
_, err := conv.ConvertSubmitTransactionResponseToProto(&stellartypes.SubmitTransactionResponse{
ResultXDR: "!!!invalid!!!",
})
require.Error(t, err)
require.Contains(t, err.Error(), "invalid result xdr")
}

func TestConvertSubmitTransactionResponseToProto_InvalidResultMetaXDR(t *testing.T) {
_, err := conv.ConvertSubmitTransactionResponseToProto(&stellartypes.SubmitTransactionResponse{
ResultMetaXDR: "!!!invalid!!!",
})
require.Error(t, err)
require.Contains(t, err.Error(), "invalid result meta xdr")
}

func TestConvertSubmitTransactionRequestFromProto_BadArg(t *testing.T) {
_, err := conv.ConvertSubmitTransactionRequestFromProto(&conv.SubmitTransactionRequest{
ContractId: "C_X",
Function: "fn",
Args: []*scval.ScVal{{}}, // missing oneof value
})
require.Error(t, err)
require.Contains(t, err.Error(), "args[0]")
}

func TestConvertGetLatestLedgerResponseToProto_InvalidFields(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading
Loading