feat_: share and parse transaction deep link
This commit is contained in:
parent
86cd41d04e
commit
4c889399eb
|
@ -45,11 +45,21 @@ type ContactURLData struct {
|
||||||
PublicKey string `json:"publicKey"`
|
PublicKey string `json:"publicKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TransactionURLData struct {
|
||||||
|
TxType int `json:"txType"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
Asset string `json:"asset"`
|
||||||
|
ChainID int `json:"chainId"`
|
||||||
|
ToAsset string `json:"toAsset"`
|
||||||
|
}
|
||||||
|
|
||||||
type URLDataResponse struct {
|
type URLDataResponse struct {
|
||||||
Community *CommunityURLData `json:"community"`
|
Community *CommunityURLData `json:"community"`
|
||||||
Channel *CommunityChannelURLData `json:"channel"`
|
Channel *CommunityChannelURLData `json:"channel"`
|
||||||
Contact *ContactURLData `json:"contact"`
|
Contact *ContactURLData `json:"contact"`
|
||||||
Shard *shard.Shard `json:"shard,omitempty"`
|
Transaction *TransactionURLData `json:"tx"`
|
||||||
|
Shard *shard.Shard `json:"shard,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseShareURL = "https://status.app"
|
const baseShareURL = "https://status.app"
|
||||||
|
@ -58,12 +68,14 @@ const userWithDataPath = "u/"
|
||||||
const communityPath = "c#"
|
const communityPath = "c#"
|
||||||
const communityWithDataPath = "c/"
|
const communityWithDataPath = "c/"
|
||||||
const channelPath = "cc/"
|
const channelPath = "cc/"
|
||||||
|
const transactionPath = "tx/"
|
||||||
|
|
||||||
const sharedURLUserPrefix = baseShareURL + "/" + userPath
|
const sharedURLUserPrefix = baseShareURL + "/" + userPath
|
||||||
const sharedURLUserPrefixWithData = baseShareURL + "/" + userWithDataPath
|
const sharedURLUserPrefixWithData = baseShareURL + "/" + userWithDataPath
|
||||||
const sharedURLCommunityPrefix = baseShareURL + "/" + communityPath
|
const sharedURLCommunityPrefix = baseShareURL + "/" + communityPath
|
||||||
const sharedURLCommunityPrefixWithData = baseShareURL + "/" + communityWithDataPath
|
const sharedURLCommunityPrefixWithData = baseShareURL + "/" + communityWithDataPath
|
||||||
const sharedURLChannelPrefixWithData = baseShareURL + "/" + channelPath
|
const sharedURLChannelPrefixWithData = baseShareURL + "/" + channelPath
|
||||||
|
const sharedURLTransaction = baseShareURL + "/" + transactionPath
|
||||||
|
|
||||||
const channelUUIDRegExp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
const channelUUIDRegExp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||||
|
|
||||||
|
@ -302,6 +314,77 @@ func (m *Messenger) prepareEncodedCommunityChannelData(community *communities.Co
|
||||||
return encodedData, shortKey, nil
|
return encodedData, shortKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) ShareTransactionURL(request *requests.TransactionShareURL) (string, error) {
|
||||||
|
encodedData, err := m.prepareTransactionUrl(request)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s%s", sharedURLTransaction, encodedData), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) prepareTransactionUrl(request *requests.TransactionShareURL) (string, error) {
|
||||||
|
if err := request.Validate(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
txProto := &protobuf.Transaction{
|
||||||
|
TxType: uint32(request.TxType),
|
||||||
|
Address: request.Address,
|
||||||
|
Amount: request.Amount,
|
||||||
|
Asset: request.Asset,
|
||||||
|
ChainId: uint32(request.ChainID),
|
||||||
|
ToAsset: request.ToAsset,
|
||||||
|
}
|
||||||
|
txData, err := proto.Marshal(txProto)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
urlDataProto := &protobuf.URLData{
|
||||||
|
Content: txData,
|
||||||
|
}
|
||||||
|
|
||||||
|
urlData, err := proto.Marshal(urlDataProto)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
encodedData, err := encodeDataURL(urlData)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return encodedData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTransactionURL(data string) (*URLDataResponse, error) {
|
||||||
|
urlData, err := decodeDataURL(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var urlDataProto protobuf.URLData
|
||||||
|
err = proto.Unmarshal(urlData, &urlDataProto)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var txProto protobuf.Transaction
|
||||||
|
err = proto.Unmarshal(urlDataProto.Content, &txProto)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &URLDataResponse{
|
||||||
|
Transaction: &TransactionURLData{
|
||||||
|
TxType: int(txProto.TxType),
|
||||||
|
Address: txProto.Address,
|
||||||
|
Amount: txProto.Amount,
|
||||||
|
Asset: txProto.Asset,
|
||||||
|
ChainID: int(txProto.ChainId),
|
||||||
|
ToAsset: txProto.ToAsset,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Messenger) ShareCommunityChannelURLWithData(request *requests.CommunityChannelShareURL) (string, error) {
|
func (m *Messenger) ShareCommunityChannelURLWithData(request *requests.CommunityChannelShareURL) (string, error) {
|
||||||
if err := request.Validate(); err != nil {
|
if err := request.Validate(); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -518,7 +601,8 @@ func IsStatusSharedURL(url string) bool {
|
||||||
strings.HasPrefix(url, sharedURLUserPrefixWithData) ||
|
strings.HasPrefix(url, sharedURLUserPrefixWithData) ||
|
||||||
strings.HasPrefix(url, sharedURLCommunityPrefix) ||
|
strings.HasPrefix(url, sharedURLCommunityPrefix) ||
|
||||||
strings.HasPrefix(url, sharedURLCommunityPrefixWithData) ||
|
strings.HasPrefix(url, sharedURLCommunityPrefixWithData) ||
|
||||||
strings.HasPrefix(url, sharedURLChannelPrefixWithData)
|
strings.HasPrefix(url, sharedURLChannelPrefixWithData) ||
|
||||||
|
strings.HasPrefix(url, sharedURLTransaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitSharedURLData(data string) (string, string, error) {
|
func splitSharedURLData(data string) (string, string, error) {
|
||||||
|
@ -576,6 +660,11 @@ func ParseSharedURL(url string) (*URLDataResponse, error) {
|
||||||
return parseCommunityChannelURLWithData(encodedData, chatKey)
|
return parseCommunityChannelURLWithData(encodedData, chatKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(url, sharedURLTransaction) {
|
||||||
|
trimmedURL := strings.TrimPrefix(url, sharedURLTransaction)
|
||||||
|
return parseTransactionURL(trimmedURL)
|
||||||
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("not a status shared url")
|
return nil, fmt.Errorf("not a status shared url")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -497,3 +497,57 @@ func (s *MessengerShareUrlsSuite) TestShareAndParseUserURLWithData() {
|
||||||
s.Require().Equal(contact.DisplayName, urlData.Contact.DisplayName)
|
s.Require().Equal(contact.DisplayName, urlData.Contact.DisplayName)
|
||||||
s.Require().Equal(shortKey, urlData.Contact.PublicKey)
|
s.Require().Equal(shortKey, urlData.Contact.PublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *MessengerShareUrlsSuite) TestShareTransactionURL() {
|
||||||
|
request := &requests.TransactionShareURL{
|
||||||
|
TxType: 0,
|
||||||
|
Address: "0x1234567890abcdef",
|
||||||
|
Amount: "0.123",
|
||||||
|
Asset: "ETH",
|
||||||
|
ChainID: 123,
|
||||||
|
ToAsset: "SNT",
|
||||||
|
}
|
||||||
|
url, err := s.m.ShareTransactionURL(request)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEmpty(url)
|
||||||
|
|
||||||
|
data, err := s.m.prepareTransactionUrl(request)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
expectedURL := fmt.Sprintf("%s/tx/%s", baseShareURL, data)
|
||||||
|
s.Require().Equal(expectedURL, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerShareUrlsSuite) TestShareTransactionURLInvalid() {
|
||||||
|
request := &requests.TransactionShareURL{
|
||||||
|
TxType: -1,
|
||||||
|
}
|
||||||
|
_, err := s.m.ShareTransactionURL(request)
|
||||||
|
s.Require().Error(err)
|
||||||
|
_, err = s.m.prepareTransactionUrl(request)
|
||||||
|
s.Require().Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerShareUrlsSuite) TestShareAndParseTransactionURL() {
|
||||||
|
request := &requests.TransactionShareURL{
|
||||||
|
TxType: 0,
|
||||||
|
Address: "0x1234567890abcdef",
|
||||||
|
Amount: "0.123",
|
||||||
|
Asset: "ETH",
|
||||||
|
ChainID: 123,
|
||||||
|
ToAsset: "SNT",
|
||||||
|
}
|
||||||
|
url, err := s.m.ShareTransactionURL(request)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
urlData, err := ParseSharedURL(url)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(urlData)
|
||||||
|
|
||||||
|
s.Require().Equal(request.TxType, urlData.Transaction.TxType)
|
||||||
|
s.Require().Equal(request.Address, urlData.Transaction.Address)
|
||||||
|
s.Require().Equal(request.Amount, urlData.Transaction.Amount)
|
||||||
|
s.Require().Equal(request.Asset, urlData.Transaction.Asset)
|
||||||
|
s.Require().Equal(request.ChainID, urlData.Transaction.ChainID)
|
||||||
|
s.Require().Equal(request.ToAsset, urlData.Transaction.ToAsset)
|
||||||
|
}
|
||||||
|
|
|
@ -28,8 +28,17 @@ message User {
|
||||||
string color = 3;
|
string color = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message Transaction {
|
||||||
|
uint32 txType = 1;
|
||||||
|
string address = 2;
|
||||||
|
string amount = 3;
|
||||||
|
string asset = 4;
|
||||||
|
uint32 chainId = 5;
|
||||||
|
string toAsset = 6;
|
||||||
|
}
|
||||||
|
|
||||||
message URLData {
|
message URLData {
|
||||||
// Community, Channel, or User
|
// Community, Channel, User or Transaction
|
||||||
bytes content = 1;
|
bytes content = 1;
|
||||||
Shard shard = 2;
|
Shard shard = 2;
|
||||||
}
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package requests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidTransactionType = errors.New("transaction-share-url: invalid transaction type")
|
||||||
|
)
|
||||||
|
|
||||||
|
type TransactionShareURL struct {
|
||||||
|
TxType int `json:"txType"`
|
||||||
|
Asset string `json:"asset"`
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
ChainID int `json:"chainId"`
|
||||||
|
ToAsset string `json:"toAsset"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TransactionShareURL) Validate() error {
|
||||||
|
if r.TxType < 0 {
|
||||||
|
return ErrInvalidTransactionType
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1687,6 +1687,10 @@ func (api *PublicAPI) ShareUserURLWithData(pubKey string) (string, error) {
|
||||||
return api.service.messenger.ShareUserURLWithData(pubKey)
|
return api.service.messenger.ShareUserURLWithData(pubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) ShareTransactionURL(txData *requests.TransactionShareURL) (string, error) {
|
||||||
|
return api.service.messenger.ShareTransactionURL(txData)
|
||||||
|
}
|
||||||
|
|
||||||
func (api *PublicAPI) ParseSharedURL(url string) (*protocol.URLDataResponse, error) {
|
func (api *PublicAPI) ParseSharedURL(url string) (*protocol.URLDataResponse, error) {
|
||||||
return protocol.ParseSharedURL(url)
|
return protocol.ParseSharedURL(url)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue