github.com/cosmos/cosmos-sdk@v0.50.10/types/coin_test.go (about) 1 package types_test 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 "github.com/stretchr/testify/require" 9 "github.com/stretchr/testify/suite" 10 11 "cosmossdk.io/math" 12 13 "github.com/cosmos/cosmos-sdk/codec" 14 sdk "github.com/cosmos/cosmos-sdk/types" 15 ) 16 17 var ( 18 testDenom1 = "atom" 19 testDenom2 = "muon" 20 ) 21 22 type coinTestSuite struct { 23 suite.Suite 24 ca0, ca1, ca2, ca4, cm0, cm1, cm2, cm4 sdk.Coin 25 emptyCoins sdk.Coins 26 } 27 28 func TestCoinTestSuite(t *testing.T) { 29 suite.Run(t, new(coinTestSuite)) 30 } 31 32 func (s *coinTestSuite) SetupSuite() { 33 zero := math.NewInt(0) 34 one := math.OneInt() 35 two := math.NewInt(2) 36 four := math.NewInt(4) 37 38 s.ca0, s.ca1, s.ca2, s.ca4 = sdk.NewCoin(testDenom1, zero), sdk.NewCoin(testDenom1, one), sdk.NewCoin(testDenom1, two), sdk.NewCoin(testDenom1, four) 39 s.cm0, s.cm1, s.cm2, s.cm4 = sdk.NewCoin(testDenom2, zero), sdk.NewCoin(testDenom2, one), sdk.NewCoin(testDenom2, two), sdk.NewCoin(testDenom2, four) 40 s.emptyCoins = sdk.Coins{} 41 } 42 43 // ---------------------------------------------------------------------------- 44 // Coin tests 45 46 func (s *coinTestSuite) TestCoin() { 47 s.Require().Panics(func() { sdk.NewInt64Coin(testDenom1, -1) }) 48 s.Require().Panics(func() { sdk.NewCoin(testDenom1, math.NewInt(-1)) }) 49 s.Require().Equal(math.NewInt(10), sdk.NewInt64Coin(strings.ToUpper(testDenom1), 10).Amount) 50 s.Require().Equal(math.NewInt(10), sdk.NewCoin(strings.ToUpper(testDenom1), math.NewInt(10)).Amount) 51 s.Require().Equal(math.NewInt(5), sdk.NewInt64Coin(testDenom1, 5).Amount) 52 s.Require().Equal(math.NewInt(5), sdk.NewCoin(testDenom1, math.NewInt(5)).Amount) 53 } 54 55 func (s *coinTestSuite) TestCoin_String() { 56 coin := sdk.NewCoin(testDenom1, math.NewInt(10)) 57 s.Require().Equal(fmt.Sprintf("10%s", testDenom1), coin.String()) 58 } 59 60 func (s *coinTestSuite) TestIsEqualCoin() { 61 cases := []struct { 62 inputOne sdk.Coin 63 inputTwo sdk.Coin 64 expected bool 65 }{ 66 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), true}, 67 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), false}, 68 {sdk.NewInt64Coin("stake", 1), sdk.NewInt64Coin("stake", 10), false}, 69 } 70 71 for tcIndex, tc := range cases { 72 res := tc.inputOne.IsEqual(tc.inputTwo) 73 s.Require().Equal(tc.expected, res, "coin equality relation is incorrect, tc #%d", tcIndex) 74 } 75 } 76 77 func (s *coinTestSuite) TestCoinIsValid() { 78 loremIpsum := `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam viverra dui vel nulla aliquet, non dictum elit aliquam. Proin consequat leo in consectetur mattis. Phasellus eget odio luctus, rutrum dolor at, venenatis ante. Praesent metus erat, sodales vitae sagittis eget, commodo non ipsum. Duis eget urna quis erat mattis pulvinar. Vivamus egestas imperdiet sem, porttitor hendrerit lorem pulvinar in. Vivamus laoreet sapien eget libero euismod tristique. Suspendisse tincidunt nulla quis luctus mattis. 79 Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed id turpis at erat placerat fermentum id sed sapien. Fusce mattis enim id nulla viverra, eget placerat eros aliquet. Nunc fringilla urna ac condimentum ultricies. Praesent in eros ac neque fringilla sodales. Donec ut venenatis eros. Quisque iaculis lectus neque, a varius sem ullamcorper nec. Cras tincidunt dignissim libero nec volutpat. Donec molestie enim sed metus venenatis, quis elementum sem varius. Curabitur eu venenatis nulla. 80 Cras sit amet ligula vel turpis placerat sollicitudin. Nunc massa odio, eleifend id lacus nec, ultricies elementum arcu. Donec imperdiet nulla lacus, a venenatis lacus fermentum nec. Proin vestibulum dolor enim, vitae posuere velit aliquet non. Suspendisse pharetra condimentum nunc tincidunt viverra. Etiam posuere, ligula ut maximus congue, mauris orci consectetur velit, vel finibus eros metus non tellus. Nullam et dictum metus. Aliquam maximus fermentum mauris elementum aliquet. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam dapibus lectus sed tellus rutrum tincidunt. Nulla at dolor sem. Ut non dictum arcu, eget congue sem.` 81 82 loremIpsum = strings.ReplaceAll(loremIpsum, " ", "") 83 loremIpsum = strings.ReplaceAll(loremIpsum, ".", "") 84 loremIpsum = strings.ReplaceAll(loremIpsum, ",", "") 85 86 cases := []struct { 87 coin sdk.Coin 88 expectPass bool 89 }{ 90 {sdk.Coin{testDenom1, math.NewInt(-1)}, false}, 91 {sdk.Coin{testDenom1, math.NewInt(0)}, true}, 92 {sdk.Coin{testDenom1, math.OneInt()}, true}, 93 {sdk.Coin{"Atom", math.OneInt()}, true}, 94 {sdk.Coin{"ATOM", math.OneInt()}, true}, 95 {sdk.Coin{"a", math.OneInt()}, false}, 96 {sdk.Coin{loremIpsum, math.OneInt()}, false}, 97 {sdk.Coin{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", math.OneInt()}, true}, 98 {sdk.Coin{"atOm", math.OneInt()}, true}, 99 {sdk.Coin{"x:y-z.1_2", math.OneInt()}, true}, 100 {sdk.Coin{" ", math.OneInt()}, false}, 101 } 102 103 for i, tc := range cases { 104 s.Require().Equal(tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i) 105 } 106 } 107 108 func (s *coinTestSuite) TestCustomValidation() { 109 newDnmRegex := `[\x{1F600}-\x{1F6FF}]` 110 sdk.SetCoinDenomRegex(func() string { 111 return newDnmRegex 112 }) 113 114 cases := []struct { 115 coin sdk.Coin 116 expectPass bool 117 }{ 118 {sdk.Coin{"🙂", math.NewInt(1)}, true}, 119 {sdk.Coin{"🙁", math.NewInt(1)}, true}, 120 {sdk.Coin{"🌶", math.NewInt(1)}, false}, // outside the unicode range listed above 121 {sdk.Coin{"asdf", math.NewInt(1)}, false}, 122 {sdk.Coin{"", math.NewInt(1)}, false}, 123 } 124 125 for i, tc := range cases { 126 s.Require().Equal(tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i) 127 } 128 sdk.SetCoinDenomRegex(sdk.DefaultCoinDenomRegex) 129 } 130 131 func (s *coinTestSuite) TestCoinsDenoms() { 132 cases := []struct { 133 coins sdk.Coins 134 testOutput []string 135 expectPass bool 136 }{ 137 {sdk.NewCoins(sdk.Coin{"ATOM", math.NewInt(1)}, sdk.Coin{"JUNO", math.NewInt(1)}, sdk.Coin{"OSMO", math.NewInt(1)}, sdk.Coin{"RAT", math.NewInt(1)}), []string{"ATOM", "JUNO", "OSMO", "RAT"}, true}, 138 {sdk.NewCoins(sdk.Coin{"ATOM", math.NewInt(1)}, sdk.Coin{"JUNO", math.NewInt(1)}), []string{"ATOM"}, false}, 139 } 140 141 for i, tc := range cases { 142 expectedOutput := tc.coins.Denoms() 143 count := 0 144 if len(expectedOutput) == len(tc.testOutput) { 145 for k := range tc.testOutput { 146 if tc.testOutput[k] != expectedOutput[k] { 147 count++ 148 break 149 } 150 } 151 } else { 152 count++ 153 } 154 s.Require().Equal(count == 0, tc.expectPass, "unexpected result for coins.Denoms, tc #%d", i) 155 } 156 } 157 158 func (s *coinTestSuite) TestAddCoin() { 159 cases := []struct { 160 inputOne sdk.Coin 161 inputTwo sdk.Coin 162 expected sdk.Coin 163 shouldPanic bool 164 }{ 165 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 2), false}, 166 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom1, 1), false}, 167 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), sdk.NewInt64Coin(testDenom1, 1), true}, 168 } 169 170 for tcIndex, tc := range cases { 171 tc := tc 172 if tc.shouldPanic { 173 s.Require().Panics(func() { tc.inputOne.Add(tc.inputTwo) }) 174 } else { 175 res := tc.inputOne.Add(tc.inputTwo) 176 s.Require().Equal(tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) 177 } 178 } 179 } 180 181 func (s *coinTestSuite) TestAddCoinAmount() { 182 cases := []struct { 183 coin sdk.Coin 184 amount math.Int 185 expected sdk.Coin 186 }{ 187 {sdk.NewInt64Coin(testDenom1, 1), math.NewInt(1), sdk.NewInt64Coin(testDenom1, 2)}, 188 {sdk.NewInt64Coin(testDenom1, 1), math.NewInt(0), sdk.NewInt64Coin(testDenom1, 1)}, 189 } 190 for i, tc := range cases { 191 res := tc.coin.AddAmount(tc.amount) 192 s.Require().Equal(tc.expected, res, "result of addition is incorrect, tc #%d", i) 193 } 194 } 195 196 func (s *coinTestSuite) TestSubCoin() { 197 cases := []struct { 198 inputOne sdk.Coin 199 inputTwo sdk.Coin 200 expected sdk.Coin 201 shouldPanic bool 202 }{ 203 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), sdk.NewInt64Coin(testDenom1, 1), true}, 204 {sdk.NewInt64Coin(testDenom1, 10), sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 9), false}, 205 {sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt64Coin(testDenom1, 3), sdk.NewInt64Coin(testDenom1, 2), false}, 206 {sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom1, 5), false}, 207 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 5), sdk.Coin{}, true}, 208 } 209 210 for tcIndex, tc := range cases { 211 tc := tc 212 if tc.shouldPanic { 213 s.Require().Panics(func() { tc.inputOne.Sub(tc.inputTwo) }) 214 } else { 215 res := tc.inputOne.Sub(tc.inputTwo) 216 s.Require().Equal(tc.expected, res, "difference of coins is incorrect, tc #%d", tcIndex) 217 } 218 } 219 220 tc := struct { 221 inputOne sdk.Coin 222 inputTwo sdk.Coin 223 expected int64 224 }{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), 0} 225 res := tc.inputOne.Sub(tc.inputTwo) 226 s.Require().Equal(tc.expected, res.Amount.Int64()) 227 } 228 229 func (s *coinTestSuite) TestSubCoinAmount() { 230 cases := []struct { 231 coin sdk.Coin 232 amount math.Int 233 expected sdk.Coin 234 shouldPanic bool 235 }{ 236 {sdk.NewInt64Coin(testDenom1, 2), math.NewInt(1), sdk.NewInt64Coin(testDenom1, 1), false}, 237 {sdk.NewInt64Coin(testDenom1, 10), math.NewInt(1), sdk.NewInt64Coin(testDenom1, 9), false}, 238 {sdk.NewInt64Coin(testDenom1, 5), math.NewInt(3), sdk.NewInt64Coin(testDenom1, 2), false}, 239 {sdk.NewInt64Coin(testDenom1, 5), math.NewInt(0), sdk.NewInt64Coin(testDenom1, 5), false}, 240 {sdk.NewInt64Coin(testDenom1, 1), math.NewInt(5), sdk.Coin{}, true}, 241 } 242 243 for i, tc := range cases { 244 if tc.shouldPanic { 245 s.Require().Panics(func() { tc.coin.SubAmount(tc.amount) }) 246 } else { 247 res := tc.coin.SubAmount(tc.amount) 248 s.Require().Equal(tc.expected, res, "result of subtraction is incorrect, tc #%d", i) 249 } 250 } 251 } 252 253 func (s *coinTestSuite) TestMulIntCoins() { 254 testCases := []struct { 255 input sdk.Coins 256 multiplier math.Int 257 expected sdk.Coins 258 shouldPanic bool 259 }{ 260 {sdk.Coins{s.ca2}, math.NewInt(0), sdk.Coins{s.ca0}, true}, 261 {sdk.Coins{s.ca2}, math.NewInt(2), sdk.Coins{s.ca4}, false}, 262 {sdk.Coins{s.ca1, s.cm2}, math.NewInt(2), sdk.Coins{s.ca2, s.cm4}, false}, 263 } 264 265 assert := s.Assert() 266 for i, tc := range testCases { 267 tc := tc 268 if tc.shouldPanic { 269 assert.Panics(func() { tc.input.MulInt(tc.multiplier) }) 270 } else { 271 res := tc.input.MulInt(tc.multiplier) 272 assert.True(res.IsValid()) 273 assert.Equal(tc.expected, res, "multiplication of coins is incorrect, tc #%d", i) 274 } 275 } 276 } 277 278 func (s *coinTestSuite) TestQuoIntCoins() { 279 testCases := []struct { 280 input sdk.Coins 281 divisor math.Int 282 expected sdk.Coins 283 isValid bool 284 shouldPanic bool 285 }{ 286 {sdk.Coins{s.ca2, s.ca1}, math.NewInt(0), sdk.Coins{s.ca0, s.ca0}, true, true}, 287 {sdk.Coins{s.ca2}, math.NewInt(4), sdk.Coins{s.ca0}, false, false}, 288 {sdk.Coins{s.ca2, s.cm4}, math.NewInt(2), sdk.Coins{s.ca1, s.cm2}, true, false}, 289 {sdk.Coins{s.ca4}, math.NewInt(2), sdk.Coins{s.ca2}, true, false}, 290 } 291 292 assert := s.Assert() 293 for i, tc := range testCases { 294 tc := tc 295 if tc.shouldPanic { 296 assert.Panics(func() { tc.input.QuoInt(tc.divisor) }) 297 } else { 298 res := tc.input.QuoInt(tc.divisor) 299 assert.Equal(tc.isValid, res.IsValid()) 300 assert.Equal(tc.expected, res, "quotient of coins is incorrect, tc #%d", i) 301 } 302 } 303 } 304 305 func (s *coinTestSuite) TestIsGTECoin() { 306 cases := []struct { 307 inputOne sdk.Coin 308 inputTwo sdk.Coin 309 expected bool 310 panics bool 311 }{ 312 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), true, false}, 313 {sdk.NewInt64Coin(testDenom1, 2), sdk.NewInt64Coin(testDenom1, 1), true, false}, 314 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 2), false, false}, 315 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), false, true}, 316 } 317 318 for tcIndex, tc := range cases { 319 tc := tc 320 if tc.panics { 321 s.Require().Panics(func() { tc.inputOne.IsGTE(tc.inputTwo) }) 322 } else { 323 res := tc.inputOne.IsGTE(tc.inputTwo) 324 s.Require().Equal(tc.expected, res, "coin GTE relation is incorrect, tc #%d", tcIndex) 325 } 326 } 327 } 328 329 func (s *coinTestSuite) TestIsLTECoin() { 330 cases := []struct { 331 inputOne sdk.Coin 332 inputTwo sdk.Coin 333 expected bool 334 panics bool 335 }{ 336 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), true, false}, 337 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 2), true, false}, 338 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), false, true}, 339 {sdk.NewInt64Coin(testDenom1, 2), sdk.NewInt64Coin(testDenom1, 1), false, false}, 340 } 341 342 for tcIndex, tc := range cases { 343 tc := tc 344 if tc.panics { 345 s.Require().Panics(func() { tc.inputOne.IsLTE(tc.inputTwo) }) 346 } else { 347 res := tc.inputOne.IsLTE(tc.inputTwo) 348 s.Require().Equal(tc.expected, res, "coin LTE relation is incorrect, tc #%d", tcIndex) 349 } 350 } 351 } 352 353 func (s *coinTestSuite) TestIsLTCoin() { 354 cases := []struct { 355 inputOne sdk.Coin 356 inputTwo sdk.Coin 357 expected bool 358 panics bool 359 }{ 360 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), false, false}, 361 {sdk.NewInt64Coin(testDenom1, 2), sdk.NewInt64Coin(testDenom1, 1), false, false}, 362 {sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1), false, true}, 363 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), false, true}, 364 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), false, false}, 365 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 2), true, false}, 366 } 367 368 for tcIndex, tc := range cases { 369 tc := tc 370 if tc.panics { 371 s.Require().Panics(func() { tc.inputOne.IsLT(tc.inputTwo) }) 372 } else { 373 res := tc.inputOne.IsLT(tc.inputTwo) 374 s.Require().Equal(tc.expected, res, "coin LT relation is incorrect, tc #%d", tcIndex) 375 } 376 } 377 } 378 379 func (s *coinTestSuite) TestCoinIsZero() { 380 coin := sdk.NewInt64Coin(testDenom1, 0) 381 res := coin.IsZero() 382 s.Require().True(res) 383 384 coin = sdk.NewInt64Coin(testDenom1, 1) 385 res = coin.IsZero() 386 s.Require().False(res) 387 } 388 389 func (s *coinTestSuite) TestCoinIsNil() { 390 coin := sdk.Coin{} 391 res := coin.IsNil() 392 s.Require().True(res) 393 394 coin = sdk.Coin{Denom: "uatom"} 395 res = coin.IsNil() 396 s.Require().True(res) 397 398 coin = sdk.NewInt64Coin(testDenom1, 1) 399 res = coin.IsNil() 400 s.Require().False(res) 401 } 402 403 func (s *coinTestSuite) TestFilteredZeroCoins() { 404 cases := []struct { 405 name string 406 input sdk.Coins 407 original string 408 expected string 409 }{ 410 { 411 name: "all greater than zero", 412 input: sdk.Coins{ 413 {"testa", math.OneInt()}, 414 {"testb", math.NewInt(2)}, 415 {"testc", math.NewInt(3)}, 416 {"testd", math.NewInt(4)}, 417 {"teste", math.NewInt(5)}, 418 }, 419 original: "1testa,2testb,3testc,4testd,5teste", 420 expected: "1testa,2testb,3testc,4testd,5teste", 421 }, 422 { 423 name: "zero coin in middle", 424 input: sdk.Coins{ 425 {"testa", math.OneInt()}, 426 {"testb", math.NewInt(2)}, 427 {"testc", math.NewInt(0)}, 428 {"testd", math.NewInt(4)}, 429 {"teste", math.NewInt(5)}, 430 }, 431 original: "1testa,2testb,0testc,4testd,5teste", 432 expected: "1testa,2testb,4testd,5teste", 433 }, 434 { 435 name: "zero coin end (unordered)", 436 input: sdk.Coins{ 437 {"teste", math.NewInt(5)}, 438 {"testc", math.NewInt(3)}, 439 {"testa", math.OneInt()}, 440 {"testd", math.NewInt(4)}, 441 {"testb", math.NewInt(0)}, 442 }, 443 original: "5teste,3testc,1testa,4testd,0testb", 444 expected: "1testa,3testc,4testd,5teste", 445 }, 446 } 447 448 for _, tt := range cases { 449 undertest := sdk.NewCoins(tt.input...) 450 s.Require().Equal(tt.expected, undertest.String(), "NewCoins must return expected results") 451 s.Require().Equal(tt.original, tt.input.String(), "input must be unmodified and match original") 452 } 453 } 454 455 // ---------------------------------------------------------------------------- 456 // Coins tests 457 458 func (s *coinTestSuite) TestCoins_String() { 459 cases := []struct { 460 name string 461 input sdk.Coins 462 expected string 463 }{ 464 { 465 "empty coins", 466 sdk.Coins{}, 467 "", 468 }, 469 { 470 "single coin", 471 sdk.Coins{{"tree", math.OneInt()}}, 472 "1tree", 473 }, 474 { 475 "multiple coins", 476 sdk.Coins{ 477 {"tree", math.OneInt()}, 478 {"gas", math.OneInt()}, 479 {"mineral", math.OneInt()}, 480 }, 481 "1tree,1gas,1mineral", 482 }, 483 } 484 485 for _, tt := range cases { 486 s.Require().Equal(tt.expected, tt.input.String()) 487 } 488 } 489 490 func (s *coinTestSuite) TestIsZeroCoins() { 491 cases := []struct { 492 inputOne sdk.Coins 493 expected bool 494 }{ 495 {sdk.Coins{}, true}, 496 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, true}, 497 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 0)}, true}, 498 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 1)}, false}, 499 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, false}, 500 } 501 502 for _, tc := range cases { 503 res := tc.inputOne.IsZero() 504 s.Require().Equal(tc.expected, res) 505 } 506 } 507 508 func (s *coinTestSuite) TestEqualCoins() { 509 cases := []struct { 510 inputOne sdk.Coins 511 inputTwo sdk.Coins 512 expected bool 513 }{ 514 {sdk.Coins{}, sdk.Coins{}, true}, 515 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, true}, 516 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, true}, 517 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom2, 0)}, false}, 518 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 1)}, false}, 519 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, false}, 520 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, true}, 521 } 522 523 for tcnum, tc := range cases { 524 res := tc.inputOne.Equal(tc.inputTwo) 525 s.Require().Equal(tc.expected, res, "Equality is differed from exported. tc #%d, expected %b, actual %b.", tcnum, tc.expected, res) 526 } 527 } 528 529 func (s *coinTestSuite) TestAddCoins() { 530 cA0M0 := sdk.Coins{s.ca0, s.cm0} 531 cA0M1 := sdk.Coins{s.ca0, s.cm1} 532 cA1M1 := sdk.Coins{s.ca1, s.cm1} 533 cases := []struct { 534 name string 535 inputOne sdk.Coins 536 inputTwo sdk.Coins 537 expected sdk.Coins 538 msg string 539 }{ 540 {"adding two empty lists", s.emptyCoins, s.emptyCoins, s.emptyCoins, "empty, non list should be returned"}, 541 {"empty list + set", s.emptyCoins, cA0M1, sdk.Coins{s.cm1}, "zero coins should be removed"}, 542 {"empty list + set", s.emptyCoins, cA1M1, cA1M1, "zero + a_non_zero = a_non_zero"}, 543 {"set + empty list", cA0M1, s.emptyCoins, sdk.Coins{s.cm1}, "zero coins should be removed"}, 544 {"set + empty list", cA1M1, s.emptyCoins, cA1M1, "a_non_zero + zero = a_non_zero"}, 545 { 546 "{1atom,1muon}+{1atom,1muon}", cA1M1, cA1M1, 547 sdk.Coins{s.ca2, s.cm2}, 548 "a + a = 2a", 549 }, 550 { 551 "{0atom,1muon}+{0atom,0muon}", cA0M1, cA0M0, 552 sdk.Coins{s.cm1}, 553 "zero coins should be removed", 554 }, 555 { 556 "{2atom}+{0muon}", 557 sdk.Coins{s.ca2}, 558 sdk.Coins{s.cm0}, 559 sdk.Coins{s.ca2}, 560 "zero coins should be removed", 561 }, 562 { 563 "{1atom}+{1atom,2muon}", 564 sdk.Coins{s.ca1}, 565 sdk.Coins{s.ca1, s.cm2}, 566 sdk.Coins{s.ca2, s.cm2}, 567 "should be correctly added", 568 }, 569 { 570 "{0atom,0muon}+{0atom,0muon}", cA0M0, cA0M0, s.emptyCoins, 571 "sets with zero coins should return empty set", 572 }, 573 } 574 575 for _, tc := range cases { 576 s.T().Run(tc.name, func(t *testing.T) { 577 res := tc.inputOne.Add(tc.inputTwo...) 578 require.True(t, res.IsValid(), fmt.Sprintf("%s + %s = %s", tc.inputOne, tc.inputTwo, res)) 579 require.Equal(t, tc.expected, res, tc.msg) 580 }) 581 } 582 } 583 584 // Tests that even if coins with repeated denominations are passed into .Add that they 585 // are correctly coalesced. Please see issue https://github.com/cosmos/cosmos-sdk/issues/13234 586 func TestCoinsAddCoalescesDuplicateDenominations(t *testing.T) { 587 A := sdk.Coins{ 588 {"den", math.NewInt(2)}, 589 {"den", math.NewInt(3)}, 590 } 591 B := sdk.Coins{ 592 {"den", math.NewInt(3)}, 593 {"den", math.NewInt(2)}, 594 {"den", math.NewInt(1)}, 595 } 596 597 A = A.Sort() 598 B = B.Sort() 599 got := A.Add(B...) 600 601 want := sdk.Coins{ 602 {"den", math.NewInt(11)}, 603 } 604 605 if !got.Equal(want) { 606 t.Fatalf("Wrong result\n\tGot: %s\n\tWant: %s", got, want) 607 } 608 } 609 610 func (s *coinTestSuite) TestSubCoins() { 611 cA0M0 := sdk.Coins{s.ca0, s.cm0} 612 cA0M1 := sdk.Coins{s.ca0, s.cm1} 613 testCases := []struct { 614 inputOne sdk.Coins 615 inputTwo sdk.Coins 616 expected sdk.Coins 617 shouldPanic bool 618 }{ 619 {s.emptyCoins, s.emptyCoins, s.emptyCoins, false}, 620 {cA0M0, s.emptyCoins, s.emptyCoins, false}, 621 {cA0M0, sdk.Coins{s.cm0}, s.emptyCoins, false}, 622 {sdk.Coins{s.cm0}, cA0M0, s.emptyCoins, false}, 623 {cA0M1, s.emptyCoins, sdk.Coins{s.cm1}, false}, 624 // denoms are not sorted - should panic 625 {sdk.Coins{s.ca2}, sdk.Coins{s.cm2, s.ca1}, sdk.Coins{}, true}, 626 {sdk.Coins{s.cm2, s.ca2}, sdk.Coins{s.ca1}, sdk.Coins{}, true}, 627 // test cases for sorted denoms 628 {sdk.Coins{s.ca2}, sdk.Coins{s.ca1, s.cm2}, sdk.Coins{s.ca1, s.cm2}, true}, 629 {sdk.Coins{s.ca2}, sdk.Coins{s.cm0}, sdk.Coins{s.ca2}, false}, 630 {sdk.Coins{s.ca1}, sdk.Coins{s.cm0}, sdk.Coins{s.ca1}, false}, 631 {sdk.Coins{s.ca1, s.cm1}, sdk.Coins{s.ca1}, sdk.Coins{s.cm1}, false}, 632 {sdk.Coins{s.ca1, s.cm1}, sdk.Coins{s.ca2}, sdk.Coins{}, true}, 633 } 634 635 assert := s.Assert() 636 for i, tc := range testCases { 637 tc := tc 638 if tc.shouldPanic { 639 assert.Panics(func() { tc.inputOne.Sub(tc.inputTwo...) }) 640 } else { 641 res := tc.inputOne.Sub(tc.inputTwo...) 642 assert.True(res.IsValid()) 643 assert.Equal(tc.expected, res, "sum of coins is incorrect, tc #%d", i) 644 } 645 } 646 } 647 648 func (s *coinTestSuite) TestSafeSubCoin() { 649 cases := []struct { 650 inputOne sdk.Coin 651 inputTwo sdk.Coin 652 expected sdk.Coin 653 expErrMsg string 654 }{ 655 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), sdk.NewInt64Coin(testDenom1, 1), "invalid coin denoms"}, 656 {sdk.NewInt64Coin(testDenom1, 10), sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 9), ""}, 657 {sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom1, 5), ""}, 658 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 5), sdk.Coin{}, "negative coin amount"}, 659 } 660 661 for _, tc := range cases { 662 tc := tc 663 res, err := tc.inputOne.SafeSub(tc.inputTwo) 664 if err != nil { 665 s.Require().Contains(err.Error(), tc.expErrMsg) 666 return 667 } 668 s.Require().Equal(tc.expected, res) 669 } 670 } 671 672 func (s *coinTestSuite) TestCoins_Validate() { 673 testCases := []struct { 674 name string 675 coins sdk.Coins 676 expPass bool 677 }{ 678 { 679 "valid lowercase coins", 680 sdk.Coins{ 681 {"gas", math.OneInt()}, 682 {"mineral", math.OneInt()}, 683 {"tree", math.OneInt()}, 684 }, 685 true, 686 }, 687 { 688 "valid uppercase coins", 689 sdk.Coins{ 690 {"GAS", math.OneInt()}, 691 {"MINERAL", math.OneInt()}, 692 {"TREE", math.OneInt()}, 693 }, 694 true, 695 }, 696 { 697 "valid uppercase coin", 698 sdk.Coins{ 699 {"ATOM", math.OneInt()}, 700 }, 701 true, 702 }, 703 { 704 "valid lower and uppercase coins (1)", 705 sdk.Coins{ 706 {"GAS", math.OneInt()}, 707 {"gAs", math.OneInt()}, 708 }, 709 true, 710 }, 711 { 712 "valid lower and uppercase coins (2)", 713 sdk.Coins{ 714 {"ATOM", math.OneInt()}, 715 {"Atom", math.OneInt()}, 716 {"atom", math.OneInt()}, 717 }, 718 true, 719 }, 720 { 721 "mixed case (1)", 722 sdk.Coins{ 723 {"MineraL", math.OneInt()}, 724 {"TREE", math.OneInt()}, 725 {"gAs", math.OneInt()}, 726 }, 727 true, 728 }, 729 { 730 "mixed case (2)", 731 sdk.Coins{ 732 {"gAs", math.OneInt()}, 733 {"mineral", math.OneInt()}, 734 }, 735 true, 736 }, 737 { 738 "mixed case (3)", 739 sdk.Coins{ 740 {"gAs", math.OneInt()}, 741 }, 742 true, 743 }, 744 { 745 "unicode letters and numbers", 746 sdk.Coins{ 747 {"𐀀𐀆𐀉Ⅲ", math.OneInt()}, 748 }, 749 false, 750 }, 751 { 752 "emojis", 753 sdk.Coins{ 754 {"🤑😋🤔", math.OneInt()}, 755 }, 756 false, 757 }, 758 { 759 "IBC denominations (ADR 001)", 760 sdk.Coins{ 761 {"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", math.OneInt()}, 762 {"ibc/876563AAAACF739EB061C67CDB5EDF2B7C9FD4AA9D876450CC21210807C2820A", math.NewInt(2)}, 763 }, 764 true, 765 }, 766 { 767 "empty (1)", 768 sdk.NewCoins(), 769 true, 770 }, 771 { 772 "empty (2)", 773 sdk.Coins{}, 774 true, 775 }, 776 { 777 "invalid denomination (1)", 778 sdk.Coins{ 779 {"MineraL", math.OneInt()}, 780 {"0TREE", math.OneInt()}, 781 {"gAs", math.OneInt()}, 782 }, 783 false, 784 }, 785 { 786 "invalid denomination (2)", 787 sdk.Coins{ 788 {"-GAS", math.OneInt()}, 789 {"gAs", math.OneInt()}, 790 }, 791 false, 792 }, 793 { 794 "bad sort (1)", 795 sdk.Coins{ 796 {"tree", math.OneInt()}, 797 {"gas", math.OneInt()}, 798 {"mineral", math.OneInt()}, 799 }, 800 false, 801 }, 802 { 803 "bad sort (2)", 804 sdk.Coins{ 805 {"gas", math.OneInt()}, 806 {"tree", math.OneInt()}, 807 {"mineral", math.OneInt()}, 808 }, 809 false, 810 }, 811 { 812 "bad sort (3)", 813 sdk.Coins{ 814 {"gas", math.OneInt()}, 815 {"tree", math.OneInt()}, 816 {"gas", math.OneInt()}, 817 }, 818 false, 819 }, 820 { 821 "non-positive amount (1)", 822 sdk.Coins{ 823 {"gas", math.OneInt()}, 824 {"tree", math.NewInt(0)}, 825 {"mineral", math.OneInt()}, 826 }, 827 false, 828 }, 829 { 830 "non-positive amount (2)", 831 sdk.Coins{ 832 {"gas", math.NewInt(-1)}, 833 {"tree", math.OneInt()}, 834 {"mineral", math.OneInt()}, 835 }, 836 false, 837 }, 838 { 839 "duplicate denomination (1)", 840 sdk.Coins{ 841 {"gas", math.OneInt()}, 842 {"gas", math.OneInt()}, 843 {"mineral", math.OneInt()}, 844 }, 845 false, 846 }, 847 { 848 "duplicate denomination (2)", 849 sdk.Coins{ 850 {"gold", math.OneInt()}, 851 {"gold", math.OneInt()}, 852 }, 853 false, 854 }, 855 { 856 "duplicate denomination (3)", 857 sdk.Coins{ 858 {"gas", math.OneInt()}, 859 {"mineral", math.OneInt()}, 860 {"silver", math.OneInt()}, 861 {"silver", math.OneInt()}, 862 }, 863 false, 864 }, 865 } 866 867 for _, tc := range testCases { 868 err := tc.coins.Validate() 869 if tc.expPass { 870 s.Require().NoError(err, tc.name) 871 } else { 872 s.Require().Error(err, tc.name) 873 } 874 } 875 } 876 877 func (s *coinTestSuite) TestMinMax() { 878 one := math.OneInt() 879 two := math.NewInt(2) 880 881 cases := []struct { 882 name string 883 input1 sdk.Coins 884 input2 sdk.Coins 885 min sdk.Coins 886 max sdk.Coins 887 }{ 888 {"zero-zero", sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, sdk.Coins{}}, 889 {"zero-one", sdk.Coins{}, sdk.Coins{{testDenom1, one}}, sdk.Coins{}, sdk.Coins{{testDenom1, one}}}, 890 {"two-zero", sdk.Coins{{testDenom2, two}}, sdk.Coins{}, sdk.Coins{}, sdk.Coins{{testDenom2, two}}}, 891 {"disjoint", sdk.Coins{{testDenom1, one}}, sdk.Coins{{testDenom2, two}}, sdk.Coins{}, sdk.Coins{{testDenom1, one}, {testDenom2, two}}}, 892 { 893 "overlap", 894 sdk.Coins{{testDenom1, one}, {testDenom2, two}}, 895 sdk.Coins{{testDenom1, two}, {testDenom2, one}}, 896 sdk.Coins{{testDenom1, one}, {testDenom2, one}}, 897 sdk.Coins{{testDenom1, two}, {testDenom2, two}}, 898 }, 899 } 900 901 for _, tc := range cases { 902 min := tc.input1.Min(tc.input2) 903 max := tc.input1.Max(tc.input2) 904 s.Require().True(min.Equal(tc.min), tc.name) 905 s.Require().True(max.Equal(tc.max), tc.name) 906 } 907 } 908 909 func (s *coinTestSuite) TestCoinsGT() { 910 one := math.OneInt() 911 two := math.NewInt(2) 912 913 s.Require().False(sdk.Coins{}.IsAllGT(sdk.Coins{})) 914 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{})) 915 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, one}})) 916 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom2, one}})) 917 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom2, one}})) 918 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(sdk.Coins{{testDenom2, two}})) 919 } 920 921 func (s *coinTestSuite) TestCoinsLT() { 922 one := math.OneInt() 923 two := math.NewInt(2) 924 925 s.Require().False(sdk.Coins{}.IsAllLT(sdk.Coins{})) 926 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLT(sdk.Coins{})) 927 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLT(sdk.Coins{{testDenom1, one}})) 928 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLT(sdk.Coins{{testDenom2, one}})) 929 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom2, one}})) 930 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom2, two}})) 931 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) 932 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom1, two}, {testDenom2, two}})) 933 s.Require().True(sdk.Coins{}.IsAllLT(sdk.Coins{{testDenom1, one}})) 934 } 935 936 func (s *coinTestSuite) TestCoinsLTE() { 937 one := math.OneInt() 938 two := math.NewInt(2) 939 940 s.Require().True(sdk.Coins{}.IsAllLTE(sdk.Coins{})) 941 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLTE(sdk.Coins{})) 942 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllLTE(sdk.Coins{{testDenom1, one}})) 943 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLTE(sdk.Coins{{testDenom2, one}})) 944 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom2, one}})) 945 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom2, two}})) 946 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) 947 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 948 s.Require().True(sdk.Coins{}.IsAllLTE(sdk.Coins{{testDenom1, one}})) 949 } 950 951 func (s *coinTestSuite) TestParseCoins() { 952 one := math.OneInt() 953 954 cases := []struct { 955 input string 956 valid bool // if false, we expect an error on parse 957 expected sdk.Coins // if valid is true, make sure this is returned 958 }{ 959 {"", true, nil}, 960 {"0stake", true, sdk.Coins{}}, // remove zero coins 961 {"0stake,1foo,99bar", true, sdk.Coins{{"bar", math.NewInt(99)}, {"foo", one}}}, // remove zero coins 962 {"1foo", true, sdk.Coins{{"foo", one}}}, 963 {"10btc,1atom,20btc", false, nil}, 964 {"10bar", true, sdk.Coins{{"bar", math.NewInt(10)}}}, 965 {"99bar,1foo", true, sdk.Coins{{"bar", math.NewInt(99)}, {"foo", one}}}, 966 {"98 bar , 1 foo ", true, sdk.Coins{{"bar", math.NewInt(98)}, {"foo", one}}}, 967 {" 55\t \t bling\n", true, sdk.Coins{{"bling", math.NewInt(55)}}}, 968 {"2foo, 97 bar", true, sdk.Coins{{"bar", math.NewInt(97)}, {"foo", math.NewInt(2)}}}, 969 {"5 mycoin,", false, nil}, // no empty coins in a list 970 {"2 3foo, 97 bar", false, nil}, // 3foo is invalid coin name 971 {"11me coin, 12you coin", false, nil}, // no spaces in coin names 972 {"1.2btc", true, sdk.Coins{{"btc", math.NewInt(1)}}}, // amount can be decimal, will get truncated 973 {"5foo:bar", true, sdk.Coins{{"foo:bar", math.NewInt(5)}}}, 974 {"10atom10", true, sdk.Coins{{"atom10", math.NewInt(10)}}}, 975 {"200transfer/channelToA/uatom", true, sdk.Coins{{"transfer/channelToA/uatom", math.NewInt(200)}}}, 976 {"50ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", true, sdk.Coins{{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", math.NewInt(50)}}}, 977 {"120000000000000000000000000000000000000000000000000000000000000000000000000000btc", false, nil}, 978 } 979 980 for tcIndex, tc := range cases { 981 res, err := sdk.ParseCoinsNormalized(tc.input) 982 if !tc.valid { 983 s.Require().Error(err, "%s: %#v. tc #%d", tc.input, res, tcIndex) 984 } else if s.Assert().Nil(err, "%s: %+v", tc.input, err) { 985 s.Require().Equal(tc.expected, res, "coin parsing was incorrect, tc #%d", tcIndex) 986 } 987 } 988 } 989 990 func (s *coinTestSuite) TestSortCoins() { 991 good := sdk.Coins{ 992 sdk.NewInt64Coin("gas", 1), 993 sdk.NewInt64Coin("mineral", 1), 994 sdk.NewInt64Coin("tree", 1), 995 } 996 empty := sdk.Coins{ 997 sdk.NewInt64Coin("gold", 0), 998 } 999 badSort1 := sdk.Coins{ 1000 sdk.NewInt64Coin("tree", 1), 1001 sdk.NewInt64Coin("gas", 1), 1002 sdk.NewInt64Coin("mineral", 1), 1003 } 1004 badSort2 := sdk.Coins{ // both are after the first one, but the second and third are in the wrong order 1005 sdk.NewInt64Coin("gas", 1), 1006 sdk.NewInt64Coin("tree", 1), 1007 sdk.NewInt64Coin("mineral", 1), 1008 } 1009 badAmt := sdk.Coins{ 1010 sdk.NewInt64Coin("gas", 1), 1011 sdk.NewInt64Coin("tree", 0), 1012 sdk.NewInt64Coin("mineral", 1), 1013 } 1014 dup := sdk.Coins{ 1015 sdk.NewInt64Coin("gas", 1), 1016 sdk.NewInt64Coin("gas", 1), 1017 sdk.NewInt64Coin("mineral", 1), 1018 } 1019 1020 cases := []struct { 1021 name string 1022 coins sdk.Coins 1023 validBefore, 1024 validAfter bool 1025 }{ 1026 {"valid coins", good, true, true}, 1027 {"empty coins", empty, false, false}, 1028 {"bad sort (1)", badSort1, false, true}, 1029 {"bad sort (2)", badSort2, false, true}, 1030 {"zero value coin", badAmt, false, false}, 1031 {"duplicate coins", dup, false, false}, 1032 } 1033 1034 for _, tc := range cases { 1035 err := tc.coins.Validate() 1036 if tc.validBefore { 1037 s.Require().NoError(err, tc.name) 1038 } else { 1039 s.Require().Error(err, tc.name) 1040 } 1041 1042 tc.coins.Sort() 1043 1044 err = tc.coins.Validate() 1045 if tc.validAfter { 1046 s.Require().NoError(err, tc.name) 1047 } else { 1048 s.Require().Error(err, tc.name) 1049 } 1050 } 1051 } 1052 1053 func (s *coinTestSuite) TestSearch() { 1054 require := s.Require() 1055 case0 := sdk.Coins{} 1056 case1 := sdk.Coins{ 1057 sdk.NewInt64Coin("gold", 0), 1058 } 1059 case2 := sdk.Coins{ 1060 sdk.NewInt64Coin("gas", 1), 1061 sdk.NewInt64Coin("mineral", 1), 1062 sdk.NewInt64Coin("tree", 1), 1063 } 1064 case3 := sdk.Coins{ 1065 sdk.NewInt64Coin("mineral", 1), 1066 sdk.NewInt64Coin("tree", 1), 1067 } 1068 case4 := sdk.Coins{ 1069 sdk.NewInt64Coin("gas", 8), 1070 } 1071 1072 amountOfCases := []struct { 1073 coins sdk.Coins 1074 amountOf int64 1075 amountOfSpace int64 1076 amountOfGAS int64 1077 amountOfMINERAL int64 1078 amountOfTREE int64 1079 }{ 1080 {case0, 0, 0, 0, 0, 0}, 1081 {case1, 0, 0, 0, 0, 0}, 1082 {case2, 0, 0, 1, 1, 1}, 1083 {case3, 0, 0, 0, 1, 1}, 1084 {case4, 0, 0, 8, 0, 0}, 1085 } 1086 1087 s.Run("AmountOf", func() { 1088 for i, tc := range amountOfCases { 1089 require.Equal(math.NewInt(tc.amountOfGAS), tc.coins.AmountOf("gas"), i) 1090 require.Equal(math.NewInt(tc.amountOfMINERAL), tc.coins.AmountOf("mineral"), i) 1091 require.Equal(math.NewInt(tc.amountOfTREE), tc.coins.AmountOf("tree"), i) 1092 } 1093 require.Panics(func() { amountOfCases[0].coins.AmountOf("10Invalid") }) 1094 }) 1095 1096 zeroCoin := sdk.Coin{} 1097 findCases := []struct { 1098 coins sdk.Coins 1099 denom string 1100 expectedOk bool 1101 expectedCoin sdk.Coin 1102 }{ 1103 {case0, "any", false, zeroCoin}, 1104 {case1, "other", false, zeroCoin}, 1105 {case1, "gold", true, case1[0]}, 1106 {case4, "gas", true, case4[0]}, 1107 {case2, "gas", true, case2[0]}, 1108 {case2, "mineral", true, case2[1]}, 1109 {case2, "tree", true, case2[2]}, 1110 {case2, "other", false, zeroCoin}, 1111 } 1112 s.Run("Find", func() { 1113 for i, tc := range findCases { 1114 ok, c := tc.coins.Find(tc.denom) 1115 require.Equal(tc.expectedOk, ok, i) 1116 require.Equal(tc.expectedCoin, c, i) 1117 } 1118 }) 1119 } 1120 1121 func (s *coinTestSuite) TestCoinsIsAnyGTE() { 1122 one := math.OneInt() 1123 two := math.NewInt(2) 1124 1125 s.Require().False(sdk.Coins{}.IsAnyGTE(sdk.Coins{})) 1126 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{})) 1127 s.Require().False(sdk.Coins{}.IsAnyGTE(sdk.Coins{{testDenom1, one}})) 1128 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom1, two}})) 1129 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom2, one}})) 1130 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(sdk.Coins{{testDenom1, two}, {testDenom2, one}})) 1131 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}})) 1132 s.Require().True(sdk.Coins{{testDenom1, two}}.IsAnyGTE(sdk.Coins{{testDenom1, one}})) 1133 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 1134 s.Require().True(sdk.Coins{{testDenom2, two}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 1135 s.Require().False(sdk.Coins{{testDenom2, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 1136 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) 1137 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 1138 s.Require().True(sdk.Coins{{"xxx", one}, {"yyy", one}}.IsAnyGTE(sdk.Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) 1139 } 1140 1141 func (s *coinTestSuite) TestCoinsIsAllGT() { 1142 one := math.OneInt() 1143 two := math.NewInt(2) 1144 1145 s.Require().False(sdk.Coins{}.IsAllGT(sdk.Coins{})) 1146 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{})) 1147 s.Require().False(sdk.Coins{}.IsAllGT(sdk.Coins{{testDenom1, one}})) 1148 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, two}})) 1149 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom2, one}})) 1150 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom1, two}, {testDenom2, one}})) 1151 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, one}})) 1152 s.Require().True(sdk.Coins{{testDenom1, two}}.IsAllGT(sdk.Coins{{testDenom1, one}})) 1153 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 1154 s.Require().False(sdk.Coins{{testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 1155 s.Require().False(sdk.Coins{{testDenom2, one}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 1156 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) 1157 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 1158 s.Require().False(sdk.Coins{{"xxx", one}, {"yyy", one}}.IsAllGT(sdk.Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) 1159 } 1160 1161 func (s *coinTestSuite) TestCoinsIsAllGTE() { 1162 one := math.OneInt() 1163 two := math.NewInt(2) 1164 1165 s.Require().True(sdk.Coins{}.IsAllGTE(sdk.Coins{})) 1166 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{})) 1167 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom2, one}})) 1168 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom2, two}})) 1169 s.Require().False(sdk.Coins{}.IsAllGTE(sdk.Coins{{testDenom1, one}})) 1170 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom1, two}})) 1171 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom2, one}})) 1172 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(sdk.Coins{{testDenom1, two}, {testDenom2, one}})) 1173 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}})) 1174 s.Require().True(sdk.Coins{{testDenom1, two}}.IsAllGTE(sdk.Coins{{testDenom1, one}})) 1175 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 1176 s.Require().False(sdk.Coins{{testDenom2, two}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 1177 s.Require().False(sdk.Coins{{testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 1178 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) 1179 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 1180 s.Require().False(sdk.Coins{{"xxx", one}, {"yyy", one}}.IsAllGTE(sdk.Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) 1181 } 1182 1183 func (s *coinTestSuite) TestNewCoins() { 1184 tenatom := sdk.NewInt64Coin("atom", 10) 1185 tenbtc := sdk.NewInt64Coin("btc", 10) 1186 zeroeth := sdk.NewInt64Coin("eth", 0) 1187 invalidCoin := sdk.Coin{"0ETH", math.OneInt()} 1188 tests := []struct { 1189 name string 1190 coins sdk.Coins 1191 want sdk.Coins 1192 wantPanic bool 1193 }{ 1194 {"empty args", []sdk.Coin{}, sdk.Coins{}, false}, 1195 {"one coin", []sdk.Coin{tenatom}, sdk.Coins{tenatom}, false}, 1196 {"sort after create", []sdk.Coin{tenbtc, tenatom}, sdk.Coins{tenatom, tenbtc}, false}, 1197 {"sort and remove zeroes", []sdk.Coin{zeroeth, tenbtc, tenatom}, sdk.Coins{tenatom, tenbtc}, false}, 1198 {"panic on dups", []sdk.Coin{tenatom, tenatom}, sdk.Coins{}, true}, 1199 {"panic on invalid coin", []sdk.Coin{invalidCoin, tenatom}, sdk.Coins{}, true}, 1200 } 1201 for _, tt := range tests { 1202 if tt.wantPanic { 1203 s.Require().Panics(func() { sdk.NewCoins(tt.coins...) }) 1204 continue 1205 } 1206 got := sdk.NewCoins(tt.coins...) 1207 s.Require().True(got.Equal(tt.want)) 1208 } 1209 } 1210 1211 func (s *coinTestSuite) TestCoinsIsAnyGT() { 1212 twoAtom := sdk.NewInt64Coin("atom", 2) 1213 fiveAtom := sdk.NewInt64Coin("atom", 5) 1214 threeEth := sdk.NewInt64Coin("eth", 3) 1215 sixEth := sdk.NewInt64Coin("eth", 6) 1216 twoBtc := sdk.NewInt64Coin("btc", 2) 1217 1218 tests := []struct { 1219 name string 1220 coinsA sdk.Coins 1221 coinsB sdk.Coins 1222 expPass bool 1223 }{ 1224 {"{} ≤ {}", sdk.Coins{}, sdk.Coins{}, false}, 1225 {"{} ≤ 5atom", sdk.Coins{}, sdk.Coins{fiveAtom}, false}, 1226 {"5atom > 2atom", sdk.Coins{fiveAtom}, sdk.Coins{twoAtom}, true}, 1227 {"2atom ≤ 5atom", sdk.Coins{twoAtom}, sdk.Coins{fiveAtom}, false}, 1228 {"2atom,6eth > 2btc,5atom,3eth", sdk.Coins{twoAtom, sixEth}, sdk.Coins{twoBtc, fiveAtom, threeEth}, true}, 1229 {"2btc,2atom,3eth ≤ 5atom,6eth", sdk.Coins{twoBtc, twoAtom, threeEth}, sdk.Coins{fiveAtom, sixEth}, false}, 1230 {"2atom,6eth ≤ 2btc,5atom", sdk.Coins{twoAtom, sixEth}, sdk.Coins{twoBtc, fiveAtom}, false}, 1231 } 1232 1233 for _, tc := range tests { 1234 s.Require().True(tc.expPass == tc.coinsA.IsAnyGT(tc.coinsB), tc.name) 1235 } 1236 } 1237 1238 func (s *coinTestSuite) TestCoinsIsAnyNil() { 1239 twoAtom := sdk.NewInt64Coin("atom", 2) 1240 fiveAtom := sdk.NewInt64Coin("atom", 5) 1241 threeEth := sdk.NewInt64Coin("eth", 3) 1242 nilAtom := sdk.Coin{Denom: "atom"} 1243 1244 s.Require().True(sdk.Coins{twoAtom, fiveAtom, threeEth, nilAtom}.IsAnyNil()) 1245 s.Require().True(sdk.Coins{twoAtom, nilAtom, fiveAtom, threeEth}.IsAnyNil()) 1246 s.Require().True(sdk.Coins{nilAtom, twoAtom, fiveAtom, threeEth}.IsAnyNil()) 1247 s.Require().False(sdk.Coins{twoAtom, fiveAtom, threeEth}.IsAnyNil()) 1248 } 1249 1250 func (s *coinTestSuite) TestMarshalJSONCoins() { 1251 cdc := codec.NewLegacyAmino() 1252 sdk.RegisterLegacyAminoCodec(cdc) 1253 1254 testCases := []struct { 1255 name string 1256 input sdk.Coins 1257 strOutput string 1258 }{ 1259 {"nil coins", nil, `[]`}, 1260 {"empty coins", sdk.Coins{}, `[]`}, 1261 {"non-empty coins", sdk.NewCoins(sdk.NewInt64Coin("foo", 50)), `[{"denom":"foo","amount":"50"}]`}, 1262 } 1263 1264 for _, tc := range testCases { 1265 bz, err := cdc.MarshalJSON(tc.input) 1266 s.Require().NoError(err) 1267 s.Require().Equal(tc.strOutput, string(bz)) 1268 1269 var newCoins sdk.Coins 1270 s.Require().NoError(cdc.UnmarshalJSON(bz, &newCoins)) 1271 1272 if tc.input.Empty() { 1273 s.Require().Nil(newCoins) 1274 } else { 1275 s.Require().Equal(tc.input, newCoins) 1276 } 1277 } 1278 } 1279 1280 func (s *coinTestSuite) TestCoinValidate() { 1281 testCases := []struct { 1282 name string 1283 coin sdk.Coin 1284 wantErr string 1285 }{ 1286 {"nil coin: nil Amount", sdk.Coin{}, "invalid denom"}, 1287 {"non-blank coin, nil Amount", sdk.Coin{Denom: "atom"}, "amount is nil"}, 1288 {"valid coin", sdk.Coin{Denom: "atom", Amount: math.NewInt(100)}, ""}, 1289 {"negative coin", sdk.Coin{Denom: "atom", Amount: math.NewInt(-999)}, "negative coin amount"}, 1290 } 1291 1292 for _, tc := range testCases { 1293 tc := tc 1294 t := s.T() 1295 t.Run(tc.name, func(t *testing.T) { 1296 err := tc.coin.Validate() 1297 switch { 1298 case tc.wantErr == "": 1299 if err != nil { 1300 t.Errorf("Unexpected error: %v", err) 1301 } 1302 return 1303 case err == nil: 1304 t.Error("Expected an error") 1305 case !strings.Contains(err.Error(), tc.wantErr): 1306 t.Errorf("Error mismatch\n\tGot: %q\nWant: %q", err, tc.wantErr) 1307 } 1308 }) 1309 } 1310 } 1311 1312 func (s *coinTestSuite) TestCoinAminoEncoding() { 1313 cdc := codec.NewLegacyAmino() 1314 c := sdk.NewInt64Coin(testDenom1, 5) 1315 1316 bz1, err := cdc.Marshal(c) 1317 s.Require().NoError(err) 1318 1319 bz2, err := cdc.MarshalLengthPrefixed(c) 1320 s.Require().NoError(err) 1321 1322 bz3, err := c.Marshal() 1323 s.Require().NoError(err) 1324 s.Require().Equal(bz1, bz3) 1325 s.Require().Equal(bz2[1:], bz3) 1326 }