forked from stellar/go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
create_claimable_balance.go
198 lines (172 loc) · 5.96 KB
/
create_claimable_balance.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
//lint:file-ignore U1001 Ignore all unused code, staticcheck doesn't understand testify/suite
package txnbuild
import (
"github.com/stellar/go/amount"
"github.com/stellar/go/support/errors"
"github.com/stellar/go/xdr"
)
// CreateClaimableBalance represents the Stellar create claimable balance operation. See
// https://www.stellar.org/developers/guides/concepts/list-of-operations.html
type CreateClaimableBalance struct {
Amount string
Asset Asset
Destinations []Claimant
SourceAccount string
}
// Claimant represents a claimable balance claimant
type Claimant struct {
Destination string
Predicate xdr.ClaimPredicate
}
var (
UnconditionalPredicate = xdr.ClaimPredicate{
Type: xdr.ClaimPredicateTypeClaimPredicateUnconditional,
}
)
// NewClaimant returns a new Claimant, if predicate is nil then a Claimant with
// unconditional predicate is returned.
func NewClaimant(destination string, predicate *xdr.ClaimPredicate) Claimant {
if predicate == nil {
predicate = &UnconditionalPredicate
}
return Claimant{
Destination: destination,
Predicate: *predicate,
}
}
// AndPredicate returns a xdr.ClaimPredicate
func AndPredicate(left xdr.ClaimPredicate, right xdr.ClaimPredicate) xdr.ClaimPredicate {
predicates := []xdr.ClaimPredicate{left, right}
return xdr.ClaimPredicate{
Type: xdr.ClaimPredicateTypeClaimPredicateAnd,
AndPredicates: &predicates,
}
}
// OrPredicate returns a xdr.ClaimPredicate
func OrPredicate(left xdr.ClaimPredicate, right xdr.ClaimPredicate) xdr.ClaimPredicate {
predicates := []xdr.ClaimPredicate{left, right}
return xdr.ClaimPredicate{
Type: xdr.ClaimPredicateTypeClaimPredicateOr,
OrPredicates: &predicates,
}
}
// NotPredicate returns a new predicate inverting the passed in predicate
func NotPredicate(pred xdr.ClaimPredicate) xdr.ClaimPredicate {
innerPred := &pred // workaround to keep API the same as Or/And
return xdr.ClaimPredicate{
Type: xdr.ClaimPredicateTypeClaimPredicateNot,
NotPredicate: &innerPred,
}
}
// BeforeAbsoluteTimePredicate returns a Before Absolute Time xdr.ClaimPredicate
//
// This predicate will be fulfilled if the closing time of the ledger that
// includes the CreateClaimableBalance operation is less than this (absolute)
// Unix timestamp.
func BeforeAbsoluteTimePredicate(epochSeconds int64) xdr.ClaimPredicate {
absBefore := xdr.Int64(epochSeconds)
return xdr.ClaimPredicate{
Type: xdr.ClaimPredicateTypeClaimPredicateBeforeAbsoluteTime,
AbsBefore: &absBefore,
}
}
// BeforeRelativeTimePredicate returns a Before Relative Time xdr.ClaimPredicate
//
// This predicate will be fulfilled if the closing time of the ledger that
// includes the CreateClaimableBalance operation plus this relative time delta
// (in seconds) is less than the current time.
func BeforeRelativeTimePredicate(secondsBefore int64) xdr.ClaimPredicate {
relBefore := xdr.Int64(secondsBefore)
return xdr.ClaimPredicate{
Type: xdr.ClaimPredicateTypeClaimPredicateBeforeRelativeTime,
RelBefore: &relBefore,
}
}
// BuildXDR for CreateClaimableBalance returns a fully configured XDR Operation.
func (cb *CreateClaimableBalance) BuildXDR(withMuxedAccounts bool) (xdr.Operation, error) {
xdrAsset, err := cb.Asset.ToXDR()
if err != nil {
return xdr.Operation{}, errors.Wrap(err, "failed to set XDR 'Asset' field")
}
xdrAmount, err := amount.Parse(cb.Amount)
if err != nil {
return xdr.Operation{}, errors.Wrap(err, "failed to parse 'Amount'")
}
claimants := []xdr.Claimant{}
for _, d := range cb.Destinations {
c := xdr.Claimant{
Type: xdr.ClaimantTypeClaimantTypeV0,
V0: &xdr.ClaimantV0{
Predicate: d.Predicate,
},
}
err = c.V0.Destination.SetAddress(d.Destination)
if err != nil {
return xdr.Operation{}, errors.Wrapf(err, "failed to set destination address: %s", d.Destination)
}
claimants = append(claimants, c)
}
xdrOp := xdr.CreateClaimableBalanceOp{
Asset: xdrAsset,
Amount: xdrAmount,
Claimants: claimants,
}
opType := xdr.OperationTypeCreateClaimableBalance
body, err := xdr.NewOperationBody(opType, xdrOp)
if err != nil {
return xdr.Operation{}, errors.Wrap(err, "failed to build XDR OperationBody")
}
op := xdr.Operation{Body: body}
if withMuxedAccounts {
SetOpSourceMuxedAccount(&op, cb.SourceAccount)
} else {
SetOpSourceAccount(&op, cb.SourceAccount)
}
return op, nil
}
// FromXDR for CreateClaimableBalance initializes the txnbuild struct from the corresponding xdr Operation.
func (cb *CreateClaimableBalance) FromXDR(xdrOp xdr.Operation, withMuxedAccounts bool) error {
result, ok := xdrOp.Body.GetCreateClaimableBalanceOp()
if !ok {
return errors.New("error parsing create_claimable_balance operation from xdr")
}
cb.SourceAccount = accountFromXDR(xdrOp.SourceAccount, withMuxedAccounts)
for _, c := range result.Claimants {
claimant := c.MustV0()
cb.Destinations = append(cb.Destinations, Claimant{
Destination: claimant.Destination.Address(),
Predicate: claimant.Predicate,
})
}
asset, err := assetFromXDR(result.Asset)
if err != nil {
return errors.Wrap(err, "error parsing asset in create_claimable_balance operation")
}
cb.Asset = asset
cb.Amount = amount.String(result.Amount)
return nil
}
// Validate for CreateClaimableBalance validates the required struct fields. It returns an error if any of the fields are
// invalid. Otherwise, it returns nil.
func (cb *CreateClaimableBalance) Validate(withMuxedAccounts bool) error {
for _, d := range cb.Destinations {
err := validateStellarPublicKey(d.Destination)
if err != nil {
return NewValidationError("Destinations", err.Error())
}
}
err := validateAmount(cb.Amount)
if err != nil {
return NewValidationError("Amount", err.Error())
}
err = validateStellarAsset(cb.Asset)
if err != nil {
return NewValidationError("Asset", err.Error())
}
return nil
}
// GetSourceAccount returns the source account of the operation, or the empty string if not
// set.
func (cb *CreateClaimableBalance) GetSourceAccount() string {
return cb.SourceAccount
}