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  }