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