code.vegaprotocol.io/vega@v0.79.0/core/bridges/erc20_logic.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package bridges 17 18 import ( 19 "encoding/hex" 20 "fmt" 21 "math/big" 22 "time" 23 24 crypto "code.vegaprotocol.io/vega/libs/crypto/signature" 25 "code.vegaprotocol.io/vega/libs/num" 26 27 "github.com/ethereum/go-ethereum/accounts/abi" 28 ethcmn "github.com/ethereum/go-ethereum/common" 29 ) 30 31 // ERC20Logic yea that's a weird name but 32 // it just matches the name of the contract. 33 type ERC20Logic struct { 34 signer Signer 35 bridgeAddr string 36 chainID string 37 v1 bool 38 } 39 40 func NewERC20Logic(signer Signer, bridgeAddr string, chainID string, v1 bool) *ERC20Logic { 41 return &ERC20Logic{ 42 signer: signer, 43 bridgeAddr: bridgeAddr, 44 chainID: chainID, 45 v1: v1, 46 } 47 } 48 49 func (e ERC20Logic) ListAsset( 50 tokenAddress string, 51 vegaAssetID string, 52 lifetimeLimit *num.Uint, 53 withdrawThreshold *num.Uint, 54 nonce *num.Uint, 55 ) (*SignaturePayload, error) { 56 typAddr, err := abi.NewType("address", "", nil) 57 if err != nil { 58 return nil, err 59 } 60 typBytes32, err := abi.NewType("bytes32", "", nil) 61 if err != nil { 62 return nil, err 63 } 64 typString, err := abi.NewType("string", "", nil) 65 if err != nil { 66 return nil, err 67 } 68 typU256, err := abi.NewType("uint256", "", nil) 69 if err != nil { 70 return nil, err 71 } 72 73 args := abi.Arguments([]abi.Argument{ 74 { 75 Name: "address", 76 Type: typAddr, 77 }, 78 { 79 Name: "vega_asset_id", 80 Type: typBytes32, 81 }, 82 { 83 Name: "lifetime_limit", 84 Type: typU256, 85 }, 86 { 87 Name: "withdraw_treshold", 88 Type: typU256, 89 }, 90 { 91 Name: "nonce", 92 Type: typU256, 93 }, 94 { 95 Name: "func_name", 96 Type: typString, 97 }, 98 }) 99 100 tokenAddressEth := ethcmn.HexToAddress(tokenAddress) 101 vegaAssetIDBytes, _ := hex.DecodeString(vegaAssetID) 102 var vegaAssetIDArray [32]byte 103 copy(vegaAssetIDArray[:], vegaAssetIDBytes[:32]) 104 buf, err := args.Pack([]interface{}{ 105 tokenAddressEth, 106 vegaAssetIDArray, 107 lifetimeLimit.BigInt(), 108 withdrawThreshold.BigInt(), 109 nonce.BigInt(), 110 "listAsset", 111 }...) 112 if err != nil { 113 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 114 } 115 116 msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) 117 if err != nil { 118 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 119 } 120 121 return sign(e.signer, msg) 122 } 123 124 func (e ERC20Logic) buildListAssetMessage( 125 tokenAddress string, 126 vegaAssetID string, 127 lifetimeLimit *num.Uint, 128 withdrawThreshold *num.Uint, 129 nonce *num.Uint, 130 ) ([]byte, error) { 131 typAddr, err := abi.NewType("address", "", nil) 132 if err != nil { 133 return nil, err 134 } 135 typBytes32, err := abi.NewType("bytes32", "", nil) 136 if err != nil { 137 return nil, err 138 } 139 typString, err := abi.NewType("string", "", nil) 140 if err != nil { 141 return nil, err 142 } 143 typU256, err := abi.NewType("uint256", "", nil) 144 if err != nil { 145 return nil, err 146 } 147 148 args := abi.Arguments([]abi.Argument{ 149 { 150 Name: "address", 151 Type: typAddr, 152 }, 153 { 154 Name: "vega_asset_id", 155 Type: typBytes32, 156 }, 157 { 158 Name: "lifetime_limit", 159 Type: typU256, 160 }, 161 { 162 Name: "withdraw_treshold", 163 Type: typU256, 164 }, 165 { 166 Name: "nonce", 167 Type: typU256, 168 }, 169 { 170 Name: "func_name", 171 Type: typString, 172 }, 173 }) 174 175 tokenAddressEth := ethcmn.HexToAddress(tokenAddress) 176 vegaAssetIDBytes, _ := hex.DecodeString(vegaAssetID) 177 var vegaAssetIDArray [32]byte 178 copy(vegaAssetIDArray[:], vegaAssetIDBytes[:32]) 179 buf, err := args.Pack([]interface{}{ 180 tokenAddressEth, 181 vegaAssetIDArray, 182 lifetimeLimit.BigInt(), 183 withdrawThreshold.BigInt(), 184 nonce.BigInt(), 185 "listAsset", 186 }...) 187 if err != nil { 188 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 189 } 190 191 msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) 192 if err != nil { 193 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 194 } 195 196 return msg, nil 197 } 198 199 func (e ERC20Logic) VerifyListAsset( 200 tokenAddress string, 201 vegaAssetID string, 202 lifetimeLimit *num.Uint, 203 withdrawThreshold *num.Uint, 204 nonce *num.Uint, 205 signatures string, 206 ) ([]string, error) { 207 msg, err := e.buildListAssetMessage( 208 tokenAddress, vegaAssetID, lifetimeLimit, withdrawThreshold, nonce, 209 ) 210 if err != nil { 211 return nil, err 212 } 213 214 addresses := []string{} 215 var hexCurrent string 216 signatures = signatures[2:] 217 for len(signatures) > 0 { 218 hexCurrent, signatures = signatures[0:130], signatures[130:] 219 current, err := hex.DecodeString(hexCurrent) 220 if err != nil { 221 return nil, fmt.Errorf("invalid signature format: %w", err) 222 } 223 224 address, err := crypto.RecoverEthereumAddress(msg, current) 225 if err != nil { 226 return nil, fmt.Errorf("error recovering ethereum address: %w", err) 227 } 228 229 addresses = append(addresses, address.Hex()) 230 } 231 232 return addresses, nil 233 } 234 235 func (e ERC20Logic) RemoveAsset( 236 tokenAddress string, 237 nonce *num.Uint, 238 ) (*SignaturePayload, error) { 239 typAddr, err := abi.NewType("address", "", nil) 240 if err != nil { 241 return nil, err 242 } 243 typString, err := abi.NewType("string", "", nil) 244 if err != nil { 245 return nil, err 246 } 247 typU256, err := abi.NewType("uint256", "", nil) 248 if err != nil { 249 return nil, err 250 } 251 252 args := abi.Arguments([]abi.Argument{ 253 { 254 Name: "address", 255 Type: typAddr, 256 }, 257 { 258 Name: "nonce", 259 Type: typU256, 260 }, 261 { 262 Name: "func_name", 263 Type: typString, 264 }, 265 }) 266 267 tokenAddressEth := ethcmn.HexToAddress(tokenAddress) 268 buf, err := args.Pack([]interface{}{ 269 tokenAddressEth, nonce.BigInt(), "removeAsset", 270 }...) 271 if err != nil { 272 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 273 } 274 275 msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) 276 if err != nil { 277 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 278 } 279 280 return sign(e.signer, msg) 281 } 282 283 func (e ERC20Logic) WithdrawAsset( 284 tokenAddress string, 285 amount *num.Uint, 286 ethPartyAddress string, 287 creation time.Time, 288 nonce *num.Uint, 289 ) (*SignaturePayload, error) { 290 msg, err := e.buildWithdrawAssetMessage( 291 tokenAddress, amount, ethPartyAddress, creation, nonce, 292 ) 293 if err != nil { 294 return nil, err 295 } 296 297 return sign(e.signer, msg) 298 } 299 300 func (e ERC20Logic) buildWithdrawAssetMessage( 301 tokenAddress string, 302 amount *num.Uint, 303 ethPartyAddress string, 304 creation time.Time, 305 nonce *num.Uint, 306 ) ([]byte, error) { 307 typAddr, err := abi.NewType("address", "", nil) 308 if err != nil { 309 return nil, err 310 } 311 typString, err := abi.NewType("string", "", nil) 312 if err != nil { 313 return nil, err 314 } 315 typU256, err := abi.NewType("uint256", "", nil) 316 if err != nil { 317 return nil, err 318 } 319 320 args := abi.Arguments([]abi.Argument{ 321 { 322 Name: "address", 323 Type: typAddr, 324 }, 325 { 326 Name: "uint256", 327 Type: typU256, 328 }, 329 { 330 Name: "address", 331 Type: typAddr, 332 }, 333 { 334 Name: "uint256", 335 Type: typU256, 336 }, 337 { 338 Name: "nonce", 339 Type: typU256, 340 }, 341 { 342 Name: "func_name", 343 Type: typString, 344 }, 345 }) 346 347 ethTokenAddr := ethcmn.HexToAddress(tokenAddress) 348 hexEthPartyAddress := ethcmn.HexToAddress(ethPartyAddress) 349 timestamp := big.NewInt(creation.Unix()) 350 351 buf, err := args.Pack([]interface{}{ 352 ethTokenAddr, 353 amount.BigInt(), 354 hexEthPartyAddress, 355 timestamp, 356 nonce.BigInt(), 357 "withdrawAsset", 358 }...) 359 if err != nil { 360 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 361 } 362 363 return packScheme(buf, e.bridgeAddr, e.chainID, e.v1) 364 } 365 366 func (e ERC20Logic) VerifyWithdrawAsset( 367 tokenAddress string, 368 amount *num.Uint, 369 ethPartyAddress string, 370 creation time.Time, 371 nonce *num.Uint, 372 signatures string, 373 ) ([]string, error) { 374 msg, err := e.buildWithdrawAssetMessage( 375 tokenAddress, amount, ethPartyAddress, creation, nonce, 376 ) 377 if err != nil { 378 return nil, err 379 } 380 381 addresses := []string{} 382 var hexCurrent string 383 signatures = signatures[2:] 384 for len(signatures) > 0 { 385 hexCurrent, signatures = signatures[0:130], signatures[130:] 386 current, err := hex.DecodeString(hexCurrent) 387 if err != nil { 388 return nil, fmt.Errorf("invalid signature format: %w", err) 389 } 390 391 address, err := crypto.RecoverEthereumAddress(msg, current) 392 if err != nil { 393 return nil, fmt.Errorf("error recovering ethereum address: %w", err) 394 } 395 396 addresses = append(addresses, address.Hex()) 397 } 398 399 return addresses, nil 400 } 401 402 func (e ERC20Logic) SetAssetLimits( 403 tokenAddress string, 404 lifetimeLimit *num.Uint, 405 withdrawThreshold *num.Uint, 406 nonce *num.Uint, 407 ) (*SignaturePayload, error) { 408 typAddr, err := abi.NewType("address", "", nil) 409 if err != nil { 410 return nil, err 411 } 412 typString, err := abi.NewType("string", "", nil) 413 if err != nil { 414 return nil, err 415 } 416 typU256, err := abi.NewType("uint256", "", nil) 417 if err != nil { 418 return nil, err 419 } 420 421 args := abi.Arguments([]abi.Argument{ 422 { 423 Name: "address", 424 Type: typAddr, 425 }, 426 { 427 Name: "uint256", 428 Type: typU256, 429 }, 430 { 431 Name: "uint256", 432 Type: typU256, 433 }, 434 { 435 Name: "uint256", 436 Type: typU256, 437 }, 438 { 439 Name: "func_name", 440 Type: typString, 441 }, 442 }) 443 444 ethTokenAddr := ethcmn.HexToAddress(tokenAddress) 445 buf, err := args.Pack([]interface{}{ 446 ethTokenAddr, 447 lifetimeLimit.BigInt(), 448 withdrawThreshold.BigInt(), 449 nonce.BigInt(), 450 "setAssetLimits", 451 }...) 452 if err != nil { 453 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 454 } 455 456 msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) 457 if err != nil { 458 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 459 } 460 461 return sign(e.signer, msg) 462 } 463 464 func (e ERC20Logic) buildSetAssetLimitsMessage( 465 tokenAddress string, 466 lifetimeLimit *num.Uint, 467 withdrawThreshold *num.Uint, 468 nonce *num.Uint, 469 ) ([]byte, error) { 470 typAddr, err := abi.NewType("address", "", nil) 471 if err != nil { 472 return nil, err 473 } 474 typString, err := abi.NewType("string", "", nil) 475 if err != nil { 476 return nil, err 477 } 478 typU256, err := abi.NewType("uint256", "", nil) 479 if err != nil { 480 return nil, err 481 } 482 483 args := abi.Arguments([]abi.Argument{ 484 { 485 Name: "address", 486 Type: typAddr, 487 }, 488 { 489 Name: "uint256", 490 Type: typU256, 491 }, 492 { 493 Name: "uint256", 494 Type: typU256, 495 }, 496 { 497 Name: "uint256", 498 Type: typU256, 499 }, 500 { 501 Name: "func_name", 502 Type: typString, 503 }, 504 }) 505 506 ethTokenAddr := ethcmn.HexToAddress(tokenAddress) 507 buf, err := args.Pack([]interface{}{ 508 ethTokenAddr, 509 lifetimeLimit.BigInt(), 510 withdrawThreshold.BigInt(), 511 nonce.BigInt(), 512 "setAssetLimits", 513 }...) 514 if err != nil { 515 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 516 } 517 518 msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) 519 if err != nil { 520 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 521 } 522 523 return msg, nil 524 } 525 526 func (e ERC20Logic) VerifySetAssetLimits( 527 tokenAddress string, 528 lifetimeLimit *num.Uint, 529 withdrawThreshold *num.Uint, 530 nonce *num.Uint, 531 signatures string, 532 ) ([]string, error) { 533 msg, err := e.buildSetAssetLimitsMessage( 534 tokenAddress, lifetimeLimit, withdrawThreshold, nonce, 535 ) 536 if err != nil { 537 return nil, err 538 } 539 540 addresses := []string{} 541 var hexCurrent string 542 signatures = signatures[2:] 543 for len(signatures) > 0 { 544 hexCurrent, signatures = signatures[0:130], signatures[130:] 545 current, err := hex.DecodeString(hexCurrent) 546 if err != nil { 547 return nil, fmt.Errorf("invalid signature format: %w", err) 548 } 549 550 address, err := crypto.RecoverEthereumAddress(msg, current) 551 if err != nil { 552 return nil, fmt.Errorf("error recovering ethereum address: %w", err) 553 } 554 555 addresses = append(addresses, address.Hex()) 556 } 557 558 return addresses, nil 559 } 560 561 func (e ERC20Logic) SetWithdrawDelay( 562 delay time.Duration, 563 nonce *num.Uint, 564 ) (*SignaturePayload, error) { 565 msg, err := e.buildWithdrawDelayMessage( 566 delay, nonce, 567 ) 568 if err != nil { 569 return nil, err 570 } 571 572 return sign(e.signer, msg) 573 } 574 575 func (e ERC20Logic) buildWithdrawDelayMessage( 576 delay time.Duration, 577 nonce *num.Uint, 578 ) ([]byte, error) { 579 typString, err := abi.NewType("string", "", nil) 580 if err != nil { 581 return nil, err 582 } 583 typU256, err := abi.NewType("uint256", "", nil) 584 if err != nil { 585 return nil, err 586 } 587 588 args := abi.Arguments([]abi.Argument{ 589 { 590 Name: "uint256", 591 Type: typU256, 592 }, 593 { 594 Name: "uint256", 595 Type: typU256, 596 }, 597 { 598 Name: "func_name", 599 Type: typString, 600 }, 601 }) 602 603 delayBig := big.NewInt(int64(delay.Seconds())) 604 buf, err := args.Pack([]interface{}{ 605 delayBig, 606 nonce.BigInt(), 607 "setWithdrawDelay", 608 }...) 609 if err != nil { 610 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 611 } 612 613 return packScheme(buf, e.bridgeAddr, e.chainID, e.v1) 614 } 615 616 func (e ERC20Logic) VerifyWithdrawDelay( 617 delay time.Duration, 618 nonce *num.Uint, 619 signatures string, 620 ) ([]string, error) { 621 msg, err := e.buildWithdrawDelayMessage( 622 delay, nonce, 623 ) 624 if err != nil { 625 return nil, err 626 } 627 628 addresses := []string{} 629 var hexCurrent string 630 signatures = signatures[2:] 631 for len(signatures) > 0 { 632 hexCurrent, signatures = signatures[0:130], signatures[130:] 633 current, err := hex.DecodeString(hexCurrent) 634 if err != nil { 635 return nil, fmt.Errorf("invalid signature format: %w", err) 636 } 637 638 address, err := crypto.RecoverEthereumAddress(msg, current) 639 if err != nil { 640 return nil, fmt.Errorf("error recovering ethereum address: %w", err) 641 } 642 643 addresses = append(addresses, address.Hex()) 644 } 645 646 return addresses, nil 647 } 648 649 func (e ERC20Logic) GlobalStop( 650 nonce *num.Uint, 651 ) (*SignaturePayload, error) { 652 typString, err := abi.NewType("string", "", nil) 653 if err != nil { 654 return nil, err 655 } 656 typU256, err := abi.NewType("uint256", "", nil) 657 if err != nil { 658 return nil, err 659 } 660 661 args := abi.Arguments([]abi.Argument{ 662 { 663 Name: "uint256", 664 Type: typU256, 665 }, 666 { 667 Name: "func_name", 668 Type: typString, 669 }, 670 }) 671 672 buf, err := args.Pack([]interface{}{ 673 nonce.BigInt(), 674 "globalStop", 675 }...) 676 if err != nil { 677 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 678 } 679 680 msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) 681 if err != nil { 682 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 683 } 684 685 return sign(e.signer, msg) 686 } 687 688 func (e ERC20Logic) GlobalResume( 689 nonce *num.Uint, 690 ) (*SignaturePayload, error) { 691 typString, err := abi.NewType("string", "", nil) 692 if err != nil { 693 return nil, err 694 } 695 typU256, err := abi.NewType("uint256", "", nil) 696 if err != nil { 697 return nil, err 698 } 699 700 args := abi.Arguments([]abi.Argument{ 701 { 702 Name: "uint256", 703 Type: typU256, 704 }, 705 { 706 Name: "func_name", 707 Type: typString, 708 }, 709 }) 710 711 buf, err := args.Pack([]interface{}{ 712 nonce.BigInt(), 713 "globalResume", 714 }...) 715 if err != nil { 716 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 717 } 718 719 msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) 720 if err != nil { 721 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 722 } 723 724 return sign(e.signer, msg) 725 } 726 727 func (e ERC20Logic) VerifyGlobalResume( 728 nonce *num.Uint, 729 signatures string, 730 ) ([]string, error) { 731 typString, err := abi.NewType("string", "", nil) 732 if err != nil { 733 return nil, err 734 } 735 typU256, err := abi.NewType("uint256", "", nil) 736 if err != nil { 737 return nil, err 738 } 739 740 args := abi.Arguments([]abi.Argument{ 741 { 742 Name: "uint256", 743 Type: typU256, 744 }, 745 { 746 Name: "func_name", 747 Type: typString, 748 }, 749 }) 750 751 buf, err := args.Pack([]interface{}{ 752 nonce.BigInt(), 753 "globalResume", 754 }...) 755 if err != nil { 756 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 757 } 758 759 msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) 760 if err != nil { 761 return nil, fmt.Errorf("couldn't pack abi message: %w", err) 762 } 763 764 addresses := []string{} 765 var hexCurrent string 766 signatures = signatures[2:] 767 for len(signatures) > 0 { 768 hexCurrent, signatures = signatures[0:130], signatures[130:] 769 current, err := hex.DecodeString(hexCurrent) 770 if err != nil { 771 return nil, fmt.Errorf("invalid signature format: %w", err) 772 } 773 774 address, err := crypto.RecoverEthereumAddress(msg, current) 775 if err != nil { 776 return nil, fmt.Errorf("error recovering ethereum address: %w", err) 777 } 778 779 addresses = append(addresses, address.Hex()) 780 } 781 782 return addresses, nil 783 }