mirror of
https://github.com/0glabs/0g-chain.git
synced 2025-01-13 08:45:18 +00:00
add payout to depositors feature
This commit is contained in:
parent
8a4109ff26
commit
77bfe11f89
@ -97,8 +97,9 @@ func (tApp TestApp) InitializeFromGenesisStates(genesisStates ...GenesisState) T
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tApp TestApp) CheckBalance(t *testing.T, ctx sdk.Context, owner sdk.AccAddress, expectedCoins sdk.Coins) {
|
func (tApp TestApp) CheckBalance(t *testing.T, ctx sdk.Context, owner sdk.AccAddress, expectedCoins sdk.Coins) {
|
||||||
actualCoins := tApp.GetAccountKeeper().GetAccount(ctx, owner).GetCoins()
|
acc := tApp.GetAccountKeeper().GetAccount(ctx, owner)
|
||||||
require.Equal(t, expectedCoins, actualCoins)
|
require.NotNilf(t, acc, "account with address '%s' doesn't exist", owner)
|
||||||
|
require.Equal(t, expectedCoins, acc.GetCoins())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new auth genesis state from some addresses and coins. The state is returned marshalled into a map.
|
// Create a new auth genesis state from some addresses and coins. The state is returned marshalled into a map.
|
||||||
|
@ -19,7 +19,8 @@ func TestKeeper_EndBlocker(t *testing.T) {
|
|||||||
// Setup
|
// Setup
|
||||||
_, addrs := app.GeneratePrivKeyAddressPairs(2)
|
_, addrs := app.GeneratePrivKeyAddressPairs(2)
|
||||||
buyer := addrs[0]
|
buyer := addrs[0]
|
||||||
recipient := addrs[1]
|
returnAddrs := addrs[1:]
|
||||||
|
returnWeights := []sdk.Int{sdk.NewInt(1)}
|
||||||
sellerModName := liquidator.ModuleName
|
sellerModName := liquidator.ModuleName
|
||||||
//sellerAddr := supply.NewModuleAddress(sellerModName)
|
//sellerAddr := supply.NewModuleAddress(sellerModName)
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ func TestKeeper_EndBlocker(t *testing.T) {
|
|||||||
ctx := tApp.NewContext(true, abci.Header{})
|
ctx := tApp.NewContext(true, abci.Header{})
|
||||||
keeper := tApp.GetAuctionKeeper()
|
keeper := tApp.GetAuctionKeeper()
|
||||||
|
|
||||||
auctionID, err := keeper.StartForwardReverseAuction(ctx, sellerModName, c("token1", 20), c("token2", 50), recipient)
|
auctionID, err := keeper.StartForwardReverseAuction(ctx, sellerModName, c("token1", 20), c("token2", 50), returnAddrs, returnWeights)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, keeper.PlaceBid(ctx, auctionID, buyer, c("token2", 30), c("token1", 20)))
|
require.NoError(t, keeper.PlaceBid(ctx, auctionID, buyer, c("token2", 30), c("token1", 20)))
|
||||||
|
|
||||||
|
@ -46,12 +46,16 @@ func (k Keeper) StartReverseAuction(ctx sdk.Context, buyer string, bid sdk.Coin,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StartForwardReverseAuction starts an auction where bidders bid up to a maxBid, then switch to bidding down on price.
|
// StartForwardReverseAuction starts an auction where bidders bid up to a maxBid, then switch to bidding down on price.
|
||||||
func (k Keeper) StartForwardReverseAuction(ctx sdk.Context, seller string, lot sdk.Coin, maxBid sdk.Coin, otherPerson sdk.AccAddress) (types.ID, sdk.Error) {
|
func (k Keeper) StartForwardReverseAuction(ctx sdk.Context, seller string, lot sdk.Coin, maxBid sdk.Coin, lotReturnAddrs []sdk.AccAddress, lotReturnWeights []sdk.Int) (types.ID, sdk.Error) {
|
||||||
// create auction
|
// create auction
|
||||||
auction := types.NewForwardReverseAuction(seller, lot, ctx.BlockTime().Add(types.DefaultMaxAuctionDuration), maxBid, otherPerson)
|
weightedAddresses, err := types.NewWeightedAddresses(lotReturnAddrs, lotReturnWeights)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
auction := types.NewForwardReverseAuction(seller, lot, ctx.BlockTime().Add(types.DefaultMaxAuctionDuration), maxBid, weightedAddresses)
|
||||||
|
|
||||||
// take coins from module account
|
// take coins from module account
|
||||||
err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, seller, types.ModuleName, sdk.NewCoins(lot))
|
err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, seller, types.ModuleName, sdk.NewCoins(lot))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -206,10 +210,17 @@ func (k Keeper) PlaceBidForwardReverse(ctx sdk.Context, a types.ForwardReverseAu
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return a, err
|
return a, err
|
||||||
}
|
}
|
||||||
err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, a.OtherPerson, sdk.NewCoins(lotDecrement))
|
// FIXME paying out rateably to cdp depositors is vulnerable to errors compounding over multiple bids
|
||||||
|
lotPayouts, err := splitCoinIntoWeightedBuckets(lotDecrement, a.LotReturns.Weights)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return a, err
|
return a, err
|
||||||
}
|
}
|
||||||
|
for i, payout := range lotPayouts {
|
||||||
|
err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, a.LotReturns.Addresses[i], sdk.NewCoins(payout))
|
||||||
|
if err != nil {
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update Auction
|
// Update Auction
|
||||||
a.Bidder = bidder
|
a.Bidder = bidder
|
||||||
@ -318,3 +329,17 @@ func earliestTime(t1, t2 time.Time) time.Time {
|
|||||||
return t2 // also returned if times are equal
|
return t2 // also returned if times are equal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func splitCoinIntoWeightedBuckets(coin sdk.Coin, buckets []sdk.Int) ([]sdk.Coin, sdk.Error) {
|
||||||
|
for _, bucket := range buckets {
|
||||||
|
if bucket.IsNegative() {
|
||||||
|
return nil, sdk.ErrInternal("cannot split coin into bucket with negative weight")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
amounts := splitIntIntoWeightedBuckets(coin.Amount, buckets)
|
||||||
|
result := make([]sdk.Coin, len(amounts))
|
||||||
|
for i, a := range amounts {
|
||||||
|
result[i] = sdk.NewCoin(coin.Denom, a)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ package keeper_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||||
"github.com/cosmos/cosmos-sdk/x/supply"
|
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||||
@ -94,9 +95,10 @@ func TestReverseAuctionBasic(t *testing.T) {
|
|||||||
|
|
||||||
func TestForwardReverseAuctionBasic(t *testing.T) {
|
func TestForwardReverseAuctionBasic(t *testing.T) {
|
||||||
// Setup
|
// Setup
|
||||||
_, addrs := app.GeneratePrivKeyAddressPairs(2)
|
_, addrs := app.GeneratePrivKeyAddressPairs(4)
|
||||||
buyer := addrs[0]
|
buyer := addrs[0]
|
||||||
recipient := addrs[1]
|
returnAddrs := addrs[1:]
|
||||||
|
returnWeights := []sdk.Int{i(30), i(20), i(10)}
|
||||||
sellerModName := liquidator.ModuleName
|
sellerModName := liquidator.ModuleName
|
||||||
sellerAddr := supply.NewModuleAddress(sellerModName)
|
sellerAddr := supply.NewModuleAddress(sellerModName)
|
||||||
|
|
||||||
@ -106,7 +108,9 @@ func TestForwardReverseAuctionBasic(t *testing.T) {
|
|||||||
tApp.InitializeFromGenesisStates(
|
tApp.InitializeFromGenesisStates(
|
||||||
NewAuthGenStateFromAccs(authexported.GenesisAccounts{
|
NewAuthGenStateFromAccs(authexported.GenesisAccounts{
|
||||||
auth.NewBaseAccount(buyer, cs(c("token1", 100), c("token2", 100)), nil, 0, 0),
|
auth.NewBaseAccount(buyer, cs(c("token1", 100), c("token2", 100)), nil, 0, 0),
|
||||||
auth.NewBaseAccount(recipient, cs(c("token1", 100), c("token2", 100)), nil, 0, 0),
|
auth.NewBaseAccount(returnAddrs[0], cs(c("token1", 100), c("token2", 100)), nil, 0, 0),
|
||||||
|
auth.NewBaseAccount(returnAddrs[1], cs(c("token1", 100), c("token2", 100)), nil, 0, 0),
|
||||||
|
auth.NewBaseAccount(returnAddrs[2], cs(c("token1", 100), c("token2", 100)), nil, 0, 0),
|
||||||
sellerAcc,
|
sellerAcc,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -114,7 +118,7 @@ func TestForwardReverseAuctionBasic(t *testing.T) {
|
|||||||
keeper := tApp.GetAuctionKeeper()
|
keeper := tApp.GetAuctionKeeper()
|
||||||
|
|
||||||
// Start auction
|
// Start auction
|
||||||
auctionID, err := keeper.StartForwardReverseAuction(ctx, sellerModName, c("token1", 20), c("token2", 50), recipient) // seller, lot, maxBid, otherPerson
|
auctionID, err := keeper.StartForwardReverseAuction(ctx, sellerModName, c("token1", 20), c("token2", 50), returnAddrs, returnWeights) // seller, lot, maxBid, otherPerson
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// Check seller's coins have decreased
|
// Check seller's coins have decreased
|
||||||
tApp.CheckBalance(t, ctx, sellerAddr, cs(c("token1", 80), c("token2", 100)))
|
tApp.CheckBalance(t, ctx, sellerAddr, cs(c("token1", 80), c("token2", 100)))
|
||||||
@ -125,8 +129,10 @@ func TestForwardReverseAuctionBasic(t *testing.T) {
|
|||||||
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 100), c("token2", 90)))
|
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 100), c("token2", 90)))
|
||||||
// Check seller's coins have increased
|
// Check seller's coins have increased
|
||||||
tApp.CheckBalance(t, ctx, sellerAddr, cs(c("token1", 80), c("token2", 110)))
|
tApp.CheckBalance(t, ctx, sellerAddr, cs(c("token1", 80), c("token2", 110)))
|
||||||
// Check recipient has not received coins
|
// Check return addresses have not received coins
|
||||||
tApp.CheckBalance(t, ctx, recipient, cs(c("token1", 100), c("token2", 100)))
|
for _, ra := range returnAddrs {
|
||||||
|
tApp.CheckBalance(t, ctx, ra, cs(c("token1", 100), c("token2", 100)))
|
||||||
|
}
|
||||||
|
|
||||||
// Place a reverse bid
|
// Place a reverse bid
|
||||||
require.NoError(t, keeper.PlaceBid(ctx, 0, buyer, c("token2", 50), c("token1", 15))) // bid, lot
|
require.NoError(t, keeper.PlaceBid(ctx, 0, buyer, c("token2", 50), c("token1", 15))) // bid, lot
|
||||||
@ -134,8 +140,10 @@ func TestForwardReverseAuctionBasic(t *testing.T) {
|
|||||||
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 100), c("token2", 50)))
|
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 100), c("token2", 50)))
|
||||||
// Check seller's coins have increased
|
// Check seller's coins have increased
|
||||||
tApp.CheckBalance(t, ctx, sellerAddr, cs(c("token1", 80), c("token2", 150)))
|
tApp.CheckBalance(t, ctx, sellerAddr, cs(c("token1", 80), c("token2", 150)))
|
||||||
// Check "recipient" has received coins
|
// Check return addresses have received coins
|
||||||
tApp.CheckBalance(t, ctx, recipient, cs(c("token1", 105), c("token2", 100)))
|
tApp.CheckBalance(t, ctx, returnAddrs[0], cs(c("token1", 102), c("token2", 100)))
|
||||||
|
tApp.CheckBalance(t, ctx, returnAddrs[1], cs(c("token1", 102), c("token2", 100)))
|
||||||
|
tApp.CheckBalance(t, ctx, returnAddrs[2], cs(c("token1", 101), c("token2", 100)))
|
||||||
|
|
||||||
// Close auction at just after auction expiry
|
// Close auction at just after auction expiry
|
||||||
ctx = ctx.WithBlockTime(ctx.BlockTime().Add(types.DefaultBidDuration))
|
ctx = ctx.WithBlockTime(ctx.BlockTime().Add(types.DefaultBidDuration))
|
||||||
|
@ -10,6 +10,13 @@ import (
|
|||||||
|
|
||||||
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
|
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
|
||||||
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }
|
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }
|
||||||
|
func i(n int64) sdk.Int { return sdk.NewInt(n) }
|
||||||
|
func is(ns ...int64) (is []sdk.Int) {
|
||||||
|
for _, n := range ns {
|
||||||
|
is = append(is, sdk.NewInt(n))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func NewAuthGenStateFromAccs(accounts authexported.GenesisAccounts) app.GenesisState {
|
func NewAuthGenStateFromAccs(accounts authexported.GenesisAccounts) app.GenesisState {
|
||||||
authGenesis := auth.NewGenesisState(auth.DefaultParams(), accounts)
|
authGenesis := auth.NewGenesisState(auth.DefaultParams(), accounts)
|
||||||
|
69
x/auction/keeper/math.go
Normal file
69
x/auction/keeper/math.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package keeper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// splitIntIntoWeightedBuckets divides an initial +ve integer among several buckets in proportion to the buckets' weights
|
||||||
|
// It uses the largest remainder method:
|
||||||
|
// https://en.wikipedia.org/wiki/Largest_remainder_method
|
||||||
|
// see also: https://stackoverflow.com/questions/13483430/how-to-make-rounded-percentages-add-up-to-100
|
||||||
|
func splitIntIntoWeightedBuckets(amount sdk.Int, buckets []sdk.Int) []sdk.Int {
|
||||||
|
// TODO ideally change algorithm to work with -ve numbers. Limiting to +ve numbers until them
|
||||||
|
if amount.IsNegative() {
|
||||||
|
panic("negative amount")
|
||||||
|
}
|
||||||
|
for _, bucket := range buckets {
|
||||||
|
if bucket.IsNegative() {
|
||||||
|
panic("negative bucket")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
totalWeights := totalInts(buckets...)
|
||||||
|
|
||||||
|
// split amount by weights, recording whole number part and remainder
|
||||||
|
quotients := make([]quoRem, len(buckets))
|
||||||
|
for i := range buckets {
|
||||||
|
q := amount.Mul(buckets[i]).Quo(totalWeights)
|
||||||
|
r := amount.Mul(buckets[i]).Mod(totalWeights)
|
||||||
|
quotients[i] = quoRem{index: i, quo: q, rem: r}
|
||||||
|
}
|
||||||
|
|
||||||
|
// apportion left over to buckets with the highest remainder (to minimize error)
|
||||||
|
sort.Slice(quotients, func(i, j int) bool {
|
||||||
|
return quotients[i].rem.GT(quotients[j].rem) // decreasing remainder order
|
||||||
|
})
|
||||||
|
|
||||||
|
allocated := sdk.ZeroInt()
|
||||||
|
for _, qr := range quotients {
|
||||||
|
allocated = allocated.Add(qr.quo)
|
||||||
|
}
|
||||||
|
leftToAllocate := amount.Sub(allocated)
|
||||||
|
|
||||||
|
results := make([]sdk.Int, len(quotients))
|
||||||
|
for _, qr := range quotients {
|
||||||
|
results[qr.index] = qr.quo
|
||||||
|
if !leftToAllocate.IsZero() {
|
||||||
|
results[qr.index] = results[qr.index].Add(sdk.OneInt())
|
||||||
|
leftToAllocate = leftToAllocate.Sub(sdk.OneInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
type quoRem struct {
|
||||||
|
index int
|
||||||
|
quo sdk.Int
|
||||||
|
rem sdk.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// totalInts adds together sdk.Ints
|
||||||
|
func totalInts(is ...sdk.Int) sdk.Int {
|
||||||
|
total := sdk.ZeroInt()
|
||||||
|
for _, i := range is {
|
||||||
|
total = total.Add(i)
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
37
x/auction/keeper/math_test.go
Normal file
37
x/auction/keeper/math_test.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package keeper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSplitIntIntoWeightedBuckets(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
amount sdk.Int
|
||||||
|
buckets []sdk.Int
|
||||||
|
want []sdk.Int
|
||||||
|
}{
|
||||||
|
{"2split1,1", i(2), is(1, 1), is(1, 1)},
|
||||||
|
{"100split1,9", i(100), is(1, 9), is(10, 90)},
|
||||||
|
{"7split1,2", i(7), is(1, 2), is(2, 5)},
|
||||||
|
{"17split1,1,1", i(17), is(1, 1, 1), is(6, 6, 5)},
|
||||||
|
// TODO more tests
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
got := splitIntIntoWeightedBuckets(tc.amount, tc.buckets)
|
||||||
|
require.Equal(t, tc.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func i(n int64) sdk.Int { return sdk.NewInt(n) }
|
||||||
|
func is(ns ...int64) (is []sdk.Int) {
|
||||||
|
for _, n := range ns {
|
||||||
|
is = append(is, sdk.NewInt(n))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -133,8 +133,8 @@ func NewReverseAuction(buyerModAccName string, bid sdk.Coin, initialLot sdk.Coin
|
|||||||
// ForwardReverseAuction type for forward reverse auction
|
// ForwardReverseAuction type for forward reverse auction
|
||||||
type ForwardReverseAuction struct {
|
type ForwardReverseAuction struct {
|
||||||
BaseAuction
|
BaseAuction
|
||||||
MaxBid sdk.Coin
|
MaxBid sdk.Coin
|
||||||
OtherPerson sdk.AccAddress // TODO rename, this is normally the original CDP owner, will have to be updated to account for deposits
|
LotReturns WeightedAddresses // return addresses to pay out reductions in the lot amount to. Lot is bid down during reverse phase.
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithID returns an auction with the ID set
|
// WithID returns an auction with the ID set
|
||||||
@ -149,15 +149,15 @@ func (a ForwardReverseAuction) String() string {
|
|||||||
End Time: %s
|
End Time: %s
|
||||||
Max End Time: %s
|
Max End Time: %s
|
||||||
Max Bid %s
|
Max Bid %s
|
||||||
Other Person %s`,
|
LotReturns %s`,
|
||||||
a.GetID(), a.Initiator, a.Lot,
|
a.GetID(), a.Initiator, a.Lot,
|
||||||
a.Bidder, a.Bid, a.GetEndTime().String(),
|
a.Bidder, a.Bid, a.GetEndTime().String(),
|
||||||
a.MaxEndTime.String(), a.MaxBid, a.OtherPerson,
|
a.MaxEndTime.String(), a.MaxBid, a.LotReturns,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewForwardReverseAuction creates a new forward reverse auction
|
// NewForwardReverseAuction creates a new forward reverse auction
|
||||||
func NewForwardReverseAuction(seller string, lot sdk.Coin, EndTime time.Time, maxBid sdk.Coin, otherPerson sdk.AccAddress) ForwardReverseAuction {
|
func NewForwardReverseAuction(seller string, lot sdk.Coin, EndTime time.Time, maxBid sdk.Coin, lotReturns WeightedAddresses) ForwardReverseAuction {
|
||||||
auction := ForwardReverseAuction{
|
auction := ForwardReverseAuction{
|
||||||
BaseAuction: BaseAuction{
|
BaseAuction: BaseAuction{
|
||||||
// no ID
|
// no ID
|
||||||
@ -167,8 +167,28 @@ func NewForwardReverseAuction(seller string, lot sdk.Coin, EndTime time.Time, ma
|
|||||||
Bid: sdk.NewInt64Coin(maxBid.Denom, 0),
|
Bid: sdk.NewInt64Coin(maxBid.Denom, 0),
|
||||||
EndTime: EndTime,
|
EndTime: EndTime,
|
||||||
MaxEndTime: EndTime},
|
MaxEndTime: EndTime},
|
||||||
MaxBid: maxBid,
|
MaxBid: maxBid,
|
||||||
OtherPerson: otherPerson,
|
LotReturns: lotReturns,
|
||||||
}
|
}
|
||||||
return auction
|
return auction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WeightedAddresses struct {
|
||||||
|
Addresses []sdk.AccAddress
|
||||||
|
Weights []sdk.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWeightedAddresses(addrs []sdk.AccAddress, weights []sdk.Int) (WeightedAddresses, sdk.Error) {
|
||||||
|
if len(addrs) != len(weights) {
|
||||||
|
return WeightedAddresses{}, sdk.ErrInternal("number of addresses doesn't match number of weights")
|
||||||
|
}
|
||||||
|
for _, w := range weights {
|
||||||
|
if w.IsNegative() {
|
||||||
|
return WeightedAddresses{}, sdk.ErrInternal("weights contain a negative amount")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return WeightedAddresses{
|
||||||
|
Addresses: addrs,
|
||||||
|
Weights: weights,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user