github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/stellar/trustlines.go (about) 1 package stellar 2 3 import ( 4 "fmt" 5 6 "github.com/keybase/client/go/libkb" 7 "github.com/keybase/client/go/protocol/stellar1" 8 "github.com/keybase/stellarnet" 9 ) 10 11 const trustlineMaxLimit = "922337203685.4775807" 12 13 func AddTrustlineLocal(mctx libkb.MetaContext, arg stellar1.AddTrustlineLocalArg) (err error) { 14 defer mctx.Trace( 15 fmt.Sprintf("Stellar.AddTrustlineLocal(%s,%s)", arg.AccountID, arg.Trustline.AssetCode), 16 &err)() 17 18 var limitAmount string 19 if arg.Limit != "" { 20 // Parse to ensure the format and the number is correct. 21 intLimit, err := stellarnet.ParseStellarAmount(arg.Limit) 22 if err != nil { 23 return err 24 } 25 26 if intLimit <= 0 { 27 return fmt.Errorf("trustline limit has to be higher than 0 in AddTrustlineLocal, got %s", arg.Limit) 28 } 29 30 limitAmount = arg.Limit 31 } else { 32 limitAmount = trustlineMaxLimit 33 } 34 35 walletState := getGlobal(mctx.G()).walletState 36 37 senderEntry, senderAccountBundle, err := LookupSender(mctx, arg.AccountID) 38 if err != nil { 39 return err 40 } 41 42 currentBalances, err := walletState.Balances(mctx.Ctx(), senderEntry.AccountID) 43 if err != nil { 44 return err 45 } 46 47 for _, bal := range currentBalances { 48 if bal.Asset.Issuer == arg.Trustline.Issuer.String() && bal.Asset.Code == arg.Trustline.AssetCode.String() { 49 return fmt.Errorf("Account %s already has trustline %s %s", arg.AccountID.String(), 50 arg.Trustline.AssetCode, arg.Trustline.Issuer) 51 } 52 } 53 54 senderSeed := senderAccountBundle.Signers[0] 55 senderSeed2, err := stellarnet.NewSeedStr(senderSeed.SecureNoLogString()) 56 if err != nil { 57 return err 58 } 59 60 sp, unlock := NewSeqnoProvider(mctx, walletState) 61 defer unlock() 62 63 tb, err := getTimeboundsForSending(mctx, walletState) 64 if err != nil { 65 return err 66 } 67 68 assetIssuerAddr, err := stellarnet.NewAddressStr(arg.Trustline.Issuer.String()) 69 if err != nil { 70 return fmt.Errorf("Malformed asset issuer ID: %s", err) 71 } 72 73 baseFee := walletState.BaseFee(mctx) 74 sig, err := stellarnet.CreateTrustlineTransaction(senderSeed2, arg.Trustline.AssetCode.String(), 75 assetIssuerAddr, limitAmount, sp, tb, baseFee) 76 if err != nil { 77 return err 78 } 79 err = walletState.ChangeTrustline(mctx.Ctx(), sig.Signed) 80 if err != nil { 81 return err 82 } 83 err = walletState.Refresh(mctx, senderEntry.AccountID, "add trustline") 84 if err != nil { 85 mctx.Debug("AddTrustlineLocal ws.Refresh error: %s", err) 86 } 87 return nil 88 } 89 90 func DeleteTrustlineLocal(mctx libkb.MetaContext, arg stellar1.DeleteTrustlineLocalArg) (err error) { 91 defer mctx.Trace( 92 fmt.Sprintf("Stellar.DeleteTrustlineLocal(%s,%s)", arg.AccountID, arg.Trustline.AssetCode), 93 &err)() 94 95 walletState := getGlobal(mctx.G()).walletState 96 97 senderEntry, senderAccountBundle, err := LookupSender(mctx, arg.AccountID) 98 if err != nil { 99 return err 100 } 101 102 currentBalances, err := walletState.Balances(mctx.Ctx(), senderEntry.AccountID) 103 if err != nil { 104 return err 105 } 106 107 var found bool 108 for _, bal := range currentBalances { 109 if bal.Asset.Issuer == arg.Trustline.Issuer.String() && bal.Asset.Code == arg.Trustline.AssetCode.String() { 110 currentAmount, err := stellarnet.ParseStellarAmount(bal.Amount) 111 if err != nil { 112 return err 113 } 114 if currentAmount != 0 { 115 return fmt.Errorf("Cannot delete a trustline with a balance.") 116 } 117 found = true 118 break 119 } 120 } 121 122 if !found { 123 return fmt.Errorf("Account %s does not have trustline %s %s", arg.AccountID.String(), 124 arg.Trustline.AssetCode, arg.Trustline.Issuer) 125 } 126 127 senderSeed := senderAccountBundle.Signers[0] 128 senderSeed2, err := stellarnet.NewSeedStr(senderSeed.SecureNoLogString()) 129 if err != nil { 130 return err 131 } 132 133 sp, unlock := NewSeqnoProvider(mctx, walletState) 134 defer unlock() 135 136 tb, err := getTimeboundsForSending(mctx, walletState) 137 if err != nil { 138 return err 139 } 140 141 assetIssuerAddr, err := stellarnet.NewAddressStr(arg.Trustline.Issuer.String()) 142 if err != nil { 143 return fmt.Errorf("Malformed asset issuer ID: %s", err) 144 } 145 146 baseFee := walletState.BaseFee(mctx) 147 sig, err := stellarnet.DeleteTrustlineTransaction(senderSeed2, arg.Trustline.AssetCode.String(), 148 assetIssuerAddr, sp, tb, baseFee) 149 if err != nil { 150 return err 151 } 152 err = walletState.ChangeTrustline(mctx.Ctx(), sig.Signed) 153 if err != nil { 154 return err 155 } 156 err = walletState.Refresh(mctx, senderEntry.AccountID, "delete trustline") 157 if err != nil { 158 mctx.Debug("DeleteTrustlineLocal ws.Refresh error: %s", err) 159 } 160 return nil 161 } 162 163 func ChangeTrustlineLimitLocal(mctx libkb.MetaContext, arg stellar1.ChangeTrustlineLimitLocalArg) (err error) { 164 defer mctx.Trace( 165 fmt.Sprintf("Stellar.ChangeTrustlineLimitLocal(%s,%s,%s)", arg.AccountID, arg.Trustline.AssetCode, arg.Limit), 166 &err)() 167 168 walletState := getGlobal(mctx.G()).walletState 169 170 intLimit, err := stellarnet.ParseStellarAmount(arg.Limit) 171 if err != nil { 172 return fmt.Errorf("while parsing `limit` number: %s", err.Error()) 173 } 174 175 if intLimit <= 0 { 176 return fmt.Errorf("trustline limit has to be higher than 0, got %s", arg.Limit) 177 } 178 179 senderEntry, senderAccountBundle, err := LookupSender(mctx, arg.AccountID) 180 if err != nil { 181 return err 182 } 183 184 currentBalances, err := walletState.Balances(mctx.Ctx(), senderEntry.AccountID) 185 if err != nil { 186 return err 187 } 188 189 var found bool 190 for _, bal := range currentBalances { 191 if bal.Asset.Issuer == arg.Trustline.Issuer.String() && bal.Asset.Code == arg.Trustline.AssetCode.String() { 192 currentAmount, err := stellarnet.ParseStellarAmount(bal.Amount) 193 if err != nil { 194 return err 195 } 196 if intLimit < currentAmount { 197 return fmt.Errorf("limit cannot be set to less what the current balance is: %s", bal.Amount) 198 } 199 found = true 200 break 201 } 202 } 203 204 if !found { 205 return fmt.Errorf("Account %s does not have trustline %s %s", arg.AccountID.String(), 206 arg.Trustline.AssetCode, arg.Trustline.Issuer) 207 } 208 209 senderSeed := senderAccountBundle.Signers[0] 210 senderSeed2, err := stellarnet.NewSeedStr(senderSeed.SecureNoLogString()) 211 if err != nil { 212 return err 213 } 214 215 sp, unlock := NewSeqnoProvider(mctx, walletState) 216 defer unlock() 217 218 tb, err := getTimeboundsForSending(mctx, walletState) 219 if err != nil { 220 return err 221 } 222 223 assetIssuerAddr, err := stellarnet.NewAddressStr(arg.Trustline.Issuer.String()) 224 if err != nil { 225 return fmt.Errorf("Malformed asset issuer ID: %s", err) 226 } 227 228 baseFee := walletState.BaseFee(mctx) 229 sig, err := stellarnet.CreateTrustlineTransaction(senderSeed2, arg.Trustline.AssetCode.String(), 230 assetIssuerAddr, arg.Limit, sp, tb, baseFee) 231 if err != nil { 232 return err 233 } 234 err = walletState.ChangeTrustline(mctx.Ctx(), sig.Signed) 235 if err != nil { 236 return err 237 } 238 err = walletState.Refresh(mctx, senderEntry.AccountID, "delete trustline") 239 if err != nil { 240 mctx.Debug("DeleteTrustlineLocal ws.Refresh error: %s", err) 241 } 242 return nil 243 }