2025-10-06 13:38:53 +07:00
|
|
|
package wallet
|
|
|
|
|
|
|
|
import (
|
|
|
|
context "context"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/phamminh0811/private-grpc/nockchain"
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
"google.golang.org/grpc/credentials"
|
|
|
|
)
|
|
|
|
|
|
|
|
type NockchainClient struct {
|
|
|
|
conn *grpc.ClientConn
|
|
|
|
client nockchain.NockchainServiceClient
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewNockchainClient creates a new gRPC client connection
|
|
|
|
func NewNockchainClient(address string) (*NockchainClient, error) {
|
|
|
|
creds := credentials.NewClientTLSFromCert(nil, "")
|
|
|
|
|
|
|
|
conn, err := grpc.NewClient(
|
|
|
|
address,
|
|
|
|
grpc.WithTransportCredentials(creds),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to connect: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
client := nockchain.NewNockchainServiceClient(conn)
|
|
|
|
|
|
|
|
return &NockchainClient{
|
|
|
|
conn: conn,
|
|
|
|
client: client,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nc *NockchainClient) Close() error {
|
|
|
|
return nc.conn.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nc *NockchainClient) WalletGetBalance(address string) (*nockchain.WalletBalanceData, error) {
|
|
|
|
pageToken := ""
|
|
|
|
allNotes := []*nockchain.BalanceEntry{}
|
|
|
|
var height *nockchain.BlockHeight
|
|
|
|
var blockId *nockchain.Hash
|
|
|
|
for {
|
|
|
|
req := nockchain.WalletGetBalanceRequest{
|
|
|
|
Address: address,
|
|
|
|
Page: &nockchain.PageRequest{
|
|
|
|
ClientPageItemsLimit: 0,
|
|
|
|
PageToken: pageToken,
|
|
|
|
MaxBytes: 0,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
resp, err := nc.client.WalletGetBalance(context.Background(), &req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
balance := nockchain.WalletBalanceData{}
|
|
|
|
switch resp.Result.(type) {
|
|
|
|
case *nockchain.WalletGetBalanceResponse_Balance:
|
|
|
|
balance = *resp.GetBalance()
|
|
|
|
case *nockchain.WalletGetBalanceResponse_Error:
|
|
|
|
return nil, fmt.Errorf("error: %s", resp.GetError().Message)
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("invalid result type")
|
|
|
|
}
|
|
|
|
|
|
|
|
if height == nil {
|
|
|
|
height = balance.Height
|
|
|
|
blockId = balance.BlockId
|
|
|
|
}
|
|
|
|
|
|
|
|
if balance.Height != height || balance.BlockId != blockId {
|
|
|
|
return nil, fmt.Errorf("snapshot changed during pagination; retry")
|
|
|
|
}
|
|
|
|
|
|
|
|
allNotes = append(allNotes, balance.Notes...)
|
|
|
|
if balance.Page.NextPageToken == "" {
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
pageToken = balance.Page.NextPageToken
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &nockchain.WalletBalanceData{
|
|
|
|
Notes: allNotes,
|
|
|
|
Height: height,
|
|
|
|
BlockId: blockId,
|
|
|
|
Page: &nockchain.PageResponse{
|
|
|
|
NextPageToken: "",
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nc *NockchainClient) TxAccepted(txId string) (*nockchain.TransactionAcceptedResponse, error) {
|
|
|
|
req := nockchain.TransactionAcceptedRequest{
|
|
|
|
TxId: &nockchain.Base58Hash{
|
|
|
|
Hash: txId,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := nc.client.TransactionAccepted(context.Background(), &req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
switch resp.Result.(type) {
|
|
|
|
case *nockchain.TransactionAcceptedResponse_Accepted:
|
|
|
|
return resp, nil
|
|
|
|
case *nockchain.TransactionAcceptedResponse_Error:
|
|
|
|
return nil, fmt.Errorf("error: %s", resp.GetError().Message)
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("invalid result type")
|
|
|
|
}
|
|
|
|
}
|
2025-10-07 11:10:08 +07:00
|
|
|
|
|
|
|
func (nc *NockchainClient) WalletSendTransaction(tx *nockchain.RawTx) (*nockchain.WalletSendTransactionResponse, error) {
|
|
|
|
inputs := []*nockchain.NamedInput{}
|
|
|
|
for _, i := range tx.Inputs {
|
|
|
|
input, err := ConvertInput(i)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error converting input: %v", err)
|
|
|
|
}
|
|
|
|
inputs = append(inputs, input)
|
|
|
|
}
|
|
|
|
req := nockchain.WalletSendTransactionRequest{
|
|
|
|
TxId: ParseHash(tx.TxId),
|
|
|
|
RawTx: &nockchain.RawTransaction{
|
|
|
|
NamedInputs: inputs,
|
|
|
|
TotalFees: &nockchain.Nicks{Value: tx.TotalFees},
|
|
|
|
TimelockRange: &nockchain.TimeLockRangeAbsolute{
|
|
|
|
Min: (*nockchain.BlockHeight)(tx.TimelockRange.Min),
|
|
|
|
Max: (*nockchain.BlockHeight)(tx.TimelockRange.Max),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := nc.client.WalletSendTransaction(context.Background(), &req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch resp.Result.(type) {
|
|
|
|
case *nockchain.WalletSendTransactionResponse_Ack:
|
|
|
|
return resp, nil
|
|
|
|
case *nockchain.WalletSendTransactionResponse_Error:
|
|
|
|
return nil, fmt.Errorf("error: %s", resp.GetError().Message)
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("invalid result type")
|
|
|
|
}
|
|
|
|
}
|