Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions bridges/stellar-solana/api/bridge/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,21 +104,13 @@ func (bridge *Bridge) Close() error {
return nil
}

func (bridge *Bridge) mint(ctx context.Context, receiver solana.Address, depositedAmount *big.Int, txID string) (err error) {
func (bridge *Bridge) mint(ctx context.Context, memoAddress solana.Address, depositedAmount *big.Int, txID string) (err error) {
if !bridge.synced {
return errors.New("bridge is not synced, retry later")
}

valid, err := bridge.solanaWallet.IsValidReceiver(ctx, receiver)
if err != nil {
return errors.Wrap(err, "Failed to check if receiver is proper")
}

if !valid {
return faults.ErrInvalidReceiver
}

log.Info().Str("receiver", receiver.String()).Str("txID", txID).Msg("Minting")
// Check if this tx is a known mint TX
log.Info().Str("receiver", memoAddress.String()).Str("txID", txID).Msg("Minting")
// check if we already know this ID
known, err := bridge.solanaWallet.IsMintTxID(ctx, txID)
if err != nil {
Expand All @@ -130,6 +122,33 @@ func (bridge *Bridge) mint(ctx context.Context, receiver solana.Address, deposit
return
}

// Check if we've already refunded this TX
known, err = bridge.wallet.TransactionStorage.TransactionWithMemoExists(ctx, txID)
if err != nil {
return
}
if known {
log.Info().Str("txID", txID).Msg("Skipping minting transaction we've already refunded")
// we already refunded this, so ignore the transaction
return
}

// Convert receiver address to derived ATA
receiver, err := bridge.solanaWallet.ATAFromMasterAddress(memoAddress)
if err != nil {
return errors.Wrap(err, "could not convert memo master address to derived ATA")
}

log.Debug().Str("ATA", receiver.String()).Msg("Checking if computed receiver ATA is valid")
valid, err := bridge.solanaWallet.IsValidReceiver(ctx, receiver)
if err != nil {
return errors.Wrap(err, "Failed to check if receiver is proper")
}

if !valid {
return faults.ErrInvalidReceiver
}

depositFeeBigInt := big.NewInt(stellar.IntToStroops(bridge.config.DepositFee))

if depositedAmount.Cmp(depositFeeBigInt) <= 0 {
Expand Down
45 changes: 30 additions & 15 deletions bridges/stellar-solana/api/bridge/signer_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,29 @@ func (s *SignerService) SignMint(ctx context.Context, request SolanaRequest, res
return err
}

// Check in transaction storage if the deposit transaction exists
tx, err := s.stellarWallet.TransactionStorage.GetTransactionWithID(ctx, request.TxID)
if err != nil {
log.Info().Str("txid", request.TxID).Msg("transaction not found")
return err
}

known, err := s.solWallet.IsMintTxID(ctx, tx.ID)
if err != nil {
return errors.Wrap(err, "Could not verify if we already know this mint")
}
if known {
return errors.New("Refusing to sign mint request for transaction we already minted")
}

known, err = s.stellarWallet.TransactionStorage.TransactionWithMemoExists(ctx, request.TxID)
if err != nil {
return errors.Wrap(err, "Could not verify if we already refunded this tx")
}
if known {
return errors.New("Refusing to sign mint request for transaction we already refunded")
}

amount, memo, receiver, err := solana.ExtractMintvalues(*solTx)
if memo != request.TxID {
log.Warn().Str("requested txid", request.TxID).Str("embedded txid", memo).Msg("could not unmarshal transaction")
Expand All @@ -102,13 +125,6 @@ func (s *SignerService) SignMint(ctx context.Context, request SolanaRequest, res
return errors.New("Tx receiver does not match requested receiver")
}

// Check in transaction storage if the deposit transaction exists
tx, err := s.stellarWallet.TransactionStorage.GetTransactionWithID(ctx, request.TxID)
if err != nil {
log.Info().Str("txid", request.TxID).Msg("transaction not found")
return err
}

// Validate amount
depositedAmount, _, err := s.stellarWallet.GetDepositAmountAndSender(request.TxID, s.bridgeMasterAddress)
if err != nil {
Expand All @@ -130,22 +146,21 @@ func (s *SignerService) SignMint(ctx context.Context, request SolanaRequest, res
if tx.MemoType != "hash" {
return errors.New("memo is not of type memo hash")
}

// Extract master address from stellar tx
addr, err := solana.AddressFromB64(tx.Memo)
if err != nil {
return err
}

if addr != request.Receiver {
return fmt.Errorf("deposit addresses do not match")
}

known, err := s.solWallet.IsMintTxID(ctx, tx.ID)
// convert to ATA address, which is the one passed in the solana mint tx and the signing request
addr, err = s.solWallet.ATAFromMasterAddress(addr)
if err != nil {
return errors.Wrap(err, "Could not verify if we already know this mint")
return err
}

if known {
return errors.New("Refusing to sign mint request for transaction we already minted")
if addr != request.Receiver {
return fmt.Errorf("deposit addresses do not match")
}

signature, idx, err := s.solWallet.CreateTokenSignature(*solTx)
Expand Down
16 changes: 16 additions & 0 deletions bridges/stellar-solana/solana/solana.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,22 @@ func (sol *Solana) listTransactionSigs(ctx context.Context) ([]Signature, error)
return signatures, nil
}

// ATAFromMasterAddress derives the expected ATA for the current mint assuming the given
// adddress is the master account.
func (sol *Solana) ATAFromMasterAddress(master Address) (Address, error) {
// Rather than calling solana.FindAssociatedTokenAddress(master, sol.tokenAddress), we call the internal function that function call directly.
// The reason for this is that we use Token2022 programs, and the library uses the regular token program, which leads to different ATA derivations.
addr, _, err := solana.FindProgramAddress([][]byte{
master[:],
solana.Token2022ProgramID[:],
sol.tokenAddress[:],
},
solana.SPLAssociatedTokenAccountProgramID,
)

return addr, err
}

// AddressFromHex decodes a hex encoded Solana address
func AddressFromHex(encoded string) (Address, error) {
b, err := hex.DecodeString(encoded)
Expand Down
Loading