diff --git a/types/v1/tx.go b/types/v1/tx.go index a6a3ddd..a336188 100644 --- a/types/v1/tx.go +++ b/types/v1/tx.go @@ -276,6 +276,52 @@ func (t *Tx) determineTransactionDirection(address, from, to string) Direction { return DirectionOutgoing } +func (t *Tx) IsUTXO() bool { + return t.Type == TxTransfer && len(t.Outputs) > 0 +} + +func (t *Tx) GetUTXOValueFor(address string) (Amount, error) { + isTransferOut := false + + var totalInputValue uint64 + var addressInputValue uint64 + for _, input := range t.Inputs { + value, err := strconv.ParseUint(string(input.Value), 10, 64) + if err != nil { + return "0", fmt.Errorf("input value for address %s: %v", input.Address, err) + } + + totalInputValue += value + + if input.Address == address { + addressInputValue = value + isTransferOut = true + } + } + + var addressOutputValue uint64 + var totalOutputValue uint64 + for _, output := range t.Outputs { + value, err := strconv.ParseUint(string(output.Value), 10, 64) + if err != nil { + return "0", fmt.Errorf("output value for address %s: %v", output.Address, err) + } + totalOutputValue += value + if output.Address == address { + addressOutputValue = value + } + } + + var result uint64 + if isTransferOut { + result = addressInputValue - (totalInputValue-totalOutputValue)/uint64(len(t.Inputs)) - addressOutputValue + } else { + result = addressOutputValue + } + + return Amount(fmt.Sprintf("%d", result)), nil +} + func InferDirection(tx *Tx, addressSet mapset.Set) Direction { inputSet := mapset.NewSet() for _, address := range tx.Inputs { diff --git a/types/v1/tx_test.go b/types/v1/tx_test.go index 8892549..4e80044 100644 --- a/types/v1/tx_test.go +++ b/types/v1/tx_test.go @@ -368,3 +368,73 @@ func TestTx_GetDirection(t *testing.T) { } } + +func TestUTXOValueByAddress(t *testing.T) { + tests := []struct { + name string + tx Tx + address string + expected Amount + expectedErrAssertion assert.ErrorAssertionFunc + }{ + { + name: "transfer_self", + tx: Tx{ + Inputs: []TxOutput{{ + Address: "addr", + Value: "1000", + }}, + Outputs: []TxOutput{{ + Address: "addr", + Value: "1000", + }}, + }, + address: "addr", + expected: "0", + expectedErrAssertion: assert.NoError, + }, + { + name: "transfer_in", + tx: Tx{ + Outputs: []TxOutput{{ + Address: "addr", + Value: "1000", + }}, + }, + address: "addr", + expected: "1000", + expectedErrAssertion: assert.NoError, + }, + { + name: "transfer_out_with_utxo", + tx: Tx{ + Inputs: []TxOutput{{ + Address: "addr", + Value: "1000", + }}, + Outputs: []TxOutput{ + { + Address: "addr", + Value: "100", + }, + { + Address: "addr1", + Value: "800", + }, + }, + }, + address: "addr", + expected: "800", + expectedErrAssertion: assert.NoError, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + result, err := tc.tx.GetUTXOValueFor(tc.address) + tc.expectedErrAssertion(t, err) + + assert.Equal(t, tc.expected, result) + }) + } +}