| @@ -8,6 +8,7 @@ import ( | |||
| "github.com/gorilla/websocket" | |||
| "github.com/imosed/signet/client" | |||
| . "github.com/imosed/signet/data" | |||
| "github.com/imosed/signet/utils" | |||
| "github.com/rs/zerolog/log" | |||
| "github.com/spf13/viper" | |||
| "github.com/stellar/go/clients/horizonclient" | |||
| @@ -73,12 +74,19 @@ func InitializeContributionStreams() { | |||
| return | |||
| } | |||
| if tx.Memo == "" { | |||
| return | |||
| } | |||
| Db.Table("reward_funds").Where("memo = ? and fund_wallet = ?", tx.Memo, payment.To).First(&fund) | |||
| newAmt := fund.AmountAvailable - amt | |||
| 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{ | |||
| ModelBase: ModelBase{CreatedAt: tx.LedgerCloseTime}, | |||
| @@ -15,6 +15,7 @@ import ( | |||
| type CreateRewardFundRequest struct { | |||
| Asset string `json:"asset"` | |||
| FundWallet string `json:"fundWallet"` | |||
| FundSecret string `json:"fundSecret"` | |||
| SellingWallet string `json:"sellingWallet"` | |||
| IssuerWallet string `json:"issuerWallet"` | |||
| Memo string `json:"memo"` | |||
| @@ -2,20 +2,10 @@ package endpoints | |||
| import ( | |||
| "encoding/json" | |||
| "fmt" | |||
| "net/http" | |||
| "github.com/imosed/signet/client" | |||
| . "github.com/imosed/signet/data" | |||
| "github.com/imosed/signet/utils" | |||
| "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 { | |||
| @@ -30,109 +20,21 @@ func SubmitFund(w http.ResponseWriter, r *http.Request) { | |||
| 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 | |||
| 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 { | |||
| 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) | |||
| if err != nil { | |||
| 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 | |||
| } | |||