@@ -8,6 +8,7 @@ import ( | |||||
"github.com/gorilla/websocket" | "github.com/gorilla/websocket" | ||||
"github.com/imosed/signet/client" | "github.com/imosed/signet/client" | ||||
. "github.com/imosed/signet/data" | . "github.com/imosed/signet/data" | ||||
"github.com/imosed/signet/utils" | |||||
"github.com/rs/zerolog/log" | "github.com/rs/zerolog/log" | ||||
"github.com/spf13/viper" | "github.com/spf13/viper" | ||||
"github.com/stellar/go/clients/horizonclient" | "github.com/stellar/go/clients/horizonclient" | ||||
@@ -73,12 +74,19 @@ func InitializeContributionStreams() { | |||||
return | return | ||||
} | } | ||||
if tx.Memo == "" { | |||||
return | |||||
} | |||||
Db.Table("reward_funds").Where("memo = ? and fund_wallet = ?", tx.Memo, payment.To).First(&fund) | Db.Table("reward_funds").Where("memo = ? and fund_wallet = ?", tx.Memo, payment.To).First(&fund) | ||||
newAmt := fund.AmountAvailable - amt | newAmt := fund.AmountAvailable - amt | ||||
Db.Model(&RewardFund{}).Where("id = ?", fund.ID).Update("amount_available", newAmt) | Db.Model(&RewardFund{}).Where("id = ?", fund.ID).Update("amount_available", newAmt) | ||||
if tx.Memo == "" { | |||||
return | |||||
} | |||||
if newAmt < 5000 && newAmt > 0 { | |||||
_, err = utils.SubmitGroupFund(fund.ID) | |||||
if err != nil { | |||||
log.Error().Err(err).Msg("Could not submit group fund from contribution stream") | |||||
} | |||||
} | |||||
contribution := Contribution{ | contribution := Contribution{ | ||||
ModelBase: ModelBase{CreatedAt: tx.LedgerCloseTime}, | ModelBase: ModelBase{CreatedAt: tx.LedgerCloseTime}, | ||||
@@ -15,6 +15,7 @@ import ( | |||||
type CreateRewardFundRequest struct { | type CreateRewardFundRequest struct { | ||||
Asset string `json:"asset"` | Asset string `json:"asset"` | ||||
FundWallet string `json:"fundWallet"` | FundWallet string `json:"fundWallet"` | ||||
FundSecret string `json:"fundSecret"` | |||||
SellingWallet string `json:"sellingWallet"` | SellingWallet string `json:"sellingWallet"` | ||||
IssuerWallet string `json:"issuerWallet"` | IssuerWallet string `json:"issuerWallet"` | ||||
Memo string `json:"memo"` | Memo string `json:"memo"` | ||||
@@ -2,20 +2,10 @@ package endpoints | |||||
import ( | import ( | ||||
"encoding/json" | "encoding/json" | ||||
"fmt" | |||||
"net/http" | "net/http" | ||||
"github.com/imosed/signet/client" | |||||
. "github.com/imosed/signet/data" | |||||
"github.com/imosed/signet/utils" | "github.com/imosed/signet/utils" | ||||
"github.com/rs/zerolog/log" | "github.com/rs/zerolog/log" | ||||
"github.com/stellar/go/clients/horizonclient" | |||||
"github.com/stellar/go/keypair" | |||||
"github.com/stellar/go/network" | |||||
"github.com/stellar/go/protocols/horizon" | |||||
"github.com/stellar/go/txnbuild" | |||||
"github.com/stellar/go/xdr" | |||||
"gorm.io/gorm/clause" | |||||
) | ) | ||||
type SubmitFundRequest struct { | type SubmitFundRequest struct { | ||||
@@ -30,109 +20,21 @@ func SubmitFund(w http.ResponseWriter, r *http.Request) { | |||||
log.Error().Err(err).Msg("Could not decode body in SubmitFund call") | log.Error().Err(err).Msg("Could not decode body in SubmitFund call") | ||||
} | } | ||||
var fund RewardFund | |||||
Db.Preload(clause.Associations).Find(&fund, req.FundID) | |||||
var resp SuccessResponse | var resp SuccessResponse | ||||
if !req.Submit { | |||||
json.NewEncoder(w).Encode(&SuccessResponse{Success: false}) | |||||
return | |||||
} | |||||
source := keypair.MustParseFull(fund.FundSecret) | |||||
sourceReq := horizonclient.AccountRequest{AccountID: source.Address()} | |||||
var sourceAcct horizon.Account | |||||
sourceAcct, err = client.SignetClient.AccountDetail(sourceReq) | |||||
offerReq := horizonclient.OfferRequest{ | |||||
Seller: fund.SellingWallet, | |||||
Selling: fmt.Sprintf("%s:%s", fund.Asset, fund.IssuerWallet), | |||||
Order: horizonclient.OrderDesc, | |||||
} | |||||
resp.Success = false | |||||
if err, ok := utils.FindOffer(offerReq, &fund); !ok { | |||||
err = json.NewEncoder(w).Encode(&SuccessResponse{Success: ok}) | |||||
if req.Submit { | |||||
var success bool | |||||
success, err = utils.SubmitGroupFund(req.FundID) | |||||
if err != nil { | if err != nil { | ||||
log.Error().Err(err).Msg("Could not deliver response after failing to find issuer offer in submission") | |||||
log.Error().Err(err).Msg("Could not submit group fund from SubmitFundRequest") | |||||
} | } | ||||
return | |||||
} | |||||
var currentContributions = fund.Contributions | |||||
var submissionAmount = SumContributions(currentContributions) | |||||
tr := Db.Begin() | |||||
tr.Table("contributions"). | |||||
Where("reward_fund_id = ? and submitted is null or submitted = false", req.FundID). | |||||
Updates(Contribution{Submitted: true}) | |||||
var tx *txnbuild.Transaction | |||||
tx, err = txnbuild.NewTransaction( | |||||
txnbuild.TransactionParams{ | |||||
SourceAccount: &sourceAcct, | |||||
IncrementSequenceNum: true, | |||||
Operations: []txnbuild.Operation{ | |||||
&txnbuild.ChangeTrust{ | |||||
Line: txnbuild.CreditAsset{ | |||||
Code: fund.Asset, | |||||
Issuer: fund.IssuerWallet, | |||||
}.MustToChangeTrustAsset(), | |||||
SourceAccount: fund.FundWallet, | |||||
}, | |||||
&txnbuild.ManageBuyOffer{ | |||||
Selling: txnbuild.NativeAsset{}, | |||||
Buying: txnbuild.CreditAsset{ | |||||
Code: fund.Asset, | |||||
Issuer: fund.IssuerWallet, | |||||
}, | |||||
Amount: fmt.Sprintf("%f", submissionAmount), | |||||
Price: xdr.Price{N: 1, D: xdr.Int32(fund.Price)}, | |||||
OfferID: 0, | |||||
SourceAccount: fund.FundWallet, | |||||
}, | |||||
}, | |||||
BaseFee: txnbuild.MinBaseFee, | |||||
Memo: txnbuild.Memo(nil), | |||||
Preconditions: txnbuild.Preconditions{ | |||||
TimeBounds: txnbuild.NewInfiniteTimeout(), // TODO: change from infinite | |||||
}, | |||||
}) | |||||
if err != nil { | |||||
log.Error().Err(err).Msg("Could not build submission transaction") | |||||
tr.Rollback() | |||||
return | |||||
} | |||||
tx, err = tx.Sign(network.TestNetworkPassphrase, source) | |||||
if err != nil { | |||||
log.Error().Err(err).Msg("Could not sign submission transaction") | |||||
tr.Rollback() | |||||
return | |||||
} | |||||
var response horizon.Transaction | |||||
response, err = client.SignetClient.SubmitTransaction(tx) | |||||
if err != nil { | |||||
log.Error().Err(err).Msg("Could not submit transaction") | |||||
tr.Rollback() | |||||
return | |||||
resp.Success = success | |||||
} | } | ||||
tr.Commit() | |||||
resp.Success = response.Successful | |||||
err = json.NewEncoder(w).Encode(resp) | err = json.NewEncoder(w).Encode(resp) | ||||
if err != nil { | if err != nil { | ||||
log.Error().Err(err).Msg("Could not deliver response in SubmitFund call") | log.Error().Err(err).Msg("Could not deliver response in SubmitFund call") | ||||
} | } | ||||
} | } | ||||
func SumContributions(contributions []Contribution) float64 { | |||||
var total float64 = 0 | |||||
for _, contribution := range contributions { | |||||
if !contribution.Submitted { | |||||
total += contribution.Amount | |||||
} | |||||
} | |||||
return total | |||||
} |
@@ -0,0 +1,106 @@ | |||||
package utils | |||||
import ( | |||||
"fmt" | |||||
"github.com/imosed/signet/client" | |||||
. "github.com/imosed/signet/data" | |||||
"github.com/stellar/go/clients/horizonclient" | |||||
"github.com/stellar/go/keypair" | |||||
"github.com/stellar/go/network" | |||||
"github.com/stellar/go/protocols/horizon" | |||||
"github.com/stellar/go/txnbuild" | |||||
"github.com/stellar/go/xdr" | |||||
"gorm.io/gorm/clause" | |||||
) | |||||
func SubmitGroupFund(fundID uint) (bool, error) { | |||||
var fund RewardFund | |||||
Db.Preload(clause.Associations).Find(&fund, fundID) | |||||
var err error | |||||
source := keypair.MustParseFull(fund.FundSecret) | |||||
sourceReq := horizonclient.AccountRequest{AccountID: source.Address()} | |||||
var sourceAcct horizon.Account | |||||
sourceAcct, err = client.SignetClient.AccountDetail(sourceReq) | |||||
offerReq := horizonclient.OfferRequest{ | |||||
Seller: fund.SellingWallet, | |||||
Selling: fmt.Sprintf("%s:%s", fund.Asset, fund.IssuerWallet), | |||||
Order: horizonclient.OrderDesc, | |||||
} | |||||
if err, ok := FindOffer(offerReq, &fund); !ok { | |||||
return false, err | |||||
} | |||||
var currentContributions = fund.Contributions | |||||
var submissionAmount = sumContributions(currentContributions) | |||||
tr := Db.Begin() | |||||
tr.Table("contributions"). | |||||
Where("reward_fund_id = ? and submitted is null or submitted = false", fundID). | |||||
Updates(Contribution{Submitted: true}) | |||||
var tx *txnbuild.Transaction | |||||
tx, err = txnbuild.NewTransaction( | |||||
txnbuild.TransactionParams{ | |||||
SourceAccount: &sourceAcct, | |||||
IncrementSequenceNum: true, | |||||
Operations: []txnbuild.Operation{ | |||||
&txnbuild.ChangeTrust{ | |||||
Line: txnbuild.CreditAsset{ | |||||
Code: fund.Asset, | |||||
Issuer: fund.IssuerWallet, | |||||
}.MustToChangeTrustAsset(), | |||||
SourceAccount: fund.FundWallet, | |||||
}, | |||||
&txnbuild.ManageBuyOffer{ | |||||
Selling: txnbuild.NativeAsset{}, | |||||
Buying: txnbuild.CreditAsset{ | |||||
Code: fund.Asset, | |||||
Issuer: fund.IssuerWallet, | |||||
}, | |||||
Amount: fmt.Sprintf("%f", submissionAmount), | |||||
Price: xdr.Price{N: 1, D: xdr.Int32(fund.Price)}, | |||||
OfferID: 0, | |||||
SourceAccount: fund.FundWallet, | |||||
}, | |||||
}, | |||||
BaseFee: txnbuild.MinBaseFee, | |||||
Memo: txnbuild.Memo(nil), | |||||
Preconditions: txnbuild.Preconditions{ | |||||
TimeBounds: txnbuild.NewInfiniteTimeout(), // TODO: change from infinite | |||||
}, | |||||
}) | |||||
if err != nil { | |||||
tr.Rollback() | |||||
return false, err | |||||
} | |||||
tx, err = tx.Sign(network.TestNetworkPassphrase, source) | |||||
if err != nil { | |||||
tr.Rollback() | |||||
return false, err | |||||
} | |||||
_, err = client.SignetClient.SubmitTransaction(tx) | |||||
if err != nil { | |||||
tr.Rollback() | |||||
return false, err | |||||
} | |||||
tr.Commit() | |||||
return true, nil | |||||
} | |||||
func sumContributions(contributions []Contribution) float64 { | |||||
var total float64 = 0 | |||||
for _, contribution := range contributions { | |||||
if !contribution.Submitted { | |||||
total += contribution.Amount | |||||
} | |||||
} | |||||
return total | |||||
} |