| @@ -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 | |||||
| } | |||||