diff --git a/Pnuttaste.code-workspace b/Pnuttaste.code-workspace new file mode 100644 index 0000000..876a149 --- /dev/null +++ b/Pnuttaste.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/SDK-mstr/.eslintrc.js b/SDK-mstr/.eslintrc.js new file mode 100644 index 0000000..f7bcee5 --- /dev/null +++ b/SDK-mstr/.eslintrc.js @@ -0,0 +1,180 @@ +module.exports = { + root: true, + ignorePatterns: ['**/*.js'], + plugins: ['import'], + overrides: [ + { + files: ['src/**/*.ts'], + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.json', + tsconfigRootDir: __dirname, + createDefaultProgram: true + }, + plugins: [ + '@typescript-eslint', + 'unused-imports', + 'simple-import-sort' + ], + extends: [ + 'airbnb-typescript/base', + 'plugin:prettier/recommended', + 'prettier' + ], + rules: { + 'import/prefer-default-export': 'off', + '@typescript-eslint/no-useless-constructor': 'off', + 'no-plusplus': 'off', + 'class-method-use-this': 'off', + 'no-underscore-dangle': 'off', + 'no-inferrable-types': 'off', + '@typescript-eslint/no-explicit-any': 2, + '@typescript-eslint/no-unused-vars': 'off', + 'simple-import-sort/imports': 'error', + 'simple-import-sort/exports': 'error', + 'unused-imports/no-unused-imports': 'error', + 'unused-imports/no-unused-vars': [ + 'error', + { + vars: 'all', + args: 'all', + ignoreRestSiblings: false, + argsIgnorePattern: '^_' + } + ], + '@typescript-eslint/no-inferrable-types': 'off', + 'class-methods-use-this': 'off', + complexity: ['error', 20], + eqeqeq: ['error', 'smart'], + '@typescript-eslint/naming-convention': [ + 'error', + { + selector: 'enumMember', + format: ['UPPER_CASE'] + } + ], + 'no-empty': ['error', { 'allowEmptyCatch': true }], + 'no-bitwise': 'off', + // Styling. + 'array-bracket-spacing': ['error', 'never'], + 'object-curly-spacing': ['error', 'always'], + indent: 'off', + 'comma-dangle': 'off', + '@typescript-eslint/comma-dangle': ['error', 'never'], + // Temporary rules. Remove after full refactoring. + 'import/no-extraneous-dependencies': 'off', + '@typescript-eslint/dot-notation': 'off', + 'no-restricted-globals': 'off', + '@typescript-eslint/no-empty-function': 'off', + 'no-param-reassign': 'off', + // Temporary rules. Remove as fast as it can be. + 'max-classes-per-file': 'off', + radix: ['warn', 'as-needed'], + 'no-prototype-builtins': 'off', + 'no-return-assign': 'off', + 'no-restricted-syntax': [ + 'error', + { + "selector": "TSEnumDeclaration", + "message": "Don't declare enums" + }, + 'LabeledStatement', + 'WithStatement' + ], + 'no-console': [ + 'warn', + { + allow: ['debug', 'error', 'info'] + } + ], + 'import/export': 0, + '@typescript-eslint/no-shadow': 'off', + '@typescript-eslint/return-await': 'off', + 'prefer-destructuring': 'off', + 'no-bitwise': 'off', + "prettier/prettier": [ + "error", + { + "endOfLine": "auto" + } + ] + } + }, + { + files: ['__tests__/**/*.ts'], + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.test.json', + tsconfigRootDir: __dirname, + createDefaultProgram: true + }, + plugins: [ + '@typescript-eslint', + 'unused-imports' + ], + extends: [ + 'airbnb-typescript/base', + 'plugin:prettier/recommended', + 'prettier' + ], + rules: { + 'import/prefer-default-export': 'off', + '@typescript-eslint/no-useless-constructor': 'off', + 'no-plusplus': 'off', + 'class-method-use-this': 'off', + 'no-underscore-dangle': 'off', + 'no-inferrable-types': 'off', + '@typescript-eslint/no-explicit-any': 2, + '@typescript-eslint/no-unused-vars': 'off', + 'unused-imports/no-unused-imports': 'error', + 'unused-imports/no-unused-vars': [ + 'error', + { + vars: 'all', + args: 'all', + ignoreRestSiblings: false, + argsIgnorePattern: '^_' + } + ], + '@typescript-eslint/no-inferrable-types': 'off', + 'class-methods-use-this': 'off', + complexity: ['error', 20], + eqeqeq: ['error', 'smart'], + '@typescript-eslint/naming-convention': [ + 'error', + { + selector: 'enumMember', + format: ['UPPER_CASE'] + } + ], + 'no-empty': ['error', { 'allowEmptyCatch': true }], + // Styling. + 'array-bracket-spacing': ['error', 'never'], + 'object-curly-spacing': ['error', 'always'], + indent: 'off', + 'comma-dangle': 'off', + '@typescript-eslint/comma-dangle': ['error', 'never'], + // Temporary rules. Remove after full refactoring. + 'import/no-extraneous-dependencies': 'off', + '@typescript-eslint/dot-notation': 'off', + 'no-restricted-globals': 'off', + '@typescript-eslint/no-empty-function': 'off', + 'no-param-reassign': 'off', + // Temporary rules. Remove as fast as it can be. + 'max-classes-per-file': 'off', + radix: ['warn', 'as-needed'], + 'no-prototype-builtins': 'off', + 'no-return-assign': 'off', + 'no-restricted-syntax': [ + 'error', + 'LabeledStatement', + 'WithStatement' + ], + 'no-console': 'off', + 'import/export': 0, + '@typescript-eslint/no-shadow': 'off', + '@typescript-eslint/return-await': 'off' + } + } + ] +}; diff --git a/SDK-mstr/.github/release-drafter.yml b/SDK-mstr/.github/release-drafter.yml new file mode 100644 index 0000000..1b184d9 --- /dev/null +++ b/SDK-mstr/.github/release-drafter.yml @@ -0,0 +1,2 @@ +template: | + ## Change log diff --git a/SDK-mstr/.github/workflows/cd-master.yml b/SDK-mstr/.github/workflows/cd-master.yml new file mode 100644 index 0000000..8536fc0 --- /dev/null +++ b/SDK-mstr/.github/workflows/cd-master.yml @@ -0,0 +1,45 @@ +name: Publish package and draft release + +on: + workflow_dispatch: + push: + branches: + - master + +permissions: + contents: read + +jobs: + publish: + name: Publish package to NPM + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Read .nvmrc + run: echo ::set-output name=NVMRC::$(cat .nvmrc) + id: nvm + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '${{ steps.nvm.outputs.NVMRC }}' + registry-url: 'https://registry.npmjs.org' + + - name: Set up yarn + run: npm install --global yarn + + - name: Set up dependencies + run: yarn + + - name: Compile lib + run: yarn compile + + - name: Build bundle + run: yarn build + + - name: Publish package + run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/SDK-mstr/.github/workflows/ci-docs.yml b/SDK-mstr/.github/workflows/ci-docs.yml new file mode 100644 index 0000000..f29393f --- /dev/null +++ b/SDK-mstr/.github/workflows/ci-docs.yml @@ -0,0 +1,38 @@ +name: Documentation generation + +on: + push: + branches: + - master + +jobs: + lint: + name: Generate docs and push to github pages + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Read .nvmrc + run: echo ::set-output name=NVMRC::$(cat .nvmrc) + id: nvm + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '${{ steps.nvm.outputs.NVMRC }}' + + - name: Set up yarn + run: npm install --global yarn + + - name: Set up dependencies + run: yarn + + - name: Generate docs + run: yarn docs + + - name: Deploy Github Page + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs diff --git a/SDK-mstr/.github/workflows/ci-master.yml b/SDK-mstr/.github/workflows/ci-master.yml new file mode 100644 index 0000000..28fb667 --- /dev/null +++ b/SDK-mstr/.github/workflows/ci-master.yml @@ -0,0 +1,41 @@ +name: Tests and build + +on: + pull_request: + branches: + - master + +jobs: + lint: + name: Check code base before merge to master + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Read .nvmrc + run: echo ::set-output name=NVMRC::$(cat .nvmrc) + id: nvm + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '${{ steps.nvm.outputs.NVMRC }}' + + - name: Set up yarn + run: npm install --global yarn + + - name: Set up dependencies + run: yarn + + - name: Run lint + run: yarn lint + + - name: Run tests + run: yarn test:unit + + - name: Compile lib + run: yarn compile + + - name: Build bundle + run: yarn build diff --git a/SDK-mstr/.gitignore b/SDK-mstr/.gitignore new file mode 100644 index 0000000..91dd9fa --- /dev/null +++ b/SDK-mstr/.gitignore @@ -0,0 +1,16 @@ +.idea +/node_modules/ +/.env +/yarn-error.log +/.data/ +/coverage/ +/__tests__/env.js +/lib/ +/dist/ +/cache/ +/scripts/node.log +stats.json +.DS_Store +/docs/ +/src/index.ts +.eslintignore \ No newline at end of file diff --git a/SDK-mstr/.nvmrc b/SDK-mstr/.nvmrc new file mode 100644 index 0000000..7ea6a59 --- /dev/null +++ b/SDK-mstr/.nvmrc @@ -0,0 +1 @@ +v20.11.0 diff --git a/SDK-mstr/.prettierrc.json b/SDK-mstr/.prettierrc.json new file mode 100644 index 0000000..127a7b6 --- /dev/null +++ b/SDK-mstr/.prettierrc.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json.schemastore.org/prettierrc", + "arrowParens": "avoid", + "bracketSpacing": true, + "printWidth": 100, + "proseWrap": "always", + "quoteProps": "as-needed", + "semi": true, + "singleQuote": true, + "tabWidth": 4, + "trailingComma": "none", + "useTabs": false, + "endOfLine": "auto" +} diff --git a/SDK-mstr/LICENSE b/SDK-mstr/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/SDK-mstr/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/SDK-mstr/README.md b/SDK-mstr/README.md new file mode 100644 index 0000000..73d6554 --- /dev/null +++ b/SDK-mstr/README.md @@ -0,0 +1,361 @@ +# Rubic SDK + +## API Documentation + +[Latest API Documentation](https://cryptorubic.github.io/rubic-sdk) + +## Installation +### Installation with cdn +```html + +``` + +### Installation with npm and webpack (React, ...) +1. `npm install rubic-sdk` + + +ℹ️️ Skip the rest of the steps if your have already installed [web3](https://github.com/ChainSafe/web3.js) in your project. + + +2. `npm install --save-dev http-browserify https-browserify stream-browserify crypto-browserify` +3. modify webpack.config.js. If you use create-react-app, run `npm run eject` to extract config + 1. add to `plugins` + ```javascript + new webpack.ProvidePlugin({ + Buffer: ['buffer', 'Buffer'], + process: 'process/browser' + }) + ``` + 2. add `resolve.fallback` + ```json + "fallback": { + "fs": false, + "constants": false, + "querystring": false, + "url": false, + "path": false, + "os": false, + "http": require.resolve("http-browserify"), + "https": require.resolve("https-browserify"), + "zlib": false, + "stream": require.resolve("stream-browserify"), + "crypto": require.resolve("crypto-browserify"), + "zlib": require.resolve('browserify-zlib') + } + ``` + +### Installation with npm for Angular +1. `npm install rubic-sdk` + + +ℹ️️ Skip the rest of the steps if your have already installed [web3](https://github.com/ChainSafe/web3.js) in your project. + + +2. `npm install --save-dev stream-browserify assert https-browserify os-browserify stream-http crypto-browserify process buffer` +3. Modify tsconfig.json + ```json + { + "compilerOptions": { + ... + "paths" : { + ... + "stream": ["./node_modules/stream-browserify"], + "assert": ["./node_modules/assert"], + "https": ["./node_modules/https-browserify"], + "os": ["./node_modules/os-browserify"], + "http": ["./node_modules/stream-http"], + "crypto": ["./node_modules/crypto-browserify"] + } + } + ``` +4. Modify polyfills.ts + ```typescript + import Process = NodeJS.Process; + + export interface AppWindow extends Window { + process: Process; + Buffer: Buffer; + global?: unknown; + } + + (window as AppWindow).global = window; + (window as AppWindow).process = window.process || require('process'); + (window as AppWindow).Buffer = (window as any).Buffer || require('buffer').Buffer; + ``` + +## Trades usage + +### Get started after cdn installation +```html + +``` + + +### Get started after npm installation +1. Create configuration + ```typescript + import { Configuration, BLOCKCHAIN_NAME } from 'rubic-sdk'; + + // you have to declare rpc links only for networks you will use + export const configuration: Configuration = { + rpcProviders: { + [BLOCKCHAIN_NAME.ETHEREUM]: { + rpcList: ['', '', ...] + }, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: { + rpcList: [''] + }, + ... + [BLOCKCHAIN_NAME.TRON]: { + rpcList: [ + { + fullHost: '', + headers: { "TRON-PRO-API-KEY": 'your api key' } + } + ] + } + }, + // if you are whitelisted integrator, provide your wallet address here + providerAddress: { + [CHAIN_TYPE.EVM]: { + crossChain: '0x0000000000000000000000000000000000000000', // Address for cross chain fee + onChain: '0x0000000000000000000000000000000000000000' // Address for on chain fee + } + } + } + ``` +2. Create sdk instance + ```typescript + import { SDK } from 'rubic-sdk'; + + const sdk = await SDK.createSDK(configuration); + ``` + +3. Use sdk instance for trade calculation + ```typescript + import { BLOCKCHAIN_NAME, TradeType, OnChainTrade, EvmOnChainTrade } from 'rubic-sdk'; + + const blockchain = BLOCKCHAIN_NAME.ETHEREUM; + const fromTokenAddress = '0x0000000000000000000000000000000000000000'; // ETH + const fromAmount = 1; + const toTokenAddress = '0xdac17f958d2ee523a2206206994597c13d831ec7'; // USDT + + const trades = await sdk.onChainManager.calculateTrade( + { blockchain, address: fromTokenAddress }, + fromAmount, + toTokenAddress + ); + const bestTrade = trades[0]; + + trades.forEach(trade => { + const tradeType: TradeType = trade.type; + console.log(`trade type: ${tradeType}`); + + if (trade instanceof OnChainTrade) { + console.log(`to amount: ${trade.to.tokenAmount.toFormat(3)}`); + } else { + console.log(`error: ${trade.error}`); + } + + // explore trades info + if (trade instanceof EvmOnChainTrade) { + console.log(`Gas fee: ${bestTrade.gasFeeInfo}`); + } + ... + }); + ``` + +4. When user connects wallet (e.g. MetaMask) you should change configuration to use trade `swap` method.
+ **⚠️ Recalculate trades after this**. + ```typescript + import { WalletProvider, CHAIN_TYPE, Configuration } from 'rubic-sdk'; + + const walletProvider: WalletProvider = { + [CHAIN_TYPE.EVM]: { + address: '0x123...', // user wallet address + core: window.ethereum + }, + [CHAIN_TYPE.TRON]: { + address: 'T123...', // user wallet address + core: window.tronLink.tronWeb // or window.tronWeb + } + }; + + // initial configuration example + const configuration: Configuration = { + ... + walletProvider + } + const sdk = await SDK.createSDK(configuration); + + // after user's wallet address changed + // Example #1: + sdk.updateWalletProvider(walletProvider); + + // Example #2: + sdk.updateWalletAddress(CHAIN_TYPE.EVM, address); + + ``` + +5. Now you can use `swap` method of trade instance. Approve transaction will be sent automatically if needed. + ```typescript + const onConfirm = (hash: string) => console.log(hash); + + // check that trade is defined + сonst bestTrade = trades[0]; + const receipt = await bestTrade.swap({ onConfirm }); + ``` + +### Get started with cross-chain swaps +Steps 1. and 2. are the same. You can use single sdk instance for on-chain trades and cross-chain swaps calculations. + +3. Use sdk instance for trade calculation + ```typescript + import { BLOCKCHAIN_NAME } from 'rubic-sdk'; + + const fromBlockchain = BLOCKCHAIN_NAME.ETHEREUM; + const fromTokenAddress = '0x0000000000000000000000000000000000000000'; // ETH + const fromAmount = 1; + const toBlockchain = BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN; + const toTokenAddress = '0xe9e7cea3dedca5984780bafc599bd69add087d56'; // BUSD + + const wrappedTrades = await sdk.crossChainManager.calculateTrade( + { blockchain: fromBlockchain, address: fromTokenAddress }, + fromAmount, + { blockchain: toBlockchain, address: toTokenAddress } + ); + + wrappedTrades.forEach(wrappedTrade => { + const tradeType: TradeType = wrappedTrade.type; + console.log(`trade type: ${tradeType}`); + + if (wrappedTrade.error) { + console.log(`error: ${wrappedTrade.error}`); + } else { + const trade = wrappedTrade.trade!; + console.log(`to amount: ${trade.to.tokenAmount.toFormat(3)}`); + + // explore trades info + if (trade instanceof EvmCrossChainTrade) { + console.log(trade.gasData) + } + ... + } + }) + ``` + +4. Same as in on-chain. + +5. Now you can use `swap` method of trade instance. Approve transaction will be sent automatically if needed. + ```typescript + const onConfirm = (hash: string) => console.log(hash); + + // check, that trade is defined + const bestTrade = wrappedTrades[0].trade; + const receipt = await bestTrade.swap({ onConfirm }); + ``` + +## Token classes + +You can use specific sdk `Token` classes to work with tokens. + +### Token + +```typescript +import { BLOCKCHAIN_NAME } from 'rubic-sdk'; + +const token: Token = await Token.createToken({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM, + address: '0xdac17f958d2ee523a2206206994597c13d831ec7' +}); + +console.log(token.symbol); // USDT +console.log(token.name); // Tether USD +console.log(token.decimals); // 6 +``` + +You can also use constructor directly. +```typescript +import { BLOCKCHAIN_NAME } from 'rubic-sdk'; + +const token = new Token({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM, + address: '0xdac17f958d2ee523a2206206994597c13d831ec7', + name: 'USD Coin', + symbol: 'USDC', + decimals: 6 +}) +``` + +### PriceToken + +```typescript +import { BLOCKCHAIN_NAME } from 'rubic-sdk'; + +const token: PriceToken = await PriceToken.createToken({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM, + address: '0xdac17f958d2ee523a2206206994597c13d831ec7' +}); + +console.log(token.price); +``` + +### PriceTokenAmount + +```typescript +import { BLOCKCHAIN_NAME } from 'rubic-sdk'; + +const token: PriceTokenAmount = await PriceTokenAmount.createToken({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM, + address: '0xdac17f958d2ee523a2206206994597c13d831ec7', + tokenAmount: new BigNumber(1) +}); + +console.log(token.weiAmount); +``` diff --git a/SDK-mstr/TODO.md b/SDK-mstr/TODO.md new file mode 100644 index 0000000..4a06d1d --- /dev/null +++ b/SDK-mstr/TODO.md @@ -0,0 +1,9 @@ +# Rubic sdk todo + +### Todo + +- [ ] Tests + - [ ] Make ethereum test env + - [ ] Write unit tests + - [ ] Write API tests +- [ ] Add CONTRIBUTING.md diff --git a/SDK-mstr/__tests__/api-tests/cross-chain-trades.ts b/SDK-mstr/__tests__/api-tests/cross-chain-trades.ts new file mode 100644 index 0000000..242fe45 --- /dev/null +++ b/SDK-mstr/__tests__/api-tests/cross-chain-trades.ts @@ -0,0 +1,35 @@ +import { minimalConfiguration } from '../utils/configuration'; +import { SDK } from 'src/core/sdk/sdk'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const crossChainApiSpec = () => + describe('Cross chain trades module tests', () => { + let sdk: SDK; + beforeEach(async () => { + sdk = await SDK.createSDK(minimalConfiguration); + }); + + test('Should create InstantTradesManager instance', async () => { + expect(typeof sdk.crossChainManager).toBe('object'); + }); + + test('Should calculate MATIC to USDT trade', async () => { + const fromToken = { + address: '0x0000000000000000000000000000000000000000', + blockchain: BLOCKCHAIN_NAME.POLYGON + }; + const toToken = { + address: '0xdac17f958d2ee523a2206206994597c13d831ec7', + blockchain: BLOCKCHAIN_NAME.ETHEREUM + }; + const fromAmount = 100; + + const trade = await sdk.crossChainManager.calculateTrade( + fromToken, + fromAmount.toString(), + toToken + ); + + expect(!!trade).toBeTruthy(); + }, 400_000); + }); diff --git a/SDK-mstr/__tests__/api-tests/instant-trades.ts b/SDK-mstr/__tests__/api-tests/instant-trades.ts new file mode 100644 index 0000000..2557e6b --- /dev/null +++ b/SDK-mstr/__tests__/api-tests/instant-trades.ts @@ -0,0 +1,28 @@ +import { minimalConfiguration } from '../utils/configuration'; +import { SDK } from '../../src/core/sdk/sdk'; +import { BLOCKCHAIN_NAME } from '../../src/core/blockchain/models/blockchain-name'; + +export const instantTradesApiSpec = () => + describe('Instant trades module tests', () => { + let sdk: SDK; + beforeEach(async () => { + sdk = await SDK.createSDK(minimalConfiguration); + }); + + test('Should create InstantTradesManager instance', async () => { + expect(typeof sdk.onChainManager).toBe('object'); + }); + + test('Should calculate ETH to USDT trade', async () => { + const fromToken = { + address: '0x0000000000000000000000000000000000000000', + blockchain: BLOCKCHAIN_NAME.ETHEREUM + }; + const toToken = '0xdac17f958d2ee523a2206206994597c13d831ec7'; + const fromAmount = 1; + + const trades = await sdk.onChainManager.calculateTrade(fromToken, fromAmount, toToken); + expect(Array.isArray(trades)).toBeTruthy(); + expect(trades.length).not.toBe(0); + }, 400_000); + }); diff --git a/SDK-mstr/__tests__/test-runners/avalanche-test-runner.spec.ts b/SDK-mstr/__tests__/test-runners/avalanche-test-runner.spec.ts new file mode 100644 index 0000000..44277cf --- /dev/null +++ b/SDK-mstr/__tests__/test-runners/avalanche-test-runner.spec.ts @@ -0,0 +1,9 @@ +import { joeAvalancheProviderSpec } from '__tests__/unit-tests/features/swap/dexes/avalanche/joe/joe'; +import { pangolinAvalancheProviderSpec } from '__tests__/unit-tests/features/swap/dexes/avalanche/pangolin/pangolin'; +import { sushiSwapAvalancheProviderSpec } from '__tests__/unit-tests/features/swap/dexes/avalanche/sushi-swap/sushi-swap'; + +describe('Avalanche tests', () => { + joeAvalancheProviderSpec(); + pangolinAvalancheProviderSpec(); + sushiSwapAvalancheProviderSpec(); +}); diff --git a/SDK-mstr/__tests__/test-runners/bsc-test-runner.spec.ts b/SDK-mstr/__tests__/test-runners/bsc-test-runner.spec.ts new file mode 100644 index 0000000..e0039be --- /dev/null +++ b/SDK-mstr/__tests__/test-runners/bsc-test-runner.spec.ts @@ -0,0 +1,9 @@ +import { pancakeSwapBscProviderSpec } from '__tests__/unit-tests/features/swap/dexes/bsc/pancake-swap/pancake-swap'; +import { sushiSwapBscProviderSpec } from '__tests__/unit-tests/features/swap/dexes/bsc/sushi-swap/sushi-swap'; +import { oneinchBscProviderSpec } from '__tests__/unit-tests/features/swap/dexes/bsc/one-inch/one-inch'; + +describe('BSC tests', () => { + pancakeSwapBscProviderSpec(); + sushiSwapBscProviderSpec(); + oneinchBscProviderSpec(); +}); diff --git a/SDK-mstr/__tests__/test-runners/eth-test-runner.spec.ts b/SDK-mstr/__tests__/test-runners/eth-test-runner.spec.ts new file mode 100644 index 0000000..6d0b953 --- /dev/null +++ b/SDK-mstr/__tests__/test-runners/eth-test-runner.spec.ts @@ -0,0 +1,24 @@ +import { instantTradesApiSpec } from '__tests__/api-tests/instant-trades'; +import { crossChainApiSpec } from '__tests__/api-tests/cross-chain-trades'; +import { uniswapV2ProviderSpec } from '__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v2/uni-swap-v2-provider'; +import { uniswapV3EthTradeSpec } from '__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v3/uni-swap-v3-ethereum-trade'; +import { uniswapV3EthProviderSpec } from '__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v3/uni-swap-v3-ethereum-provider'; +import { uniswapV2TradeSpec } from '__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v2/uni-swap-v2-trade'; +import { oneinchProviderEthereumSpec } from '__tests__/unit-tests/features/swap/dexes/ethereum/one-inch/one-inch'; +import { sushiSwapProviderEthereumSpec } from '__tests__/unit-tests/features/swap/dexes/ethereum/sushi-swap/sushi-swap'; +import { zrxProviderEthereumSpec } from '__tests__/unit-tests/features/swap/dexes/ethereum/zrx/zrx'; + +describe('Eth tests', () => { + instantTradesApiSpec(); + crossChainApiSpec(); + + uniswapV2ProviderSpec(); + uniswapV2TradeSpec(); + + uniswapV3EthProviderSpec(); + uniswapV3EthTradeSpec(); + + oneinchProviderEthereumSpec(); + sushiSwapProviderEthereumSpec(); + zrxProviderEthereumSpec(); +}); diff --git a/SDK-mstr/__tests__/test-runners/fantom-test-runner.spec.ts b/SDK-mstr/__tests__/test-runners/fantom-test-runner.spec.ts new file mode 100644 index 0000000..ea22214 --- /dev/null +++ b/SDK-mstr/__tests__/test-runners/fantom-test-runner.spec.ts @@ -0,0 +1,9 @@ +import { spiritSwapFantomProviderSpec } from '__tests__/unit-tests/features/swap/dexes/fantom/spirit-swap/spirit-swap'; +import { spookySwapFantomProviderSpec } from '__tests__/unit-tests/features/swap/dexes/fantom/spooky-swap/spooky-swap'; +import { sushiSwapFantomProviderSpec } from '__tests__/unit-tests/features/swap/dexes/fantom/sushi-swap/sushi-swap'; + +describe('Fantom tests', () => { + spiritSwapFantomProviderSpec(); + spookySwapFantomProviderSpec(); + sushiSwapFantomProviderSpec(); +}); diff --git a/SDK-mstr/__tests__/test-runners/harmony-test-runner.spec.ts b/SDK-mstr/__tests__/test-runners/harmony-test-runner.spec.ts new file mode 100644 index 0000000..20606fa --- /dev/null +++ b/SDK-mstr/__tests__/test-runners/harmony-test-runner.spec.ts @@ -0,0 +1,5 @@ +import { sushiSwapHarmonyProviderSpec } from '__tests__/unit-tests/features/swap/dexes/harmony/sushi-swap'; + +describe('Harmony tests', () => { + sushiSwapHarmonyProviderSpec(); +}); diff --git a/SDK-mstr/__tests__/test-runners/moonriver-test-runner.spec.ts b/SDK-mstr/__tests__/test-runners/moonriver-test-runner.spec.ts new file mode 100644 index 0000000..78f2d9c --- /dev/null +++ b/SDK-mstr/__tests__/test-runners/moonriver-test-runner.spec.ts @@ -0,0 +1,7 @@ +import { solarbeamMoonriverProviderSpec } from '__tests__/unit-tests/features/swap/dexes/moonriver/solarbeam/solarbeam'; +import { sushiSwapMoonriverProviderSpec } from '__tests__/unit-tests/features/swap/dexes/moonriver/sushi-swap/sushi-swap'; + +describe('Moonriver tests', () => { + solarbeamMoonriverProviderSpec(); + sushiSwapMoonriverProviderSpec(); +}); diff --git a/SDK-mstr/__tests__/test-runners/polygon-test-runner.spec.ts b/SDK-mstr/__tests__/test-runners/polygon-test-runner.spec.ts new file mode 100644 index 0000000..41193ab --- /dev/null +++ b/SDK-mstr/__tests__/test-runners/polygon-test-runner.spec.ts @@ -0,0 +1,19 @@ +import { uniswapV3PolygonTradeSpec } from '__tests__/unit-tests/features/swap/dexes/polygon/uni-swap-v3/uni-swap-v3-polygon-trade'; +import { uniswapV3PolygonProviderSpec } from '__tests__/unit-tests/features/swap/dexes/polygon/uni-swap-v3/uni-swap-v3-polygon-provider'; +import { sushiSwapPolygonProviderSpec } from '__tests__/unit-tests/features/swap/dexes/polygon/sushi-swap/sushi-swap'; +import { oneinchPolygonProviderSpec } from '__tests__/unit-tests/features/swap/dexes/polygon/one-inch/one-inch'; +import { algebraPolygonProviderSpec } from '__tests__/unit-tests/features/swap/dexes/polygon/algebra/algebra-provider'; +import { algebraPolygonTradeSpec } from '__tests__/unit-tests/features/swap/dexes/polygon/algebra/algebra-trade'; + +describe('Polygon tests', () => { + uniswapV3PolygonProviderSpec(); + uniswapV3PolygonTradeSpec(); + + algebraPolygonProviderSpec(); + algebraPolygonTradeSpec(); + + // quickSwapPolygonProviderSpec(); + sushiSwapPolygonProviderSpec(); + + oneinchPolygonProviderSpec(); +}); diff --git a/SDK-mstr/__tests__/unit-tests/common/decorators/cache.decorator.spec.ts b/SDK-mstr/__tests__/unit-tests/common/decorators/cache.decorator.spec.ts new file mode 100644 index 0000000..0baa874 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/common/decorators/cache.decorator.spec.ts @@ -0,0 +1,393 @@ +import JestMockPromise from 'jest-mock-promise'; +import { Cache } from 'src/common/utils/decorators'; +import fn = jest.fn; + +describe('Cache decorator tests', () => { + test('Cache must be used when method is called second time.', () => { + const spyFn = fn(); + const returnedValue = 1; + class CacheTestClass { + @Cache + public zeroParametersCacheableMethod() { + spyFn(); + return returnedValue; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallResult = testClassInstance.zeroParametersCacheableMethod(); + const secondCallResult = testClassInstance.zeroParametersCacheableMethod(); + + expect(spyFn.mock.calls.length).toBe(1); + expect(firstCallResult).toBe(returnedValue); + expect(secondCallResult).toBe(returnedValue); + }); + + test('Cache must not be used when method is called second time with other parameter.', () => { + const spyFn = fn(); + class CacheTestClass { + @Cache + public oneParametersCacheableMethod(parameter: unknown) { + spyFn(); + return parameter; + } + } + + const testClassInstance = new CacheTestClass(); + testClassInstance.oneParametersCacheableMethod(0); + testClassInstance.oneParametersCacheableMethod(1); + + expect(spyFn.mock.calls.length).toBe(2); + }); + + test('Cache must be used when method is called second time with same parameter. Stored value must be correct.', () => { + const spyFn = fn(); + const additionSeed = 1; + const firstCallParameter = 2; + const secondCallParameter = 3; + class CacheTestClass { + @Cache + public oneParametersCacheableMethod(parameter: number) { + spyFn(); + return additionSeed + parameter; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallResult = testClassInstance.oneParametersCacheableMethod(firstCallParameter); + const secondCallResult = + testClassInstance.oneParametersCacheableMethod(secondCallParameter); + const spyCallsAfterSecondTimeMethodCall = spyFn.mock.calls.length; + const thirdCallResult = testClassInstance.oneParametersCacheableMethod(firstCallParameter); + const spyCallsAfterThirdTimeMethodCall = spyFn.mock.calls.length; + + expect(spyCallsAfterSecondTimeMethodCall).toBe(2); + expect(spyCallsAfterThirdTimeMethodCall).toBe(2); + expect(firstCallResult).toBe(additionSeed + firstCallParameter); + expect(secondCallResult).toBe(additionSeed + secondCallParameter); + expect(thirdCallResult).toBe(firstCallResult); + }); + + test('Cache decorator must works with async functions.', async () => { + const spyFn = fn(); + const promise = new JestMockPromise(); + + const returnedValue = 1; + class CacheTestClass { + @Cache + public async asyncCacheableMethod() { + spyFn(); + await promise; + return returnedValue; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallPromise = testClassInstance.asyncCacheableMethod(); + const secondCallPromise = testClassInstance.asyncCacheableMethod(); + promise.resolve(); + + const firstCallResult = await firstCallPromise; + const secondCallResult = await secondCallPromise; + + expect(firstCallResult).toBe(returnedValue); + expect(secondCallResult).toBe(returnedValue); + expect(spyFn.mock.calls.length).toBe(1); + }); + + test('Cache decorator must works with async function which resolves before second call.', async () => { + const spyFn = fn(); + const promise = new JestMockPromise(); + + const returnedValue = 1; + class CacheTestClass { + @Cache + public async asyncCacheableMethod() { + spyFn(); + await promise; + return returnedValue; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallPromise = testClassInstance.asyncCacheableMethod(); + promise.resolve(); + const secondCallPromise = testClassInstance.asyncCacheableMethod(); + + const firstCallResult = await firstCallPromise; + const secondCallResult = await secondCallPromise; + + expect(firstCallResult).toBe(returnedValue); + expect(secondCallResult).toBe(returnedValue); + expect(spyFn.mock.calls.length).toBe(1); + }); + + test('Cache must be used when async method is called second time with same parameter. Stored value must be correct.', async () => { + const spyFn = fn(); + const promise = new JestMockPromise(); + const additionSeed = 1; + const firstCallParameter = 2; + const secondCallParameter = 3; + class CacheTestClass { + @Cache + public async oneParametersAsyncCacheableMethod(parameter: number) { + spyFn(); + const additionSeedValue = await promise; + return additionSeedValue + parameter; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallPromise = + testClassInstance.oneParametersAsyncCacheableMethod(firstCallParameter); + const secondCallPromise = + testClassInstance.oneParametersAsyncCacheableMethod(secondCallParameter); + const spyCallsAfterSecondTimeMethodCall = spyFn.mock.calls.length; + const thirdCallPromise = + testClassInstance.oneParametersAsyncCacheableMethod(firstCallParameter); + const spyCallsAfterThirdTimeMethodCall = spyFn.mock.calls.length; + promise.resolve(additionSeed); + + const firstCallResult = await firstCallPromise; + const secondCallResult = await secondCallPromise; + const thirdCallResult = await thirdCallPromise; + + expect(spyCallsAfterSecondTimeMethodCall).toBe(2); + expect(spyCallsAfterThirdTimeMethodCall).toBe(2); + expect(firstCallResult).toBe(additionSeed + firstCallParameter); + expect(secondCallResult).toBe(additionSeed + secondCallParameter); + expect(thirdCallResult).toBe(firstCallResult); + }); + + test('Cache decorator must works with getters.', () => { + const spyFn = fn(); + const returnedValue = 1; + class CacheTestClass { + @Cache + public get cacheableGetter(): number { + spyFn(); + return returnedValue; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallResult = testClassInstance.cacheableGetter; + const secondCallResult = testClassInstance.cacheableGetter; + + expect(spyFn.mock.calls.length).toBe(1); + expect(firstCallResult).toBe(returnedValue); + expect(secondCallResult).toBe(returnedValue); + }); + + test('Cached value must be used before maxAge time is not up. ', () => { + jest.useFakeTimers().setSystemTime(0); + const spyFn = fn(); + const returnedValue = 1; + const maxAge = 10; + class CacheTestClass { + @Cache({ maxAge }) + public cacheableMethod(): number { + spyFn(); + return returnedValue; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallResult = testClassInstance.cacheableMethod(); + jest.useFakeTimers().setSystemTime(maxAge - 1); + const secondCallResult = testClassInstance.cacheableMethod(); + + expect(spyFn.mock.calls.length).toBe(1); + expect(firstCallResult).toBe(returnedValue); + expect(secondCallResult).toBe(returnedValue); + }); + + test('Cached value must not be used after maxAge time is up.', () => { + jest.useFakeTimers().setSystemTime(0); + const spyFn = fn(); + const returnedValue = 1; + const maxAge = 10; + class CacheTestClass { + @Cache({ maxAge }) + public cacheableMethod(): number { + spyFn(); + return returnedValue; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallResult = testClassInstance.cacheableMethod(); + jest.useFakeTimers().setSystemTime(maxAge + 1); + const secondCallResult = testClassInstance.cacheableMethod(); + + expect(spyFn.mock.calls.length).toBe(2); + expect(firstCallResult).toBe(returnedValue); + expect(secondCallResult).toBe(returnedValue); + }); + + test('Cached value must be used in async function before maxAge time is up but even if first promise has not resolved yet.', async () => { + jest.useFakeTimers().setSystemTime(0); + const spyFn = fn(); + const promise = new JestMockPromise(); + const returnedValue = 1; + const maxAge = 10; + class CacheTestClass { + @Cache({ maxAge: 10 }) + public async cacheableMethod(): Promise { + spyFn(); + await promise; + return returnedValue; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallPromise = testClassInstance.cacheableMethod(); + jest.useFakeTimers().setSystemTime(maxAge - 1); + const secondCallPromise = testClassInstance.cacheableMethod(); + promise.resolve(); + const firstCallResult = await firstCallPromise; + const secondCallResult = await secondCallPromise; + + expect(spyFn.mock.calls.length).toBe(1); + expect(firstCallResult).toBe(returnedValue); + expect(secondCallResult).toBe(returnedValue); + }); + + test('Cached value must not be used in async function after maxAge time is up but even if first promise has not resolved yet.', async () => { + jest.useFakeTimers().setSystemTime(0); + const spyFn = fn(); + const promise = new JestMockPromise(); + const returnedValue = 1; + const maxAge = 10; + class CacheTestClass { + @Cache({ maxAge: 10 }) + public async cacheableMethod(): Promise { + spyFn(); + await promise; + return returnedValue; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallPromise = testClassInstance.cacheableMethod(); + jest.useFakeTimers().setSystemTime(maxAge + 1); + const secondCallPromise = testClassInstance.cacheableMethod(); + promise.resolve(); + const firstCallResult = await firstCallPromise; + const secondCallResult = await secondCallPromise; + + expect(spyFn.mock.calls.length).toBe(2); + expect(firstCallResult).toBe(returnedValue); + expect(secondCallResult).toBe(returnedValue); + }); + + test('Conditional cache must not be saved if flag is passed.', () => { + const spyFn = fn(); + const returnedValue = 1; + class CacheTestClass { + @Cache({ conditionalCache: true }) + public conditionalCacheableMethod() { + spyFn(); + return { + notSave: true, + value: returnedValue + }; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallResult = testClassInstance.conditionalCacheableMethod(); + const secondCallResult = testClassInstance.conditionalCacheableMethod(); + + expect(spyFn.mock.calls.length).toBe(2); + expect(firstCallResult).toBe(returnedValue); + expect(secondCallResult).toBe(returnedValue); + }); + + test('Conditional cache must be saved if flag is not passed.', () => { + const spyFn = fn(); + const returnedValue = 1; + class CacheTestClass { + @Cache({ conditionalCache: true }) + public conditionalCacheableMethod() { + spyFn(); + return { + notSave: false, + value: returnedValue + }; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallResult = testClassInstance.conditionalCacheableMethod(); + const secondCallResult = testClassInstance.conditionalCacheableMethod(); + + expect(spyFn.mock.calls.length).toBe(1); + expect(firstCallResult).toBe(returnedValue); + expect(secondCallResult).toBe(returnedValue); + }); + + test('Conditional cache must be saved if flag is not passed in async method.', async () => { + const spyFn = fn(); + const promise = new JestMockPromise(); + const returnedValue = 1; + class CacheTestClass { + @Cache({ conditionalCache: true }) + public async conditionalAsyncCacheableMethod() { + spyFn(); + await promise; + return { + notSave: false, + value: returnedValue + }; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallPromise = testClassInstance.conditionalAsyncCacheableMethod(); + promise.resolve(); + const secondCallPromise = testClassInstance.conditionalAsyncCacheableMethod(); + const firstCallResult = await firstCallPromise; + const secondCallResult = await secondCallPromise; + + expect(spyFn.mock.calls.length).toBe(1); + expect(firstCallResult).toBe(returnedValue); + expect(secondCallResult).toBe(returnedValue); + }); + + test('Conditional cache must be saved before async method resolves, but must not be saved after promise resolving if fleg is passed.', async () => { + const spyFn = fn(); + const promise = new JestMockPromise(); + const returnedValue = 1; + class CacheTestClass { + @Cache({ conditionalCache: true }) + public async conditionalAsyncCacheableMethod() { + spyFn(); + await promise; + return { + notSave: true, + value: returnedValue + }; + } + } + + const testClassInstance = new CacheTestClass(); + const firstCallPromise = testClassInstance.conditionalAsyncCacheableMethod(); + const secondCallPromise = testClassInstance.conditionalAsyncCacheableMethod(); + const spyCallsNumberAfterSecondMethodCall = spyFn.mock.calls.length; + promise.resolve(); + const firstCallResult = await firstCallPromise; + const secondCallResult = await secondCallPromise; + + const thirdCallPromise = testClassInstance.conditionalAsyncCacheableMethod(); + const spyCallsNumberAfterThirdMethodCall = spyFn.mock.calls.length; + promise.resolve(); + const thirdCallResult = await thirdCallPromise; + + expect(spyCallsNumberAfterSecondMethodCall).toBe(1); + expect(spyCallsNumberAfterThirdMethodCall).toBe(2); + expect(firstCallResult).toBe(returnedValue); + expect(secondCallResult).toBe(returnedValue); + expect(thirdCallResult).toBe(returnedValue); + }); +}); diff --git a/SDK-mstr/__tests__/unit-tests/common/utils/p-timeout.spec.ts b/SDK-mstr/__tests__/unit-tests/common/utils/p-timeout.spec.ts new file mode 100644 index 0000000..80520bf --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/common/utils/p-timeout.spec.ts @@ -0,0 +1,137 @@ +import { TimeoutError } from 'src/common/errors'; +import CancelablePromise from 'cancelable-promise'; +import pTimeout from 'src/common/utils/p-timeout'; +import delay from 'delay'; +import fn = jest.fn; + +describe('p-timeout tests', () => { + const expected = Symbol('expected'); + const expectedError = new Error('expectedError'); + + const smallTimeout = 50; + const bigTimeout = 200; + + test('Resolves before timeout', async () => { + const result = await pTimeout( + delay(smallTimeout).then(() => expected), + bigTimeout + ); + expect(result).toBe(expected); + }); + + test('Resolves after timeout', async () => { + await expect(pTimeout(delay(bigTimeout), smallTimeout)).rejects.toThrow( + new TimeoutError(`Promise timed out after ${smallTimeout} milliseconds`) + ); + }); + + test('Throws when milliseconds is negative number', async () => { + const milliseconds = -1; + await expect(pTimeout(delay(smallTimeout), milliseconds)).rejects.toThrow( + `Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\`` + ); + }); + + test('Throws when milliseconds is NaN', async () => { + const milliseconds = NaN; + await expect(pTimeout(delay(smallTimeout), milliseconds)).rejects.toThrow( + `Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\`` + ); + }); + + test('Handles milliseconds being `Infinity`', async () => { + const result = await pTimeout( + delay(smallTimeout).then(() => expected), + +Infinity + ); + expect(result).toBe(expected); + }); + + test('Rejects after timeout', async () => { + await expect( + pTimeout( + delay(bigTimeout).then(() => expected), + smallTimeout + ) + ).rejects.toThrow(new TimeoutError(`Promise timed out after ${smallTimeout} milliseconds`)); + }); + + test('Rejects before timeout if specified promise rejects', async () => { + await expect( + pTimeout( + delay(smallTimeout).then(() => Promise.reject(expectedError)), + bigTimeout + ) + ).rejects.toThrow(expectedError); + }); + + test('Fallback argument', async () => { + await expect(pTimeout(delay(bigTimeout), smallTimeout, 'error message')).rejects.toThrow( + 'error message' + ); + await expect( + pTimeout(delay(bigTimeout), smallTimeout, new RangeError('error message')) + ).rejects.toThrow(new RangeError('error message')); + await expect( + pTimeout(delay(bigTimeout), smallTimeout, () => Promise.reject(expectedError)) + ).rejects.toThrow(expectedError); + await expect( + pTimeout(delay(bigTimeout), smallTimeout, () => { + throw new RangeError('error message'); + }) + ).rejects.toThrow(new RangeError('error message')); + }); + + test('Calls `.cancel()` on promise when it exists', async () => { + const callSpy = fn(); + + const promise = new CancelablePromise(async (resolve, _, onCancel) => { + onCancel(() => { + callSpy(); + }); + + await delay(bigTimeout); + resolve(); + }); + + await expect(pTimeout(promise, smallTimeout)).rejects.toThrow( + new TimeoutError(`Promise timed out after ${smallTimeout} milliseconds`) + ); + expect(promise.isCanceled).toBeTruthy(); + expect(callSpy.mock.calls.length).toBe(1); + }); + + test('Accepts `customTimers` option', async () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const setTimeoutSpy = fn((_: number | undefined) => {}); + const clearTimeoutSpy = fn(); + + await pTimeout(delay(smallTimeout), bigTimeout, undefined, { + customTimers: { + setTimeout: function (handler: () => void, timeout?: number) { + setTimeoutSpy(timeout); + return setTimeout(handler as unknown as () => void, timeout || 1000); + } as typeof setTimeout, + clearTimeout(timeoutId) { + clearTimeoutSpy(); + return clearTimeout(timeoutId); + } + } + }); + + expect(setTimeoutSpy.mock.calls.length).toBe(1); + expect(setTimeoutSpy.mock.calls[0]).toEqual([bigTimeout]); + expect(clearTimeoutSpy.mock.calls.length).toBe(1); + }); + + test('`.clear()` method', async () => { + const promise = pTimeout( + delay(bigTimeout).then(() => expected), + smallTimeout + ); + promise.clear(); + + const result = await promise; + expect(result).toBe(expected); + }); +}); diff --git a/SDK-mstr/__tests__/unit-tests/core/sdk/sdk.spec.ts b/SDK-mstr/__tests__/unit-tests/core/sdk/sdk.spec.ts new file mode 100644 index 0000000..3b78a22 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/core/sdk/sdk.spec.ts @@ -0,0 +1,12 @@ +import { SDK } from 'src/core/sdk/sdk'; +import { freeRpc } from '__tests__/utils/constants/free-rpc'; +import { Configuration } from 'src/core/sdk/models/configuration'; + +describe('Common SDK tests', () => { + const minimalConfig: Configuration = { rpcProviders: freeRpc }; + + test('Should create sdk instance', async () => { + const sdk = await SDK.createSDK(minimalConfig); + expect(typeof sdk).toBe('object'); + }); +}); diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/avalanche/joe/joe.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/avalanche/joe/joe.ts new file mode 100644 index 0000000..fed242f --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/avalanche/joe/joe.ts @@ -0,0 +1,20 @@ +import { JoeProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/joe-provider'; +import { JoeTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/joe-trade'; +import { JOE_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/constants'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const joeAvalancheProviderSpec = () => { + describe('Joe provider tests', () => { + let joeProvider: JoeProvider; + + beforeAll(() => { + joeProvider = new JoeProvider(); + }); + + test('Initialize values', () => { + expect(joeProvider.blockchain).toBe(BLOCKCHAIN_NAME.AVALANCHE); + expect(typeof joeProvider.UniswapV2TradeClass).toBe(typeof JoeTrade); + expect(joeProvider.providerSettings).toBe(JOE_PROVIDER_CONFIGURATION); + }); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/avalanche/pangolin/pangolin.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/avalanche/pangolin/pangolin.ts new file mode 100644 index 0000000..e5003a6 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/avalanche/pangolin/pangolin.ts @@ -0,0 +1,20 @@ +import { PangolinProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/pangolin-provider'; +import { PangolinTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/pangolin-trade'; +import { PANGOLIN_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/constants'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const pangolinAvalancheProviderSpec = () => { + describe('Pangolin provider tests', () => { + let pangolinProvider: PangolinProvider; + + beforeAll(() => { + pangolinProvider = new PangolinProvider(); + }); + + test('Initialize values', () => { + expect(pangolinProvider.blockchain).toBe(BLOCKCHAIN_NAME.AVALANCHE); + expect(typeof pangolinProvider.UniswapV2TradeClass).toBe(typeof PangolinTrade); + expect(pangolinProvider.providerSettings).toBe(PANGOLIN_PROVIDER_CONFIGURATION); + }); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/avalanche/sushi-swap/sushi-swap.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/avalanche/sushi-swap/sushi-swap.ts new file mode 100644 index 0000000..a86480e --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/avalanche/sushi-swap/sushi-swap.ts @@ -0,0 +1,24 @@ +import { SushiSwapAvalancheProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/sushi-swap-avalanche-provider'; +import { SushiSwapAvalancheTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/sushi-swap-avalanche-trade'; +import { SUSHI_SWAP_AVALANCHE_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/constants'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const sushiSwapAvalancheProviderSpec = () => { + describe('SushiSwap provider tests', () => { + let sushiSwapProvider: SushiSwapAvalancheProvider; + + beforeAll(() => { + sushiSwapProvider = new SushiSwapAvalancheProvider(); + }); + + test('Initialize values', () => { + expect(sushiSwapProvider.blockchain).toBe(BLOCKCHAIN_NAME.AVALANCHE); + expect(typeof sushiSwapProvider.UniswapV2TradeClass).toBe( + typeof SushiSwapAvalancheTrade + ); + expect(sushiSwapProvider.providerSettings).toBe( + SUSHI_SWAP_AVALANCHE_PROVIDER_CONFIGURATION + ); + }); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/bsc/one-inch/one-inch.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/bsc/one-inch/one-inch.ts new file mode 100644 index 0000000..4de6bdf --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/bsc/one-inch/one-inch.ts @@ -0,0 +1,18 @@ +import { TRADE_TYPE } from 'src/features'; +import { OneinchBscProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/oneinch-bsc/oneinch-bsc-provider'; +import { mockEmptyInjector } from '__tests__/utils/mock-injector'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const oneinchBscProviderSpec = () => { + let oneinchProvider: OneinchBscProvider; + + beforeAll(() => { + mockEmptyInjector(); + oneinchProvider = new OneinchBscProvider(); + }); + + test('', () => { + expect(oneinchProvider.type).toBe(TRADE_TYPE.ONE_INCH_BSC); + expect(oneinchProvider.blockchain).toBe(BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/bsc/pancake-swap/pancake-swap.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/bsc/pancake-swap/pancake-swap.ts new file mode 100644 index 0000000..566ca89 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/bsc/pancake-swap/pancake-swap.ts @@ -0,0 +1,18 @@ +import { PancakeSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/pancake-swap-provider'; +import { PancakeSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/pancake-swap-trade'; +import { PANCAKE_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/constants'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const pancakeSwapBscProviderSpec = () => { + let pancakeSwapProvider: PancakeSwapProvider; + + beforeAll(() => { + pancakeSwapProvider = new PancakeSwapProvider(); + }); + + test('Initialize values', () => { + expect(pancakeSwapProvider.blockchain).toBe(BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN); + expect(typeof pancakeSwapProvider.UniswapV2TradeClass).toBe(typeof PancakeSwapTrade); + expect(pancakeSwapProvider.providerSettings).toBe(PANCAKE_SWAP_PROVIDER_CONFIGURATION); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/bsc/sushi-swap/sushi-swap.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/bsc/sushi-swap/sushi-swap.ts new file mode 100644 index 0000000..0d42b34 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/bsc/sushi-swap/sushi-swap.ts @@ -0,0 +1,18 @@ +import { SushiSwapBscProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/sushi-swap-bsc-provider'; +import { SushiSwapBscTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/sushi-swap-bsc-trade'; +import { SUSHI_SWAP_BSC_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/constants'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const sushiSwapBscProviderSpec = () => { + let sushiSwapBscProvider: SushiSwapBscProvider; + + beforeAll(() => { + sushiSwapBscProvider = new SushiSwapBscProvider(); + }); + + test('Initialize values', () => { + expect(sushiSwapBscProvider.blockchain).toBe(BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN); + expect(typeof sushiSwapBscProvider.UniswapV2TradeClass).toBe(typeof SushiSwapBscTrade); + expect(sushiSwapBscProvider.providerSettings).toBe(SUSHI_SWAP_BSC_PROVIDER_CONFIGURATION); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/one-inch/one-inch.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/one-inch/one-inch.ts new file mode 100644 index 0000000..e0c79af --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/one-inch/one-inch.ts @@ -0,0 +1,18 @@ +import { TRADE_TYPE } from 'src/features'; +import { OneinchEthereumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/oneinch-ethereum/oneinch-ethereum-provider'; +import { mockEmptyInjector } from '__tests__/utils/mock-injector'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const oneinchProviderEthereumSpec = () => { + let oneinchProvider: OneinchEthereumProvider; + + beforeAll(() => { + mockEmptyInjector(); + oneinchProvider = new OneinchEthereumProvider(); + }); + + test('Initialize values', () => { + expect(oneinchProvider.type).toBe(TRADE_TYPE.ONE_INCH_ETHEREUM); + expect(oneinchProvider.blockchain).toBe(BLOCKCHAIN_NAME.ETHEREUM); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/sushi-swap/sushi-swap.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/sushi-swap/sushi-swap.ts new file mode 100644 index 0000000..1857be4 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/sushi-swap/sushi-swap.ts @@ -0,0 +1,17 @@ +import { SushiSwapEthereumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/sushi-swap-ethereum-provider'; +import { SUSHI_SWAP_ETHEREUM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/constants'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const sushiSwapProviderEthereumSpec = () => { + let sushiSwapProvider: SushiSwapEthereumProvider; + + beforeAll(() => { + sushiSwapProvider = new SushiSwapEthereumProvider(); + }); + + test('Initialize values', () => { + expect(sushiSwapProvider.blockchain).toBe(BLOCKCHAIN_NAME.ETHEREUM); + expect(typeof sushiSwapProvider.UniswapV2TradeClass).toBe(typeof SushiSwapEthereumProvider); + expect(sushiSwapProvider.providerSettings).toBe(SUSHI_SWAP_ETHEREUM_PROVIDER_CONFIGURATION); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v2/uni-swap-v2-provider.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v2/uni-swap-v2-provider.ts new file mode 100644 index 0000000..d798c67 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v2/uni-swap-v2-provider.ts @@ -0,0 +1,92 @@ +import { Chain } from '__tests__/utils/chain'; +import { mockInjector } from '__tests__/utils/mock-injector'; +import { TOKENS as ALL_TOKENS } from '__tests__/utils/tokens'; +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens/price-token-amount'; +import { PriceToken } from 'src/common/tokens/price-token'; +import { UniSwapV2EthereumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/uni-swap-v2-ethereum-provider'; +import { UniSwapV2EthereumTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/uni-swap-v2-ethereum-trade'; +import { UNISWAP_V2_ETHEREUM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/constants'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +const TOKENS = ALL_TOKENS[BLOCKCHAIN_NAME.ETHEREUM]; + +export const uniswapV2ProviderSpec = () => + describe('Uniswap V2 provider tests', () => { + let uniswapV2Provider: UniSwapV2EthereumProvider; + + beforeAll(async () => { + uniswapV2Provider = new UniSwapV2EthereumProvider(); + }); + + beforeEach(async () => { + const chain = await Chain.reset(BLOCKCHAIN_NAME.ETHEREUM); + const configuration = await chain.getConfiguration(); + await mockInjector(configuration); + }); + + test('Initialize values', () => { + expect(uniswapV2Provider.blockchain).toBe(BLOCKCHAIN_NAME.ETHEREUM); + expect(typeof uniswapV2Provider.UniswapV2TradeClass).toBe( + typeof UniSwapV2EthereumTrade + ); + expect(uniswapV2Provider.providerSettings).toBe( + UNISWAP_V2_ETHEREUM_PROVIDER_CONFIGURATION + ); + }); + + test('Must calculate correct NATIVE-ERC20 trade with simple path.', async () => { + const expectedToTokensAmount = 3173.460947; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.ETH, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.USDT); + + const trade = await uniswapV2Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.ETH.address); + expect(trade.path[1].address).toBe(TOKENS.USDT.address); + }, 400_000); + + test('Must calculate correct ERC20-NATIVE trade with simple path.', async () => { + const expectedToTokensAmount = '0.000313213011396446'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.ETH); + + const trade = await uniswapV2Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.USDT.address); + expect(trade.path[1].address).toBe(TOKENS.ETH.address); + }, 400_000); + + test('Must calculate correct ERC20-ERC20 trade with simple path.', async () => { + const expectedToTokensAmount = '4.557092512974888195'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.RBC); + + const trade = await uniswapV2Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(3); + expect(trade.path[0].address).toBe(TOKENS.USDT.address); + expect(trade.path[1].address).toBe(TOKENS.WETH.address); + expect(trade.path[2].address).toBe(TOKENS.RBC.address); + }, 400_000); + }); diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v2/uni-swap-v2-trade.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v2/uni-swap-v2-trade.ts new file mode 100644 index 0000000..fcd9a90 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v2/uni-swap-v2-trade.ts @@ -0,0 +1,166 @@ +import { Chain } from '__tests__/utils/chain'; +import { mockInjector } from '__tests__/utils/mock-injector'; +import { TOKENS as ALL_TOKENS } from '__tests__/utils/tokens'; +import { Injector } from 'src/core/injector/injector'; +import BigNumber from 'bignumber.js'; +import { Web3Public } from 'src/core'; +import { PriceTokenAmount } from 'src/common/tokens/price-token-amount'; +import { PriceToken } from 'src/common/tokens/price-token'; +import { TransactionReceipt } from 'web3-eth'; +import { UniSwapV2EthereumProvider } from '@rsdk-features/instant-trades/dexes/ethereum/uni-swap-v2-ethereum/uni-swap-v2-ethereum-provider'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import fn = jest.fn; + +const TOKENS = ALL_TOKENS[BLOCKCHAIN_NAME.ETHEREUM]; + +export const uniswapV2TradeSpec = () => + describe('Uniswap V2 trade tests.', () => { + let chain: Chain; + let uniswapV2Provider: UniSwapV2EthereumProvider; + let web3Public: Web3Public; + let userAddress: string; + + const getTransactionFeeByReceipt = async ( + transactionReceipt: TransactionReceipt + ): Promise => { + const transaction = (await web3Public.getTransactionByHash( + transactionReceipt.transactionHash + ))!; + return new BigNumber(transactionReceipt.gasUsed).multipliedBy(transaction.gasPrice); + }; + + const getTransactionFeeByHash = async (transactionHash: string): Promise => { + const transaction = (await web3Public.getTransactionByHash(transactionHash))!; + const transactionReceipt = (await chain.web3.eth.getTransactionReceipt( + transactionHash + ))!; + return new BigNumber(transactionReceipt.gasUsed).multipliedBy(transaction.gasPrice); + }; + + beforeAll(async () => { + uniswapV2Provider = new UniSwapV2EthereumProvider(); + }); + + beforeEach(async () => { + chain = await Chain.reset(BLOCKCHAIN_NAME.ETHEREUM); + const configuration = await chain.getConfiguration(); + await mockInjector(configuration); + web3Public = Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.ETHEREUM); + userAddress = Injector.web3PrivateService.address; + }, 20_000); + + test('Swap method must works with NATIVE-ERC20 trade', async () => { + const ethTokenAmountToSwap = 1; + const expectedToTokensAmount = 3173.460947; // constant data about tokens rate in 13961175 block + const ethBalanceBefore = await web3Public.getBalance(userAddress); + const usdtBalanceBefore = await web3Public.getBalance(userAddress, TOKENS.USDT.address); + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.ETH, + tokenAmount: new BigNumber(ethTokenAmountToSwap) + }); + const to = await PriceToken.createFromToken(TOKENS.USDT); + + const trade = await uniswapV2Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + const transactionReceipt = await trade.swap(); + const ethBalanceAfter = await web3Public.getBalance(userAddress); + const usdtBalanceAfter = await web3Public.getBalance(userAddress, TOKENS.USDT.address); + const transactionFee = await getTransactionFeeByReceipt(transactionReceipt); + + expect(transactionReceipt.status).toBeTruthy(); + expect( + ethBalanceAfter.isEqualTo( + ethBalanceBefore + .minus(ethTokenAmountToSwap * 10 ** TOKENS.ETH.decimals) + .minus(transactionFee) + ) + ).toBeTruthy(); + expect( + usdtBalanceAfter.isEqualTo( + usdtBalanceBefore.plus(expectedToTokensAmount * 10 ** TOKENS.USDT.decimals) + ) + ).toBeTruthy(); + + const trade1 = await uniswapV2Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + expect(trade1.to.weiAmount.isEqualTo(trade.to.weiAmount)).not.toBeTruthy(); + }, 400_000); + + test('Swap method must works with ERC20-NATIVE trade', async () => { + const rbcTokenAmountToSwap = 1; + const expectedToTokensAmount = '0.000068319082745321'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.RBC, + tokenAmount: new BigNumber(rbcTokenAmountToSwap) + }); + const to = await PriceToken.createFromToken(TOKENS.ETH); + await chain.increaseTokensBalance(from, rbcTokenAmountToSwap, { inEtherUnits: true }); + const rbcBalanceBefore = await web3Public.getBalance(userAddress, from.address); + const ethBalanceBefore = await web3Public.getBalance(userAddress); + let approveTxHash = ''; + + const onApprove = fn(hash => (approveTxHash = hash)); + const trade = await uniswapV2Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + const transactionReceipt = await trade.swap({ onApprove }); + const rbcBalanceAfter = await web3Public.getBalance(userAddress, from.address); + const ethBalanceAfter = await web3Public.getBalance(userAddress); + const approveTransactionFee = await getTransactionFeeByHash(approveTxHash); + const transactionFee = await getTransactionFeeByReceipt(transactionReceipt); + + expect(transactionReceipt.status).toBeTruthy(); + expect( + rbcBalanceAfter.isEqualTo( + rbcBalanceBefore.minus(rbcTokenAmountToSwap * 10 ** from.decimals) + ) + ).toBeTruthy(); + + expect( + ethBalanceAfter.isEqualTo( + ethBalanceBefore + .plus(new BigNumber(expectedToTokensAmount).multipliedBy(10 ** to.decimals)) + .minus(approveTransactionFee) + .minus(transactionFee) + ) + ).toBeTruthy(); + expect(onApprove.mock.calls.length).toBe(1); + }, 400_000); + + test('Swap method must works with ERC20-ERC20 trade', async () => { + const rbcTokenAmountToSwap = 1; + const expectedToTokensAmount = '0.216816'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.RBC, + tokenAmount: new BigNumber(rbcTokenAmountToSwap) + }); + const to = await PriceToken.createFromToken(TOKENS.USDT); + await chain.increaseTokensBalance(from, rbcTokenAmountToSwap, { inEtherUnits: true }); + const rbcBalanceBefore = await web3Public.getBalance(userAddress, from.address); + const usdtBalanceBefore = await web3Public.getBalance(userAddress, to.address); + + const trade = await uniswapV2Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + const transactionReceipt = await trade.swap(); + const rbcBalanceAfter = await web3Public.getBalance(userAddress, from.address); + const usdtBalanceAfter = await web3Public.getBalance(userAddress, to.address); + + expect(transactionReceipt.status).toBeTruthy(); + expect( + rbcBalanceAfter.isEqualTo( + rbcBalanceBefore.minus(rbcTokenAmountToSwap * 10 ** from.decimals) + ) + ).toBeTruthy(); + + expect( + usdtBalanceAfter.isEqualTo( + usdtBalanceBefore.plus( + new BigNumber(expectedToTokensAmount).multipliedBy(10 ** to.decimals) + ) + ) + ).toBeTruthy(); + }, 400_000); + }); diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v3/uni-swap-v3-ethereum-provider.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v3/uni-swap-v3-ethereum-provider.ts new file mode 100644 index 0000000..446ff77 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v3/uni-swap-v3-ethereum-provider.ts @@ -0,0 +1,94 @@ +import { Chain } from '__tests__/utils/chain'; +import { mockInjector } from '__tests__/utils/mock-injector'; +import { TOKENS as ALL_TOKENS } from '__tests__/utils/tokens'; +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens/price-token-amount'; +import { PriceToken } from 'src/common/tokens/price-token'; +import { UniSwapV3EthereumTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/uni-swap-v3-ethereum-trade'; +import { UNI_SWAP_V3_ETHEREUM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/constants/provider-configuration'; +import { UNI_SWAP_V3_ETHEREUM_ROUTER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/constants/router-configuration'; +import { UniSwapV3EthereumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/uni-swap-v3-ethereum-provider'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +const TOKENS = ALL_TOKENS[BLOCKCHAIN_NAME.ETHEREUM]; + +export const uniswapV3EthProviderSpec = () => + describe('UnisSwap V3 Ethereum provider tests', () => { + let uniswapV3Provider: UniSwapV3EthereumProvider; + + beforeAll(async () => { + uniswapV3Provider = new UniSwapV3EthereumProvider(); + }); + + beforeEach(async () => { + const chain = await Chain.reset(BLOCKCHAIN_NAME.ETHEREUM); + const configuration = await chain.getConfiguration(); + await mockInjector(configuration); + }); + + test('Initialize values', () => { + expect(uniswapV3Provider.blockchain).toBe(BLOCKCHAIN_NAME.ETHEREUM); + expect(typeof uniswapV3Provider.OnChainTradeClass).toBe(typeof UniSwapV3EthereumTrade); + expect(uniswapV3Provider.providerConfiguration).toBe( + UNI_SWAP_V3_ETHEREUM_PROVIDER_CONFIGURATION + ); + expect(uniswapV3Provider.routerConfiguration).toBe( + UNI_SWAP_V3_ETHEREUM_ROUTER_CONFIGURATION + ); + }); + + test('Must calculate correct NATIVE-ERC20 trade with simple path.', async () => { + const expectedToTokensAmount = '3177.56875989798300356'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.ETH, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.DAI); + + const trade = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.ETH.address); + expect(trade.path[1].address).toBe(TOKENS.DAI.address); + }, 800_000); + + test('Must calculate correct ERC20-NATIVE trade with simple path.', async () => { + const expectedToTokensAmount = '0.000314923189705958'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.ETH); + + const trade = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.USDT.address); + expect(trade.path[1].address).toBe(TOKENS.ETH.address); + }, 400_000); + + test('Must calculate correct ERC20-ERC20 trade with simple path.', async () => { + const expectedToTokensAmount = '1.02692208070202167'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.DAI); + + const trade = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(3); + expect(trade.path[0].address).toBe(TOKENS.USDT.address); + expect(trade.path[1].address).toBe(TOKENS.WBTC.address); + expect(trade.path[2].address).toBe(TOKENS.DAI.address); + }, 400_000); + }); diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v3/uni-swap-v3-ethereum-trade.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v3/uni-swap-v3-ethereum-trade.ts new file mode 100644 index 0000000..60ce518 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/uni-swap-v3/uni-swap-v3-ethereum-trade.ts @@ -0,0 +1,160 @@ +import { Chain } from '__tests__/utils/chain'; +import { mockInjector } from '__tests__/utils/mock-injector'; +import { TOKENS as ALL_TOKENS } from '__tests__/utils/tokens'; +import { Utils } from '__tests__/unit-tests/features/swap/utils/utils'; +import { Injector } from 'src/core/injector/injector'; +import BigNumber from 'bignumber.js'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { PriceTokenAmount } from 'src/common/tokens/price-token-amount'; +import { PriceToken } from 'src/common/tokens/price-token'; +import { UniSwapV3EthereumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/uni-swap-v3-ethereum-provider'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import fn = jest.fn; + +const TOKENS = ALL_TOKENS[BLOCKCHAIN_NAME.ETHEREUM]; + +export const uniswapV3EthTradeSpec = () => + describe('UniSwap V3 Ethereum trade tests.', () => { + let chain: Chain; + let uniswapV3Provider: UniSwapV3EthereumProvider; + let web3Public: EvmWeb3Public; + let userAddress: string; + let utils: Utils; + + beforeAll(async () => { + uniswapV3Provider = new UniSwapV3EthereumProvider(); + }); + + beforeEach(async () => { + chain = await Chain.reset(BLOCKCHAIN_NAME.ETHEREUM); + const configuration = await chain.getConfiguration(); + await mockInjector(configuration); + web3Public = Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.ETHEREUM); + userAddress = Injector.web3PrivateService.getWeb3PrivateByBlockchain( + BLOCKCHAIN_NAME.POLYGON + ).address; + utils = new Utils(chain, web3Public); + }); + + test('Swap method must works with NATIVE-ERC20 trade', async () => { + const ethTokenAmountToSwap = 1; + const expectedToTokensAmount = '3177.56875989798300356'; // constant data about tokens rate in 13961175 block + const ethBalanceBefore = await web3Public.getBalance(userAddress); + const daiBalanceBefore = await web3Public.getBalance(userAddress, TOKENS.DAI.address); + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.ETH, + tokenAmount: new BigNumber(ethTokenAmountToSwap) + }); + const to = await PriceToken.createFromToken(TOKENS.DAI); + + const trade = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + const transactionHash = await trade.swap(); + const transactionReceipt = await web3Public.getTransactionReceipt(transactionHash); + const ethBalanceAfter = await web3Public.getBalance(userAddress); + const daiBalanceAfter = await web3Public.getBalance(userAddress, TOKENS.DAI.address); + const transactionFee = await utils.getTransactionFeeByReceipt(transactionReceipt); + + expect(transactionReceipt.status).toBeTruthy(); + expect( + ethBalanceAfter.isEqualTo( + ethBalanceBefore + .minus(ethTokenAmountToSwap * 10 ** TOKENS.ETH.decimals) + .minus(transactionFee) + ) + ).toBeTruthy(); + expect( + daiBalanceAfter.isEqualTo( + daiBalanceBefore.plus( + new BigNumber(expectedToTokensAmount).multipliedBy( + 10 ** TOKENS.DAI.decimals + ) + ) + ) + ).toBeTruthy(); + + const trade1 = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + expect(trade1.to.weiAmount.isEqualTo(trade.to.weiAmount)).not.toBeTruthy(); + }, 400_000); + + test('Swap method must works with ERC20-NATIVE trade', async () => { + const usdtTokenAmountToSwap = 1; + const expectedToTokensAmount = '0.000314923189705958'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(usdtTokenAmountToSwap) + }); + const to = await PriceToken.createFromToken(TOKENS.ETH); + await chain.increaseTokensBalance(from, usdtTokenAmountToSwap, { inEtherUnits: true }); + const usdtBalanceBefore = await web3Public.getBalance(userAddress, from.address); + const ethBalanceBefore = await web3Public.getBalance(userAddress); + let approveTxHash = ''; + + const onApprove = fn(hash => (approveTxHash = hash)); + const trade = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + const transactionHash = await trade.swap({ onApprove }); + const transactionReceipt = await web3Public.getTransactionReceipt(transactionHash); + const usdtBalanceAfter = await web3Public.getBalance(userAddress, from.address); + const ethBalanceAfter = await web3Public.getBalance(userAddress); + const approveTransactionFee = await utils.getTransactionFeeByHash(approveTxHash); + const transactionFee = await utils.getTransactionFeeByReceipt(transactionReceipt); + + expect(transactionReceipt.status).toBeTruthy(); + expect( + usdtBalanceAfter.isEqualTo( + usdtBalanceBefore.minus(usdtTokenAmountToSwap * 10 ** from.decimals) + ) + ).toBeTruthy(); + + expect( + ethBalanceAfter.isEqualTo( + ethBalanceBefore + .plus(new BigNumber(expectedToTokensAmount).multipliedBy(10 ** to.decimals)) + .minus(approveTransactionFee) + .minus(transactionFee) + ) + ).toBeTruthy(); + expect(onApprove.mock.calls.length).toBe(1); + }, 400_000); + + test('Swap method must works with ERC20-ERC20 trade', async () => { + const usdtTokenAmountToSwap = 1; + const expectedToTokensAmount = '1.02692208070202167'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(usdtTokenAmountToSwap) + }); + const to = await PriceToken.createFromToken(TOKENS.DAI); + await chain.increaseTokensBalance(from, usdtTokenAmountToSwap, { inEtherUnits: true }); + const usdtBalanceBefore = await web3Public.getBalance(userAddress, from.address); + const daiBalanceBefore = await web3Public.getBalance(userAddress, to.address); + + const trade = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + const transactionHash = await trade.swap(); + const transactionReceipt = await web3Public.getTransactionReceipt(transactionHash); + const usdtBalanceAfter = await web3Public.getBalance(userAddress, from.address); + const daiBalanceAfter = await web3Public.getBalance(userAddress, to.address); + + expect(transactionReceipt.status).toBeTruthy(); + expect( + usdtBalanceAfter.isEqualTo( + usdtBalanceBefore.minus(usdtTokenAmountToSwap * 10 ** from.decimals) + ) + ).toBeTruthy(); + + expect( + daiBalanceAfter.isEqualTo( + daiBalanceBefore.plus( + new BigNumber(expectedToTokensAmount).multipliedBy(10 ** to.decimals) + ) + ) + ).toBeTruthy(); + }, 400_000); + }); diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/zrx/zrx.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/zrx/zrx.ts new file mode 100644 index 0000000..fe6536f --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/ethereum/zrx/zrx.ts @@ -0,0 +1,16 @@ +import { TRADE_TYPE } from 'src/features'; +import { ZrxEthereumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/zrx-ethereum/zrx-ethereum-provider'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const zrxProviderEthereumSpec = () => { + let zrxProvider: ZrxEthereumProvider; + + beforeEach(() => { + zrxProvider = new ZrxEthereumProvider(); + }); + + test('Initialize values', () => { + expect(zrxProvider.type).toBe(TRADE_TYPE.ZRX_ETHEREUM); + expect(zrxProvider.blockchain).toBe(BLOCKCHAIN_NAME.ETHEREUM); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/fantom/spirit-swap/spirit-swap.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/fantom/spirit-swap/spirit-swap.ts new file mode 100644 index 0000000..60b90a5 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/fantom/spirit-swap/spirit-swap.ts @@ -0,0 +1,18 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { SpiritSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/spirit-swap-provider'; +import { SpiritSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/spirit-swap-trade'; +import { SPIRIT_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/constants'; + +export const spiritSwapFantomProviderSpec = () => { + let spiritSwapProvider: SpiritSwapProvider; + + beforeAll(() => { + spiritSwapProvider = new SpiritSwapProvider(); + }); + + test('Initialize values', () => { + expect(spiritSwapProvider.blockchain).toBe(BLOCKCHAIN_NAME.FANTOM); + expect(typeof spiritSwapProvider.UniswapV2TradeClass).toBe(typeof SpiritSwapTrade); + expect(spiritSwapProvider.providerSettings).toBe(SPIRIT_SWAP_PROVIDER_CONFIGURATION); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/fantom/spooky-swap/spooky-swap.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/fantom/spooky-swap/spooky-swap.ts new file mode 100644 index 0000000..399e1b3 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/fantom/spooky-swap/spooky-swap.ts @@ -0,0 +1,18 @@ +import { BLOCKCHAIN_NAME } from 'src/core'; +import { SpookySwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/spooky-swap-trade'; +import { SPOOKY_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/constants'; +import { SpookySwapProvider } from '@rsdk-features/instant-trades/dexes/fantom/spooky-swap/spooky-swap-provider'; + +export const spookySwapFantomProviderSpec = () => { + let spookySwapProvider: SpookySwapProvider; + + beforeAll(() => { + spookySwapProvider = new SpookySwapProvider(); + }); + + test('Initialize values', () => { + expect(spookySwapProvider.blockchain).toBe(BLOCKCHAIN_NAME.FANTOM); + expect(typeof spookySwapProvider.InstantTradeClass).toBe(typeof SpookySwapTrade); + expect(spookySwapProvider.providerSettings).toBe(SPOOKY_SWAP_PROVIDER_CONFIGURATION); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/fantom/sushi-swap/sushi-swap.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/fantom/sushi-swap/sushi-swap.ts new file mode 100644 index 0000000..a989ff7 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/fantom/sushi-swap/sushi-swap.ts @@ -0,0 +1,18 @@ +import { SushiSwapFantomProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/sushi-swap-fantom-provider'; +import { SushiSwapFantomTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/sushi-swap-fantom-trade'; +import { SUSHI_SWAP_FANTOM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/constants'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const sushiSwapFantomProviderSpec = () => { + let sushiSwapProvider: SushiSwapFantomProvider; + + beforeAll(() => { + sushiSwapProvider = new SushiSwapFantomProvider(); + }); + + test('Initialize values', () => { + expect(sushiSwapProvider.blockchain).toBe(BLOCKCHAIN_NAME.FANTOM); + expect(typeof sushiSwapProvider.UniswapV2TradeClass).toBe(typeof SushiSwapFantomTrade); + expect(sushiSwapProvider.providerSettings).toBe(SUSHI_SWAP_FANTOM_PROVIDER_CONFIGURATION); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/harmony/sushi-swap.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/harmony/sushi-swap.ts new file mode 100644 index 0000000..63d9f58 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/harmony/sushi-swap.ts @@ -0,0 +1,18 @@ +import { SushiSwapHarmonyProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/sushi-swap-harmony-provider'; +import { SushiSwapHarmonyTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/sushi-swap-harmony-trade'; +import { SUSHI_SWAP_HARMONY_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/constants'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const sushiSwapHarmonyProviderSpec = () => { + let sushiSwapProvider: SushiSwapHarmonyProvider; + + beforeAll(() => { + sushiSwapProvider = new SushiSwapHarmonyProvider(); + }); + + test('Initialize values', () => { + expect(sushiSwapProvider.blockchain).toBe(BLOCKCHAIN_NAME.HARMONY); + expect(typeof sushiSwapProvider.UniswapV2TradeClass).toBe(typeof SushiSwapHarmonyTrade); + expect(sushiSwapProvider.providerSettings).toBe(SUSHI_SWAP_HARMONY_PROVIDER_CONFIGURATION); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/moonriver/solarbeam/solarbeam.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/moonriver/solarbeam/solarbeam.ts new file mode 100644 index 0000000..bc18965 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/moonriver/solarbeam/solarbeam.ts @@ -0,0 +1,20 @@ +import { SolarbeamProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/solarbeam-provider'; +import { SolarbeamTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/solarbeam-trade'; +import { SOLARBEAM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/constants'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const solarbeamMoonriverProviderSpec = () => { + describe('Solarbeam provider tests', () => { + let solarbeamProvider: SolarbeamProvider; + + beforeAll(async () => { + solarbeamProvider = new SolarbeamProvider(); + }); + + test('Initialize values', () => { + expect(solarbeamProvider.blockchain).toBe(BLOCKCHAIN_NAME.MOONRIVER); + expect(typeof solarbeamProvider.UniswapV2TradeClass).toBe(typeof SolarbeamTrade); + expect(solarbeamProvider.providerSettings).toBe(SOLARBEAM_PROVIDER_CONFIGURATION); + }, 400_000); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/moonriver/sushi-swap/sushi-swap.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/moonriver/sushi-swap/sushi-swap.ts new file mode 100644 index 0000000..d714046 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/moonriver/sushi-swap/sushi-swap.ts @@ -0,0 +1,24 @@ +import { SushiSwapMoonriverProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/sushi-swap-moonriver-provider'; +import { SushiSwapMoonriverTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/sushi-swap-moonriver-trade'; +import { SUSHI_SWAP_MOONRIVER_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/constants'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const sushiSwapMoonriverProviderSpec = () => { + describe('SushiSwap provider tests', () => { + let sushiSwapProvider: SushiSwapMoonriverProvider; + + beforeAll(async () => { + sushiSwapProvider = new SushiSwapMoonriverProvider(); + }); + + test('Initialize values', () => { + expect(sushiSwapProvider.blockchain).toBe(BLOCKCHAIN_NAME.MOONRIVER); + expect(typeof sushiSwapProvider.UniswapV2TradeClass).toBe( + typeof SushiSwapMoonriverTrade + ); + expect(sushiSwapProvider.providerSettings).toBe( + SUSHI_SWAP_MOONRIVER_PROVIDER_CONFIGURATION + ); + }, 400_000); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/algebra/algebra-provider.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/algebra/algebra-provider.ts new file mode 100644 index 0000000..865c2c6 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/algebra/algebra-provider.ts @@ -0,0 +1,76 @@ +import { Chain } from '__tests__/utils/chain'; +import { mockInjector } from '__tests__/utils/mock-injector'; +import { TOKENS as ALL_TOKENS } from '__tests__/utils/tokens'; +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens/price-token-amount'; +import { PriceToken } from 'src/common/tokens/price-token'; +import { AlgebraProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/algebra-provider'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +const TOKENS = ALL_TOKENS[BLOCKCHAIN_NAME.POLYGON]; + +export const algebraPolygonProviderSpec = () => { + describe('Algebra provider tests', () => { + let algebraProvider: AlgebraProvider; + + beforeAll(async () => { + algebraProvider = new AlgebraProvider(); + }); + + beforeEach(async () => { + const chain = await Chain.reset(BLOCKCHAIN_NAME.POLYGON); + const configuration = await chain.getConfiguration(); + await mockInjector(configuration); + }); + + test('Must calculate correct NATIVE-ERC20 trade with simple path.', async () => { + const expectedToTokensAmount = '2.037958'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.MATIC, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.USDT); + + const trade = await algebraProvider.calculate(from, to, { gasCalculation: 'disabled' }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(3); + expect(trade.path[0].address).toBe(TOKENS.MATIC.address); + expect(trade.path[1].address).toBe(TOKENS.USDC.address); + expect(trade.path[2].address).toBe(TOKENS.USDT.address); + }, 300_000); + + test('Must calculate correct ERC20-NATIVE trade with simple path.', async () => { + const expectedToTokensAmount = '0.487931209815334164'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.MATIC); + + const trade = await algebraProvider.calculate(from, to, { gasCalculation: 'disabled' }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(3); + expect(trade.path[0].address).toBe(TOKENS.USDT.address); + expect(trade.path[1].address).toBe(TOKENS.USDC.address); + expect(trade.path[2].address).toBe(TOKENS.MATIC.address); + }, 300_000); + + test('Must calculate correct ERC20-ERC20 trade with simple path.', async () => { + const expectedToTokensAmount = '1.000083'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.USDC); + + const trade = await algebraProvider.calculate(from, to, { gasCalculation: 'disabled' }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.USDT.address); + expect(trade.path[1].address).toBe(TOKENS.USDC.address); + }, 300_000); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/algebra/algebra-trade.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/algebra/algebra-trade.ts new file mode 100644 index 0000000..e24fbc6 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/algebra/algebra-trade.ts @@ -0,0 +1,147 @@ +import { Chain } from '__tests__/utils/chain'; +import { mockInjector } from '__tests__/utils/mock-injector'; +import { TOKENS as ALL_TOKENS } from '__tests__/utils/tokens'; +import { Utils } from '__tests__/unit-tests/features/swap/utils/utils'; +import { Injector } from 'src/core/injector/injector'; +import BigNumber from 'bignumber.js'; +import { BLOCKCHAIN_NAME, Web3Public } from 'src/core'; +import { PriceTokenAmount } from 'src/common/tokens/price-token-amount'; +import { PriceToken } from 'src/common/tokens/price-token'; +import { AlgebraProvider } from '@rsdk-features/instant-trades/dexes/polygon/algebra/algebra-provider'; +import fn = jest.fn; + +const TOKENS = ALL_TOKENS[BLOCKCHAIN_NAME.POLYGON]; + +export const algebraPolygonTradeSpec = () => { + describe('Algebra trade tests.', () => { + let chain: Chain; + let algebraProvider: AlgebraProvider; + let web3Public: Web3Public; + let userAddress: string; + let utils: Utils; + + beforeAll(async () => { + algebraProvider = new AlgebraProvider(); + }); + + beforeEach(async () => { + chain = await Chain.reset(BLOCKCHAIN_NAME.POLYGON); + const configuration = await chain.getConfiguration(); + await mockInjector(configuration); + web3Public = Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.POLYGON); + userAddress = Injector.web3PrivateService.address; + utils = new Utils(chain, web3Public); + }); + + test('Swap method must works with NATIVE-ERC20 trade', async () => { + const maticTokenAmountToSwap = 1; + const expectedToTokensAmount = '2.037958'; // constant data about tokens rate in 23571568 block + const maticBalanceBefore = await web3Public.getBalance(userAddress); + const usdtBalanceBefore = await web3Public.getBalance(userAddress, TOKENS.USDT.address); + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.MATIC, + tokenAmount: new BigNumber(maticTokenAmountToSwap) + }); + const to = await PriceToken.createFromToken(TOKENS.USDT); + + const trade = await algebraProvider.calculate(from, to, { gasCalculation: 'disabled' }); + const transactionReceipt = await trade.swap(); + const maticBalanceAfter = await web3Public.getBalance(userAddress); + const usdtBalanceAfter = await web3Public.getBalance(userAddress, TOKENS.USDT.address); + const transactionFee = await utils.getTransactionFeeByReceipt(transactionReceipt); + + expect(transactionReceipt.status).toBeTruthy(); + expect( + maticBalanceAfter.isEqualTo( + maticBalanceBefore + .minus(maticTokenAmountToSwap * 10 ** from.decimals) + .minus(transactionFee) + ) + ).toBeTruthy(); + expect( + usdtBalanceAfter.isEqualTo( + usdtBalanceBefore.plus( + new BigNumber(expectedToTokensAmount).multipliedBy(10 ** to.decimals) + ) + ) + ).toBeTruthy(); + + const trade1 = await algebraProvider.calculate(from, to, { + gasCalculation: 'disabled' + }); + expect(trade1.to.weiAmount.isEqualTo(trade.to.weiAmount)).not.toBeTruthy(); + }, 300_000); + + test('Swap method must works with ERC20-NATIVE trade', async () => { + const usdtTokenAmountToSwap = 1; + const expectedToTokensAmount = '0.487931209815334164'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(usdtTokenAmountToSwap) + }); + const to = await PriceToken.createFromToken(TOKENS.MATIC); + await chain.increaseTokensBalance(from, usdtTokenAmountToSwap, { inEtherUnits: true }); + const usdtBalanceBefore = await web3Public.getBalance(userAddress, from.address); + const maticBalanceBefore = await web3Public.getBalance(userAddress); + let approveTxHash = ''; + + const onApprove = fn(hash => (approveTxHash = hash)); + const trade = await algebraProvider.calculate(from, to, { gasCalculation: 'disabled' }); + const transactionReceipt = await trade.swap({ onApprove }); + const usdtBalanceAfter = await web3Public.getBalance(userAddress, from.address); + const maticBalanceAfter = await web3Public.getBalance(userAddress); + const approveTransactionFee = await utils.getTransactionFeeByHash(approveTxHash); + const transactionFee = await utils.getTransactionFeeByReceipt(transactionReceipt); + + expect(transactionReceipt.status).toBeTruthy(); + expect( + usdtBalanceAfter.isEqualTo( + usdtBalanceBefore.minus(usdtTokenAmountToSwap * 10 ** from.decimals) + ) + ).toBeTruthy(); + + expect( + maticBalanceAfter.isEqualTo( + maticBalanceBefore + .plus(new BigNumber(expectedToTokensAmount).multipliedBy(10 ** to.decimals)) + .minus(approveTransactionFee) + .minus(transactionFee) + ) + ).toBeTruthy(); + expect(onApprove.mock.calls.length).toBe(1); + }, 300_000); + + test('Swap method must works with ERC20-ERC20 trade', async () => { + const usdtTokenAmountToSwap = 1; + const expectedToTokensAmount = '1.000083'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(usdtTokenAmountToSwap) + }); + const to = await PriceToken.createFromToken(TOKENS.USDC); + await chain.increaseTokensBalance(from, usdtTokenAmountToSwap, { inEtherUnits: true }); + const usdtBalanceBefore = await web3Public.getBalance(userAddress, from.address); + const usdcBalanceBefore = await web3Public.getBalance(userAddress, to.address); + + const trade = await algebraProvider.calculate(from, to, { gasCalculation: 'disabled' }); + const transactionReceipt = await trade.swap(); + const usdtBalanceAfter = await web3Public.getBalance(userAddress, from.address); + const usdcBalanceAfter = await web3Public.getBalance(userAddress, to.address); + + expect(transactionReceipt.status).toBeTruthy(); + expect( + usdtBalanceAfter.isEqualTo( + usdtBalanceBefore.minus(usdtTokenAmountToSwap * 10 ** from.decimals) + ) + ).toBeTruthy(); + + expect( + usdcBalanceAfter.isEqualTo( + usdcBalanceBefore.plus( + new BigNumber(expectedToTokensAmount).multipliedBy(10 ** to.decimals) + ) + ) + ).toBeTruthy(); + }, 300_000); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/one-inch/one-inch.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/one-inch/one-inch.ts new file mode 100644 index 0000000..911e1db --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/one-inch/one-inch.ts @@ -0,0 +1,20 @@ +import { TRADE_TYPE } from 'src/features'; +import { OneinchPolygonProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/oneinch-polygon/oneinch-polygon-provider'; +import { mockEmptyInjector } from '__tests__/utils/mock-injector'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const oneinchPolygonProviderSpec = () => { + describe('QuickSwap provider tests', () => { + let oneinchPolygonProvider: OneinchPolygonProvider; + + beforeAll(() => { + mockEmptyInjector(); + oneinchPolygonProvider = new OneinchPolygonProvider(); + }); + + test('Initialize values', () => { + expect(oneinchPolygonProvider.type).toBe(TRADE_TYPE.ONE_INCH_POLYGON); + expect(oneinchPolygonProvider.blockchain).toBe(BLOCKCHAIN_NAME.POLYGON); + }, 400_000); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/quick-swap/quick-swap.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/quick-swap/quick-swap.ts new file mode 100644 index 0000000..766d545 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/quick-swap/quick-swap.ts @@ -0,0 +1,204 @@ +import { Chain } from '__tests__/utils/chain'; +import { mockInjector } from '__tests__/utils/mock-injector'; +import { TOKENS as ALL_TOKENS } from '__tests__/utils/tokens'; +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens/price-token-amount'; +import { PriceToken } from 'src/common/tokens/price-token'; +import { QuickSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/quick-swap-provider'; +import { QuickSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/quick-swap-trade'; +import { QUICK_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/constants'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +const TOKENS = ALL_TOKENS[BLOCKCHAIN_NAME.POLYGON]; + +export const quickSwapPolygonProviderSpec = () => { + describe('QuickSwap provider tests', () => { + let quickSwapProvider: QuickSwapProvider; + + beforeAll(async () => { + quickSwapProvider = new QuickSwapProvider(); + }); + + beforeEach(async () => { + const chain = await Chain.reset(BLOCKCHAIN_NAME.POLYGON); + const configuration = await chain.getConfiguration(); + await mockInjector(configuration); + }); + + test('Initialize values', () => { + expect(quickSwapProvider.blockchain).toBe(BLOCKCHAIN_NAME.POLYGON); + expect(typeof quickSwapProvider.UniswapV2TradeClass).toBe(typeof QuickSwapTrade); + expect(quickSwapProvider.providerSettings).toBe(QUICK_SWAP_PROVIDER_CONFIGURATION); + }, 400_000); + + test('Must calculate correct NATIVE-ERC20 trade with simple path without gas calculation.', async () => { + const expectedToTokensAmount = '2.051425'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.MATIC, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.USDT); + + const trade = await quickSwapProvider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(4); + expect(trade.path[0].address).toBe(TOKENS.MATIC.address); + expect(trade.path[1].address).toBe(TOKENS.QUICK.address); + expect(trade.path[2].address).toBe(TOKENS.USDC.address); + expect(trade.path[3].address).toBe(TOKENS.USDT.address); + }, 400_000); + + test('Must calculate correct ERC20-ERC20 trade with simple path without gas calculation.', async () => { + const expectedToTokensAmount = '263.134808'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.QUICK, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.USDC); + + const trade = await quickSwapProvider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.QUICK.address); + expect(trade.path[1].address).toBe(TOKENS.USDC.address); + }, 400_000); + + test('Must calculate correct ERC20-NATIVE trade with simple path without gas calculation.', async () => { + const expectedToTokensAmount = '127.513605898661444581'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.QUICK, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.MATIC); + + const trade = await quickSwapProvider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(3); + expect(trade.path[0].address).toBe(TOKENS.QUICK.address); + expect(trade.path[1].address).toBe(TOKENS.USDC.address); + expect(trade.path[2].address).toBe(TOKENS.MATIC.address); + }, 400_000); + + test('Must calculate correct NATIVE-ERC20 trade with simple path.', async () => { + const expectedToTokensAmount = '2.051425'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.MATIC, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.USDT); + + const trade = await quickSwapProvider.calculate(from, to, { + gasCalculation: 'calculate' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(4); + expect(trade.path[0].address).toBe(TOKENS.MATIC.address); + expect(trade.path[1].address).toBe(TOKENS.QUICK.address); + expect(trade.path[2].address).toBe(TOKENS.USDC.address); + expect(trade.path[3].address).toBe(TOKENS.USDT.address); + }, 400_000); + + test('Must calculate correct ERC20-ERC20 trade with simple path.', async () => { + const expectedToTokensAmount = '0.003783444491122842'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.QUICK); + + const trade = await quickSwapProvider.calculate(from, to, { + gasCalculation: 'calculate' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(3); + expect(trade.path[0].address).toBe(TOKENS.USDT.address); + expect(trade.path[1].address).toBe(TOKENS.WETH.address); + expect(trade.path[2].address).toBe(TOKENS.QUICK.address); + }, 400_000); + + test('Must calculate correct ERC20-NATIVE trade with simple path.', async () => { + const expectedToTokensAmount = '1488.913004591397373038'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.WETH, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.MATIC); + + const trade = await quickSwapProvider.calculate(from, to, { + gasCalculation: 'calculate' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.WETH.address); + expect(trade.path[1].address).toBe(TOKENS.MATIC.address); + }, 400_000); + + test('Must calculate correct NATIVE-ERC20 trade with simple path with rubic optimisation.', async () => { + const expectedToTokensAmount = '2.051886'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.MATIC, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.USDC); + + const trade = await quickSwapProvider.calculate(from, to, { + gasCalculation: 'rubicOptimisation' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(3); + expect(trade.path[0].address).toBe(TOKENS.MATIC.address); + expect(trade.path[1].address).toBe(TOKENS.QUICK.address); + expect(trade.path[2].address).toBe(TOKENS.USDC.address); + }, 400_000); + + test('Must calculate correct ERC20-ERC20 trade with simple path with rubic optimisation.', async () => { + const expectedToTokensAmount = '0.003788546445518352'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDC, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.QUICK); + + const trade = await quickSwapProvider.calculate(from, to, { + gasCalculation: 'rubicOptimisation' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(3); + expect(trade.path[0].address).toBe(TOKENS.USDC.address); + expect(trade.path[1].address).toBe(TOKENS.WETH.address); + expect(trade.path[2].address).toBe(TOKENS.QUICK.address); + }, 400_000); + + test('Must calculate correct ERC20-NATIVE trade with simple path with rubic optimisation.', async () => { + const expectedToTokensAmount = '0.485965481122745139'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.DAI, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.MATIC); + + const trade = await quickSwapProvider.calculate(from, to, { + gasCalculation: 'rubicOptimisation' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.DAI.address); + expect(trade.path[1].address).toBe(TOKENS.MATIC.address); + }, 400_000); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/sushi-swap/sushi-swap.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/sushi-swap/sushi-swap.ts new file mode 100644 index 0000000..62aa105 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/sushi-swap/sushi-swap.ts @@ -0,0 +1,202 @@ +import { Chain } from '__tests__/utils/chain'; +import { mockInjector } from '__tests__/utils/mock-injector'; +import { TOKENS as ALL_TOKENS } from '__tests__/utils/tokens'; +import { PriceTokenAmount } from 'src/common/tokens/price-token-amount'; +import { PriceToken } from 'src/common/tokens/price-token'; +import BigNumber from 'bignumber.js'; +import { SushiSwapPolygonProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/sushi-swap-polygon-provider'; +import { SushiSwapPolygonTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/sushi-swap-polygon-trade'; +import { SUSHI_SWAP_POLYGON_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/constants'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +const TOKENS = ALL_TOKENS[BLOCKCHAIN_NAME.POLYGON]; + +export const sushiSwapPolygonProviderSpec = () => { + describe('SushiSwap provider tests', () => { + let sushiSwapProvider: SushiSwapPolygonProvider; + + beforeEach(async () => { + sushiSwapProvider = new SushiSwapPolygonProvider(); + }); + + beforeEach(async () => { + const chain = await Chain.reset(BLOCKCHAIN_NAME.POLYGON); + const configuration = await chain.getConfiguration(); + await mockInjector(configuration); + }); + + test('Initialize values', () => { + expect(sushiSwapProvider.blockchain).toBe(BLOCKCHAIN_NAME.POLYGON); + expect(typeof sushiSwapProvider.UniswapV2TradeClass).toBe(typeof SushiSwapPolygonTrade); + expect(sushiSwapProvider.providerSettings).toBe( + SUSHI_SWAP_POLYGON_PROVIDER_CONFIGURATION + ); + }, 400_000); + + test('Must calculate correct NATIVE-ERC20 trade with simple path without gas calculation.', async () => { + const expectedToTokensAmount = '2.050733'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.MATIC, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.USDT); + + const trade = await sushiSwapProvider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(3); + expect(trade.path[0].address).toBe(TOKENS.MATIC.address); + expect(trade.path[1].address).toBe(TOKENS.USDC.address); + expect(trade.path[2].address).toBe(TOKENS.USDT.address); + }, 400_000); + + test('Must calculate correct ERC20-ERC20 trade with simple path without gas calculation.', async () => { + const expectedToTokensAmount = '159.954588'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.QUICK, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.USDC); + + const trade = await sushiSwapProvider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.QUICK.address); + expect(trade.path[1].address).toBe(TOKENS.USDC.address); + }, 400_000); + + test('Must calculate correct ERC20-NATIVE trade with simple path without gas calculation.', async () => { + const expectedToTokensAmount = '77.507430129172371211'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.QUICK, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.MATIC); + + const trade = await sushiSwapProvider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(3); + expect(trade.path[0].address).toBe(TOKENS.QUICK.address); + expect(trade.path[1].address).toBe(TOKENS.USDC.address); + expect(trade.path[2].address).toBe(TOKENS.MATIC.address); + }, 400_000); + + test('Must calculate correct NATIVE-ERC20 trade with simple path.', async () => { + const expectedToTokensAmount = '2.050733'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.MATIC, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.USDT); + + const trade = await sushiSwapProvider.calculate(from, to, { + gasCalculation: 'calculate' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(3); + expect(trade.path[0].address).toBe(TOKENS.MATIC.address); + expect(trade.path[1].address).toBe(TOKENS.USDC.address); + expect(trade.path[2].address).toBe(TOKENS.USDT.address); + }, 400_000); + + test('Must calculate correct ERC20-ERC20 trade with simple path.', async () => { + const expectedToTokensAmount = '0.003770130241669953'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.QUICK); + + const trade = await sushiSwapProvider.calculate(from, to, { + gasCalculation: 'calculate' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(3); + expect(trade.path[0].address).toBe(TOKENS.USDT.address); + expect(trade.path[1].address).toBe(TOKENS.WMATIC.address); + expect(trade.path[2].address).toBe(TOKENS.QUICK.address); + }, 400_000); + + test('Must calculate correct ERC20-NATIVE trade with simple path.', async () => { + const expectedToTokensAmount = '1488.721017828230077849'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.WETH, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.MATIC); + + const trade = await sushiSwapProvider.calculate(from, to, { + gasCalculation: 'calculate' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.WETH.address); + expect(trade.path[1].address).toBe(TOKENS.MATIC.address); + }, 400_000); + + test('Must calculate correct NATIVE-ERC20 trade with simple path with rubic optimisation.', async () => { + const expectedToTokensAmount = '2.051291'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.MATIC, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.USDC); + + const trade = await sushiSwapProvider.calculate(from, to, { + gasCalculation: 'rubicOptimisation' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.MATIC.address); + expect(trade.path[1].address).toBe(TOKENS.USDC.address); + }, 400_000); + + test('Must calculate correct ERC20-ERC20 trade with simple path with rubic optimisation.', async () => { + const expectedToTokensAmount = '0.003779917038690047'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDC, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.QUICK); + + const trade = await sushiSwapProvider.calculate(from, to, { + gasCalculation: 'rubicOptimisation' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.USDC.address); + expect(trade.path[1].address).toBe(TOKENS.QUICK.address); + }, 400_000); + + test('Must calculate correct ERC20-NATIVE trade with simple path with rubic optimisation.', async () => { + const expectedToTokensAmount = '0.486133316016377403'; // constant data about tokens rate in 13961175 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.DAI, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.MATIC); + + const trade = await sushiSwapProvider.calculate(from, to, { + gasCalculation: 'rubicOptimisation' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.DAI.address); + expect(trade.path[1].address).toBe(TOKENS.MATIC.address); + }, 400_000); + }); +}; diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/uni-swap-v3/uni-swap-v3-polygon-provider.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/uni-swap-v3/uni-swap-v3-polygon-provider.ts new file mode 100644 index 0000000..5276112 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/uni-swap-v3/uni-swap-v3-polygon-provider.ts @@ -0,0 +1,80 @@ +import { Chain } from '__tests__/utils/chain'; +import { mockInjector } from '__tests__/utils/mock-injector'; +import { TOKENS as ALL_TOKENS } from '__tests__/utils/tokens'; +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens/price-token-amount'; +import { PriceToken } from 'src/common/tokens/price-token'; +import { UniSwapV3PolygonProvider } from '@rsdk-features/instant-trades/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-provider'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +const TOKENS = ALL_TOKENS[BLOCKCHAIN_NAME.POLYGON]; + +export const uniswapV3PolygonProviderSpec = () => + describe('UnisSwap V3 Polygon provider tests', () => { + let uniswapV3Provider: UniSwapV3PolygonProvider; + + beforeAll(async () => { + uniswapV3Provider = new UniSwapV3PolygonProvider(); + }); + + beforeEach(async () => { + const chain = await Chain.reset(BLOCKCHAIN_NAME.POLYGON); + const configuration = await chain.getConfiguration(); + await mockInjector(configuration); + }); + + test('Must calculate correct NATIVE-ERC20 trade with simple path.', async () => { + const expectedToTokensAmount = '2.055903'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.MATIC, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.USDT); + + const trade = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.MATIC.address); + expect(trade.path[1].address).toBe(TOKENS.USDT.address); + }, 400_000); + + test('Must calculate correct ERC20-NATIVE trade with simple path.', async () => { + const expectedToTokensAmount = '0.487261802620573316'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.MATIC); + + const trade = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(3); + expect(trade.path[0].address).toBe(TOKENS.USDT.address); + expect(trade.path[1].address).toBe(TOKENS.DAI.address); + expect(trade.path[2].address).toBe(TOKENS.MATIC.address); + }, 400_000); + + test('Must calculate correct ERC20-ERC20 trade with simple path.', async () => { + const expectedToTokensAmount = '0.998641521554865859'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(1) + }); + const to = await PriceToken.createFromToken(TOKENS.DAI); + + const trade = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + + expect(trade.to.tokenAmount.isEqualTo(expectedToTokensAmount)).toBeTruthy(); + expect(trade.path.length).toBe(2); + expect(trade.path[0].address).toBe(TOKENS.USDT.address); + expect(trade.path[1].address).toBe(TOKENS.DAI.address); + }, 400_000); + }); diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/uni-swap-v3/uni-swap-v3-polygon-trade.ts b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/uni-swap-v3/uni-swap-v3-polygon-trade.ts new file mode 100644 index 0000000..6dd249f --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/dexes/polygon/uni-swap-v3/uni-swap-v3-polygon-trade.ts @@ -0,0 +1,158 @@ +import { Chain } from '__tests__/utils/chain'; +import { mockInjector } from '__tests__/utils/mock-injector'; +import { TOKENS as ALL_TOKENS } from '__tests__/utils/tokens'; +import { Utils } from '__tests__/unit-tests/features/swap/utils/utils'; +import { Injector } from 'src/core/injector/injector'; +import BigNumber from 'bignumber.js'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { PriceTokenAmount } from 'src/common/tokens/price-token-amount'; +import { PriceToken } from 'src/common/tokens/price-token'; +import { UniSwapV3PolygonProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-provider'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import fn = jest.fn; + +const TOKENS = ALL_TOKENS[BLOCKCHAIN_NAME.POLYGON]; + +export const uniswapV3PolygonTradeSpec = () => + describe('UniSwap V3 Polygon trade tests.', () => { + let chain: Chain; + let uniswapV3Provider: UniSwapV3PolygonProvider; + let web3Public: EvmWeb3Public; + let userAddress: string; + let utils: Utils; + + beforeAll(async () => { + uniswapV3Provider = new UniSwapV3PolygonProvider(); + }); + + beforeEach(async () => { + chain = await Chain.reset(BLOCKCHAIN_NAME.POLYGON); + const configuration = await chain.getConfiguration(); + await mockInjector(configuration); + web3Public = Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.POLYGON); + userAddress = Injector.web3PrivateService.getWeb3PrivateByBlockchain( + BLOCKCHAIN_NAME.POLYGON + ).address; + utils = new Utils(chain, web3Public); + }); + + test('Swap method must works with NATIVE-ERC20 trade', async () => { + const maticTokenAmountToSwap = 1; + const expectedToTokensAmount = '2.055903'; // constant data about tokens rate in 23571568 block + const maticBalanceBefore = await web3Public.getBalance(userAddress); + const usdtBalanceBefore = await web3Public.getBalance(userAddress, TOKENS.USDT.address); + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.MATIC, + tokenAmount: new BigNumber(maticTokenAmountToSwap) + }); + const to = await PriceToken.createFromToken(TOKENS.USDT); + + const trade = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + const transactionHash = await trade.swap(); + const transactionReceipt = await web3Public.getTransactionReceipt(transactionHash); + const maticBalanceAfter = await web3Public.getBalance(userAddress); + const usdtBalanceAfter = await web3Public.getBalance(userAddress, TOKENS.USDT.address); + const transactionFee = await utils.getTransactionFeeByReceipt(transactionReceipt); + + expect(transactionReceipt.status).toBeTruthy(); + expect( + maticBalanceAfter.isEqualTo( + maticBalanceBefore + .minus(maticTokenAmountToSwap * 10 ** from.decimals) + .minus(transactionFee) + ) + ).toBeTruthy(); + expect( + usdtBalanceAfter.isEqualTo( + usdtBalanceBefore.plus( + new BigNumber(expectedToTokensAmount).multipliedBy(10 ** to.decimals) + ) + ) + ).toBeTruthy(); + + const trade1 = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + expect(trade1.to.weiAmount.isEqualTo(trade.to.weiAmount)).not.toBeTruthy(); + }, 400_000); + + test('Swap method must works with ERC20-NATIVE trade', async () => { + const usdtTokenAmountToSwap = 1; + const expectedToTokensAmount = '0.487261802620573316'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(usdtTokenAmountToSwap) + }); + const to = await PriceToken.createFromToken(TOKENS.MATIC); + await chain.increaseTokensBalance(from, usdtTokenAmountToSwap, { inEtherUnits: true }); + const usdtBalanceBefore = await web3Public.getBalance(userAddress, from.address); + const maticBalanceBefore = await web3Public.getBalance(userAddress); + let approveTxHash = ''; + + const onApprove = fn(hash => (approveTxHash = hash)); + const trade = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + const transactionHash = await trade.swap({ onApprove }); + const transactionReceipt = await web3Public.getTransactionReceipt(transactionHash); + const usdtBalanceAfter = await web3Public.getBalance(userAddress, from.address); + const maticBalanceAfter = await web3Public.getBalance(userAddress); + const approveTransactionFee = await utils.getTransactionFeeByHash(approveTxHash); + const transactionFee = await utils.getTransactionFeeByReceipt(transactionReceipt); + + expect(transactionReceipt.status).toBeTruthy(); + expect( + usdtBalanceAfter.isEqualTo( + usdtBalanceBefore.minus(usdtTokenAmountToSwap * 10 ** from.decimals) + ) + ).toBeTruthy(); + + expect( + maticBalanceAfter.isEqualTo( + maticBalanceBefore + .plus(new BigNumber(expectedToTokensAmount).multipliedBy(10 ** to.decimals)) + .minus(approveTransactionFee) + .minus(transactionFee) + ) + ).toBeTruthy(); + expect(onApprove.mock.calls.length).toBe(1); + }, 400_000); + + test('Swap method must works with ERC20-ERC20 trade', async () => { + const usdtTokenAmountToSwap = 1; + const expectedToTokensAmount = '0.998641521554865859'; // constant data about tokens rate in 23571568 block + const from = await PriceTokenAmount.createFromToken({ + ...TOKENS.USDT, + tokenAmount: new BigNumber(usdtTokenAmountToSwap) + }); + const to = await PriceToken.createFromToken(TOKENS.DAI); + await chain.increaseTokensBalance(from, usdtTokenAmountToSwap, { inEtherUnits: true }); + const usdtBalanceBefore = await web3Public.getBalance(userAddress, from.address); + const daiBalanceBefore = await web3Public.getBalance(userAddress, to.address); + + const trade = await uniswapV3Provider.calculate(from, to, { + gasCalculation: 'disabled' + }); + const transactionHash = await trade.swap(); + const transactionReceipt = await web3Public.getTransactionReceipt(transactionHash); + const usdtBalanceAfter = await web3Public.getBalance(userAddress, from.address); + const daiBalanceAfter = await web3Public.getBalance(userAddress, to.address); + + expect(transactionReceipt.status).toBeTruthy(); + expect( + usdtBalanceAfter.isEqualTo( + usdtBalanceBefore.minus(usdtTokenAmountToSwap * 10 ** from.decimals) + ) + ).toBeTruthy(); + + expect( + daiBalanceAfter.isEqualTo( + daiBalanceBefore.plus( + new BigNumber(expectedToTokensAmount).multipliedBy(10 ** to.decimals) + ) + ) + ).toBeTruthy(); + }, 400_000); + }); diff --git a/SDK-mstr/__tests__/unit-tests/features/swap/utils/utils.ts b/SDK-mstr/__tests__/unit-tests/features/swap/utils/utils.ts new file mode 100644 index 0000000..44312f8 --- /dev/null +++ b/SDK-mstr/__tests__/unit-tests/features/swap/utils/utils.ts @@ -0,0 +1,23 @@ +import { TransactionReceipt } from 'web3-eth'; +import BigNumber from 'bignumber.js'; +import { Web3Public } from 'src/core/blockchain/web3-public-service/web3-public/web3-public'; +import { Chain } from '__tests__/utils/chain'; + +export class Utils { + constructor(private readonly chain: Chain, private readonly web3Public: Web3Public) {} + + async getTransactionFeeByReceipt(transactionReceipt: TransactionReceipt): Promise { + const transaction = (await this.web3Public.getTransactionByHash( + transactionReceipt.transactionHash + ))!; + return new BigNumber(transactionReceipt.gasUsed).multipliedBy(transaction.gasPrice); + } + + async getTransactionFeeByHash(transactionHash: string): Promise { + const transaction = (await this.web3Public.getTransactionByHash(transactionHash))!; + const transactionReceipt = (await this.chain.web3.eth.getTransactionReceipt( + transactionHash + ))!; + return new BigNumber(transactionReceipt.gasUsed).multipliedBy(transaction.gasPrice); + } +} diff --git a/SDK-mstr/__tests__/utils/accounts-from-mnemonic.ts b/SDK-mstr/__tests__/utils/accounts-from-mnemonic.ts new file mode 100644 index 0000000..0d78639 --- /dev/null +++ b/SDK-mstr/__tests__/utils/accounts-from-mnemonic.ts @@ -0,0 +1,17 @@ +import { mnemonicToSeedSync } from 'bip39'; +import { hdkey } from 'ethereumjs-wallet'; + +export function generateAccountsFromMnemonic(mnemonic: string, count: number) { + const seed = mnemonicToSeedSync(mnemonic); + const hdwallet = hdkey.fromMasterSeed(seed); + const walletHdpath = "m/44'/60'/0'/0/"; + + const accounts = []; + for (let i = 0; i < count; i++) { + const wallet = hdwallet.derivePath(walletHdpath + i).getWallet(); + const address = `0x${wallet.getAddress().toString('hex')}`; + const privateKey = wallet.getPrivateKey().toString('hex'); + accounts.push({ address, privateKey }); + } + return accounts; +} diff --git a/SDK-mstr/__tests__/utils/chain.ts b/SDK-mstr/__tests__/utils/chain.ts new file mode 100644 index 0000000..1176082 --- /dev/null +++ b/SDK-mstr/__tests__/utils/chain.ts @@ -0,0 +1,146 @@ +import { ERC20_TOKEN_ABI } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/erc-20-token-abi'; +import { generateAccountsFromMnemonic } from '__tests__/utils/accounts-from-mnemonic'; +import { + publicProvidersRPC, + publicProvidersSupportServerUrls +} from '__tests__/utils/configuration'; +import { DEFAULT_MNEMONIC } from '__tests__/utils/constants/mnemonic'; +import { TOKENS_HOLDERS } from '__tests__/utils/tokens'; +import axios from 'axios'; +import BigNumber from 'bignumber.js'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Configuration } from 'src/core/sdk/models/configuration'; +import * as util from 'util'; +import Web3 from 'web3'; +import { HttpProvider } from 'web3-core'; +import { Token } from 'src/common/tokens'; +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; + +export class Chain { + private static walletsNumber = 10; + + private static wallets = generateAccountsFromMnemonic(DEFAULT_MNEMONIC, Chain.walletsNumber); + + private static web3Instances = Object.fromEntries( + Object.entries(publicProvidersRPC).map(([key, value]) => { + const web3 = new Web3(value); + Chain.wallets.forEach(wallet => { + web3.eth.accounts.wallet.add(wallet.privateKey); + }); + return [key, web3]; + }) + ); + + private static get accounts(): string[] { + return Chain.wallets.map(wallet => wallet.address); + } + + public static async reset( + blockchainName: BlockchainName, + blockNumber?: number + ): Promise { + const rpcUrl = publicProvidersRPC[blockchainName]; + const resetUrl = publicProvidersSupportServerUrls[blockchainName]; + + if (!rpcUrl || !resetUrl) { + throw new Error(`RPC for ${blockchainName} was not set.`); + } + await axios.post(resetUrl, { blockNumber }); + + return new Chain(Chain.web3Instances[blockchainName], Chain.accounts); + } + + private constructor(public web3: Web3, public accounts: string[]) {} + + public async getConfiguration(): Promise { + return { + rpcProviders: Object.fromEntries( + Object.entries(publicProvidersRPC).map(([key, value]) => [ + key, + { + mainRpc: value + } + ]) + ), + walletProvider: { + [CHAIN_TYPE.EVM]: { + core: this.web3, + address: this.accounts[0] + } + } + }; + } + + public async increaseTokensBalance( + token: Token, + amount: number | string | BigNumber, + options: { inEtherUnits: boolean } = { inEtherUnits: false } + ): Promise { + const weiAmount = options.inEtherUnits + ? new BigNumber(amount).multipliedBy(10 ** token.decimals) + : new BigNumber(amount); + const holder = this.getTokenHolderAddress(token); + await this.setBalance(holder, 1, { + inEtherUnits: true + }); + + const tokenContract = new this.web3.eth.Contract(ERC20_TOKEN_ABI, token.address); + const holderBalance = new BigNumber(await tokenContract.methods.balanceOf(holder).call()); + if (holderBalance.lt(weiAmount)) { + throw new Error(`${ + token.symbol + } holder balance is not enough to transfer, set other holder to config. + Holder balance is ${holderBalance.toFixed()}, but ${weiAmount.toFixed( + 0 + )} is required.`); + } + + await this.impersonateAccount(holder); + await tokenContract.methods + .transfer(this.accounts[0], weiAmount.toFixed(0)) + .send({ from: holder, gas: 100_000 }); + await this.stopImpersonateAccount(holder); + } + + private getTokenHolderAddress(token: Token): string { + const holder = + TOKENS_HOLDERS[token.blockchain as keyof typeof TOKENS_HOLDERS]?.[token.address]; + if (!holder) { + throw new Error(`Holder for ${token.symbol} is not specified.`); + } + return holder; + } + + public async impersonateAccount(address: string): Promise { + await this.sendRpcRequest('hardhat_impersonateAccount', [address]); + } + + public async stopImpersonateAccount(address: string): Promise { + await this.sendRpcRequest('hardhat_stopImpersonatingAccount', [address]); + } + + public async setBalance( + address: string, + value: number | string | BigNumber, + options: { inEtherUnits: boolean } = { inEtherUnits: false } + ): Promise { + const bnValue = options.inEtherUnits + ? new BigNumber(value).multipliedBy(10 ** 18) + : new BigNumber(value); + + const hexValue = `0x${bnValue.toString(16)}`; + + await this.sendRpcRequest('hardhat_setBalance', [address, hexValue]); + } + + private async sendRpcRequest(method: string, params: string[]): Promise { + await util.promisify( + (this.web3.currentProvider as HttpProvider).send.bind(this.web3.currentProvider) + )({ + method, + params, + jsonrpc: '2.0', + id: new Date().getTime() + }); + } +} diff --git a/SDK-mstr/__tests__/utils/configuration.ts b/SDK-mstr/__tests__/utils/configuration.ts new file mode 100644 index 0000000..8ea7575 --- /dev/null +++ b/SDK-mstr/__tests__/utils/configuration.ts @@ -0,0 +1,29 @@ +import { BLOCKCHAIN_NAME, BlockchainName } from '../../src/core/blockchain/models/blockchain-name'; +import { Configuration } from '../../src/core/sdk/models/configuration'; + +const baseRpcUrl = 'http://localhost'; + +const addPort = (port: number) => `${baseRpcUrl}:${port}`; + +export const publicProvidersRPC: Partial> = { + [BLOCKCHAIN_NAME.ETHEREUM]: addPort(8545), + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: addPort(8546), + [BLOCKCHAIN_NAME.POLYGON]: addPort(8547) +}; + +export const publicProvidersSupportServerUrls: Partial> = { + [BLOCKCHAIN_NAME.ETHEREUM]: addPort(1545), + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: addPort(1546), + [BLOCKCHAIN_NAME.POLYGON]: addPort(1547) +}; + +export const minimalConfiguration: Configuration = { + rpcProviders: Object.fromEntries( + Object.entries(publicProvidersRPC).map(([key, value]) => [ + key, + { + mainRpc: value + } + ]) + ) +}; diff --git a/SDK-mstr/__tests__/utils/constants/free-rpc.ts b/SDK-mstr/__tests__/utils/constants/free-rpc.ts new file mode 100644 index 0000000..404893a --- /dev/null +++ b/SDK-mstr/__tests__/utils/constants/free-rpc.ts @@ -0,0 +1,29 @@ +import { + BLOCKCHAIN_NAME, + EvmBlockchainName, + TronBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { TronWebProvider } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-web-provider'; +import { RpcProvider } from 'src/core/sdk/models/rpc-provider'; + +export const freeRpc: Partial< + Record> & + Record> +> = { + [BLOCKCHAIN_NAME.ETHEREUM]: { + rpcList: ['https://rpc.ankr.com/eth'] + }, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: { + rpcList: ['https://bsc-dataseed.binance.org'] + }, + [BLOCKCHAIN_NAME.POLYGON]: { + rpcList: ['https://polygon-rpc.com'] + }, + [BLOCKCHAIN_NAME.TRON]: { + rpcList: [ + { + fullHost: 'https://rpc.ankr.com/http/tron' + } + ] + } +}; diff --git a/SDK-mstr/__tests__/utils/constants/mnemonic.ts b/SDK-mstr/__tests__/utils/constants/mnemonic.ts new file mode 100644 index 0000000..b65e332 --- /dev/null +++ b/SDK-mstr/__tests__/utils/constants/mnemonic.ts @@ -0,0 +1 @@ +export const DEFAULT_MNEMONIC = 'test test test test test test test test test test test junk'; diff --git a/SDK-mstr/__tests__/utils/mock-injector.ts b/SDK-mstr/__tests__/utils/mock-injector.ts new file mode 100644 index 0000000..ac3f1c9 --- /dev/null +++ b/SDK-mstr/__tests__/utils/mock-injector.ts @@ -0,0 +1,18 @@ +import { DefaultHttpClient } from 'src/core/http-client/default-http-client'; +import { Web3PrivateService } from 'src/core/blockchain/web3-private-service/web3-private-service'; +import { Web3PublicService } from 'src/core/blockchain/web3-public-service/web3-public-service'; +import { Injector } from 'src/core/injector/injector'; +import { Configuration } from 'src/core/sdk/models/configuration'; + +export async function mockInjector(configuration: Configuration): Promise { + const web3PublicService = new Web3PublicService(configuration.rpcProviders); + const web3PrivateService = new Web3PrivateService(configuration.walletProvider); + const httpClient = configuration.httpClient || (await DefaultHttpClient.getInstance()); + + Injector.createInjector(web3PublicService, web3PrivateService, httpClient); +} + +export function mockEmptyInjector(): void { + // @ts-ignore + Injector.createInjector(null, null, null); +} diff --git a/SDK-mstr/__tests__/utils/models/global.ts b/SDK-mstr/__tests__/utils/models/global.ts new file mode 100644 index 0000000..86d24fe --- /dev/null +++ b/SDK-mstr/__tests__/utils/models/global.ts @@ -0,0 +1,7 @@ +import { BlockchainName } from 'src/core'; + +export interface Global { + sdkEnv: { + providers: Record; + }; +} diff --git a/SDK-mstr/__tests__/utils/tokens.ts b/SDK-mstr/__tests__/utils/tokens.ts new file mode 100644 index 0000000..30f2c74 --- /dev/null +++ b/SDK-mstr/__tests__/utils/tokens.ts @@ -0,0 +1,111 @@ +import { BLOCKCHAIN_NAME, BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Token } from 'src/common/tokens'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; + +export const TOKENS = { + [BLOCKCHAIN_NAME.ETHEREUM]: { + ETH: new Token({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM, + address: EvmWeb3Pure.nativeTokenAddress, + decimals: 18, + symbol: 'ETH', + name: 'Ethereum' + }), + USDT: new Token({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM, + address: '0xdac17f958d2ee523a2206206994597c13d831ec7', + decimals: 6, + symbol: 'USDT', + name: 'Tether USD' + }), + RBC: new Token({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM, + address: '0xa4eed63db85311e22df4473f87ccfc3dadcfa3e3', + decimals: 18, + symbol: 'RBC', + name: 'Rubic' + }), + WETH: new Token({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM, + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + decimals: 18, + symbol: 'WETH', + name: 'Wrapped Ether' + }), + DAI: new Token({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM, + address: '0x6b175474e89094c44da98b954eedeac495271d0f', + decimals: 18, + symbol: 'DAI', + name: 'Dai Stablecoin' + }), + WBTC: new Token({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM, + address: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + decimals: 8, + symbol: 'WBTC', + name: 'Wrapped BTC' + }) + }, + [BLOCKCHAIN_NAME.POLYGON]: { + MATIC: new Token({ + blockchain: BLOCKCHAIN_NAME.POLYGON, + address: EvmWeb3Pure.nativeTokenAddress, + decimals: 18, + symbol: 'MAT', + name: 'Matoc' + }), + WMATIC: new Token({ + blockchain: BLOCKCHAIN_NAME.POLYGON, + address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', + decimals: 18, + symbol: 'WMATIC', + name: 'Wrapped Matic' + }), + WETH: new Token({ + blockchain: BLOCKCHAIN_NAME.POLYGON, + address: '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619', + decimals: 18, + symbol: 'WETH', + name: 'Wrapped Ether' + }), + USDT: new Token({ + blockchain: BLOCKCHAIN_NAME.POLYGON, + address: '0xc2132d05d31c914a87c6611c10748aeb04b58e8f', + decimals: 6, + symbol: 'USDT', + name: '(PoS) Tether USD' + }), + DAI: new Token({ + blockchain: BLOCKCHAIN_NAME.POLYGON, + address: '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', + decimals: 18, + symbol: 'DAI', + name: '(PoS) Dai Stablecoin' + }), + USDC: new Token({ + blockchain: BLOCKCHAIN_NAME.POLYGON, + address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', + decimals: 6, + symbol: 'USDC', + name: 'USD Coin (PoS)' + }), + QUICK: new Token({ + blockchain: BLOCKCHAIN_NAME.POLYGON, + address: '0x831753DD7087CaC61aB5644b308642cc1c33Dc13', + decimals: 18, + symbol: 'QUICK', + name: 'Quickswap' + }) + } +}; + +export const TOKENS_HOLDERS: Partial>> = { + [BLOCKCHAIN_NAME.ETHEREUM]: { + '0xa4eed63db85311e22df4473f87ccfc3dadcfa3e3': '0x0541f3300307984984a587aeb7c34139e19124fa', // RBC + '0xdac17f958d2ee523a2206206994597c13d831ec7': '0x5754284f345afc66a98fbb0a0afe71e0f007b949' // USDT + }, + [BLOCKCHAIN_NAME.POLYGON]: { + '0xc2132d05d31c914a87c6611c10748aeb04b58e8f': '0xdac17f958d2ee523a2206206994597c13d831ec7' // USDT + } +}; diff --git a/SDK-mstr/docker-compose.yml b/SDK-mstr/docker-compose.yml new file mode 100644 index 0000000..d6a598b --- /dev/null +++ b/SDK-mstr/docker-compose.yml @@ -0,0 +1,16 @@ +version: "3.7" + +services: + eth-node: + image: ottebrut/rubic-sdk-tools:eth-node-1.2 + container_name: eth-node + ports: + - 8545:8545 + - 1545:1545 + polygon-node: + image: ottebrut/rubic-sdk-tools:polygon-node-1.2 + container_name: polygon-node + ports: + - 8547:8545 + - 1547:1545 + diff --git a/SDK-mstr/jest.config.js b/SDK-mstr/jest.config.js new file mode 100644 index 0000000..a8290c7 --- /dev/null +++ b/SDK-mstr/jest.config.js @@ -0,0 +1,25 @@ +module.exports = { + preset: 'ts-jest', + roots: [ + "src", "__tests__" + ], + moduleDirectories: ['node_modules', '/src'], + testEnvironment: 'node', + transform: { + "node_modules/(ethereum-cryptography)/.+\\.(j|t)sx?$": "ts-jest", + "node_modules/@layerzerolabs/.+\\.(j|t)sx?$": "ts-jest" + }, + transformIgnorePatterns: [ 'node_modules/((?!ethereum-cryptography)/.*)', "node_modules/(?!@layerzerolabs/.*)"], + testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"], + globals: { + 'ts-jest': { + tsconfig: 'tsconfig.test.json', + useESM: true + } + }, + moduleNameMapper: { + '^src/(.*)$': '/src/$1', + '__tests__/(.*)$': '/__tests__/$1', + }, + testTimeout: 300_000 +}; diff --git a/SDK-mstr/package.json b/SDK-mstr/package.json new file mode 100644 index 0000000..12deb6e --- /dev/null +++ b/SDK-mstr/package.json @@ -0,0 +1,155 @@ +{ + "name": "rubic-sdk", + "version": "5.26.2", + "description": "Simplify dApp creation", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "files": [ + "lib", + "dist" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/Cryptorubic/rubic-sdk.git" + }, + "homepage": "https://github.com/Cryptorubic/rubic-sdk", + "bugs": { + "url": "https://github.com/Cryptorubic/rubic-sdk/issues" + }, + "keywords": [ + "Ethereum", + "Rubic", + "Cross-chain", + "Multi-Chain", + "SDK", + "dApp", + "DEX", + "Polygon", + "Binance-Smart-Chain" + ], + "engines": { + "node": ">=16" + }, + "author": "rubic.exchange", + "authors": [ + { + "name": "Sergey Andreev", + "email": "andreev@mywish.io", + "homepage": "https://github.com/siandreev" + }, + { + "name": "Andrey Ott", + "email": "ott@mywish.io", + "url": "https://github.com/ottebrut" + }, + { + "name": "Sleta Dmitry", + "email": "sleta@rubic.finance", + "url": "https://github.com/axtezy" + }, + { + "name": "Kolebaev Vladislav", + "email": "ko1ebayev.worx@gmail.com", + "url": "https://github.com/ko1ebayev" + } + ], + "license": "GPL-3.0", + "scripts": { + "build": "yarn create-index && webpack && yarn delete-index", + "start": "webpack --watch", + "compile": "yarn create-index && npx rimraf lib && tsc --project tsconfig.json && tscpaths -p ./tsconfig.json -s ./src -o ./lib && yarn delete-index", + "lint": "eslint src __tests__", + "create-index": "cti entrypoint ./src --withoutbackup && renamer -f \"/^entrypoint/\" -r \"index\" \"./src/*\" --force", + "delete-index": "npx rimraf ./src/index.ts", + "test": "cd ./scripts && bash test-runner.sh", + "test:unit": "yarn jest ./__tests__/unit-tests", + "build:publish": "yarn compile && yarn build && npm publish --access public", + "analyze": "webpack --profile --json > stats.json && webpack-bundle-analyzer stats.json", + "docs": "yarn create-index && typedoc && yarn delete-index", + "publish": "yarn publish --access public", + "publish:alpha": "yarn publish --access public --tag alpha" + }, + "dependencies": { + "@1inch/limit-order-protocol-utils": "3.0.1", + "@arbitrum/sdk": "^3.1.3", + "@debridge-finance/debridge-external-call": "^1.0.7", + "@layerzerolabs/scan-client": "^0.0.0-beta.6", + "@noble/secp256k1": "^1.7.1", + "@pancakeswap/sdk": "^5.1.0", + "@pancakeswap/smart-router": "4.2.1", + "@pancakeswap/swap-sdk-core": "^1.0.0", + "@pancakeswap/tokens": "0.1.6", + "@solana/web3.js": "^1.89.1", + "@solflare-wallet/utl-sdk": "^1.4.0", + "@viaprotocol/router-sdk": "^1.0.7", + "assert": "^2.0.0", + "axios": "^0.26.1", + "bignumber.js": "9.1.0", + "bitcoin-address-validation": "2.2.1", + "browserify-zlib": "^0.2.0", + "cbridge-revert-manager": "1.1.0", + "crc32": "^0.2.2", + "ethers": "^5.6.8", + "graphql-request": "^6.1.0", + "grpc-web": "^1.4.2", + "iziswap-sdk": "1.4.12", + "lodash.clonedeep": "^4.5.0", + "rxjs": "7.8.1", + "tronweb": "^5.3.2", + "viem": "^1.3.1", + "web3": "^1.8.1" + }, + "devDependencies": { + "@babel/core": "^7.0.0-0", + "@types/jest": "^29.2.3", + "@types/lodash.clonedeep": "^4.5.7", + "@typescript-eslint/eslint-plugin": "^5.61.0", + "@typescript-eslint/parser": "^5.36.2", + "bip39": "^3.0.4", + "cancelable-promise": "^4.2.1", + "commander": "^8.3.0", + "compression-webpack-plugin": "^9.2.0", + "create-ts-index": "^1.14.0", + "crypto-browserify": "^3.12.0", + "delay": "^5.0.0", + "eslint": "8.22.0", + "eslint-config-airbnb-typescript": "^17.0.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-prettier": "^3.4.0", + "eslint-plugin-simple-import-sort": "^8.0.0", + "eslint-plugin-unused-imports": "^1.1.4", + "ethereumjs-wallet": "^1.0.2", + "http-browserify": "^1.7.0", + "https-browserify": "^1.0.0", + "jest": "^29.3.1", + "jest-mock-promise": "^2.0.2", + "prettier": "^2.2.1", + "querystring-es3": "^0.2.1", + "renamer": "^4.0.0", + "rimraf": "^3.0.2", + "stream-browserify": "^3.0.0", + "terser-webpack-plugin": "^5.3.0", + "ts-essentials": "^9.0.0", + "ts-jest": "^29.0.3", + "ts-loader": "^9.3.0", + "ts-patch": "^3.0.1", + "tsconfig-paths-webpack-plugin": "^3.5.2", + "tscpaths": "^0.0.9", + "typedoc": "^0.23.21", + "typescript": "^5.1.6", + "typescript-transform-paths": "^3.4.6", + "url": "^0.11.0", + "webpack": "^5.65.0", + "webpack-bundle-analyzer": "^4.5.0", + "webpack-cli": "^4.9.1" + }, + "overrides": { + "ethers": { + "bignumber.js": "9.1.0" + }, + "symbiosis-js-sdk": { + "bignumber.js": "9.1.0" + } + } +} diff --git a/SDK-mstr/scripts/build-env.js b/SDK-mstr/scripts/build-env.js new file mode 100644 index 0000000..ba7e2d4 --- /dev/null +++ b/SDK-mstr/scripts/build-env.js @@ -0,0 +1,31 @@ +const fs = require('fs'); +const commander = require('commander'); +const program = new commander.Command(); + +program + .option('--eth ') + .option('--polygon ') + .parse(); + +const options = program.opts(); + +if (!options.eth || !options.polygon) { + throw new Error('You must pass providers for all networks.'); +} + +const config = ` +global.sdkEnv = { + providers: { + ETH: { + jsonRpcUrl: '${options.eth}', + blockNumber: 13961175 + }, + POLYGON: { + jsonRpcUrl: '${options.polygon}', + blockNumber: 23571568 + } + } +} +`; + +fs.writeFileSync('./__tests__/env.js', config); diff --git a/SDK-mstr/scripts/test-runner.sh b/SDK-mstr/scripts/test-runner.sh new file mode 100644 index 0000000..681127a --- /dev/null +++ b/SDK-mstr/scripts/test-runner.sh @@ -0,0 +1,19 @@ +rm node.log 2> /dev/null + +cd .. + +docker compose up &> scripts/node.log & + +echo 'See node logs in scripts/node.log' + +while ! ( grep -q 'Account #19:' scripts/node.log ) + do + sleep 2 + echo "Waiting for node..." + done + +sleep 1 + +jest + +docker compose down diff --git a/SDK-mstr/src/common/errors/blockchain/deflation-token.error.ts b/SDK-mstr/src/common/errors/blockchain/deflation-token.error.ts new file mode 100644 index 0000000..665dccd --- /dev/null +++ b/SDK-mstr/src/common/errors/blockchain/deflation-token.error.ts @@ -0,0 +1,13 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; +import { Token } from 'src/common/tokens'; + +/** + * Thrown, if token has deflation. + */ +export class DeflationTokenError extends RubicSdkError { + constructor(public readonly token: Token, public readonly deflationPercent: BigNumber) { + super(`Token ${token.address} has ${deflationPercent.dp(2).toFixed()}% deflation`); + Object.setPrototypeOf(this, DeflationTokenError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/blockchain/healthcheck.error.ts b/SDK-mstr/src/common/errors/blockchain/healthcheck.error.ts new file mode 100644 index 0000000..35c7cb0 --- /dev/null +++ b/SDK-mstr/src/common/errors/blockchain/healthcheck.error.ts @@ -0,0 +1,12 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * @internal + * Thrown, if rpc provider has not passed healthcheck. + */ +export class HealthcheckError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, HealthcheckError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/blockchain/index.ts b/SDK-mstr/src/common/errors/blockchain/index.ts new file mode 100644 index 0000000..b2ad547 --- /dev/null +++ b/SDK-mstr/src/common/errors/blockchain/index.ts @@ -0,0 +1,8 @@ +export * from './deflation-token.error'; +export * from './healthcheck.error'; +export * from './invalid-address.error'; +export * from './low-gas.error'; +export * from './transaction-reverted.error'; +export * from './user-reject.error'; +export * from './wrong-from-address.error'; +export * from './wrong-receiver-address.error'; diff --git a/SDK-mstr/src/common/errors/blockchain/invalid-address.error.ts b/SDK-mstr/src/common/errors/blockchain/invalid-address.error.ts new file mode 100644 index 0000000..2d82618 --- /dev/null +++ b/SDK-mstr/src/common/errors/blockchain/invalid-address.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when passed wallet address is invalid for {@link chainType}. + */ +export class InvalidAddressError extends RubicSdkError { + constructor(public readonly address: string) { + super(); + Object.setPrototypeOf(this, InvalidAddressError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/blockchain/low-gas.error.ts b/SDK-mstr/src/common/errors/blockchain/low-gas.error.ts new file mode 100644 index 0000000..7d8dd63 --- /dev/null +++ b/SDK-mstr/src/common/errors/blockchain/low-gas.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when gas price is too low. + */ +export class LowGasError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, LowGasError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/blockchain/transaction-reverted.error.ts b/SDK-mstr/src/common/errors/blockchain/transaction-reverted.error.ts new file mode 100644 index 0000000..bf8ef45 --- /dev/null +++ b/SDK-mstr/src/common/errors/blockchain/transaction-reverted.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, if transaction was reverted without specified error. + */ +export class TransactionRevertedError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, TransactionRevertedError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/blockchain/tron-insufficient-native-balance.ts b/SDK-mstr/src/common/errors/blockchain/tron-insufficient-native-balance.ts new file mode 100644 index 0000000..a740b39 --- /dev/null +++ b/SDK-mstr/src/common/errors/blockchain/tron-insufficient-native-balance.ts @@ -0,0 +1,13 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, if transaction was reverted because of insufficient native balance. + */ +export class TronInsufficientNativeBalance extends RubicSdkError { + constructor() { + super( + 'Insufficient funds of native token. Decrease swap amount or increase native tokens balance.' + ); + Object.setPrototypeOf(this, TronInsufficientNativeBalance.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/blockchain/tron-transaction-expired.ts b/SDK-mstr/src/common/errors/blockchain/tron-transaction-expired.ts new file mode 100644 index 0000000..862310a --- /dev/null +++ b/SDK-mstr/src/common/errors/blockchain/tron-transaction-expired.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, if transaction signing in wallet expired. + */ +export class TronTransactionExpired extends RubicSdkError { + constructor() { + super('Signing timeout expired. Please, try again.'); + Object.setPrototypeOf(this, TronTransactionExpired.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/blockchain/user-reject.error.ts b/SDK-mstr/src/common/errors/blockchain/user-reject.error.ts new file mode 100644 index 0000000..5d11eb9 --- /dev/null +++ b/SDK-mstr/src/common/errors/blockchain/user-reject.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when user cancels transaction. + */ +export class UserRejectError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, UserRejectError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/blockchain/wrong-from-address.error.ts b/SDK-mstr/src/common/errors/blockchain/wrong-from-address.error.ts new file mode 100644 index 0000000..a426cf8 --- /dev/null +++ b/SDK-mstr/src/common/errors/blockchain/wrong-from-address.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when passed wrong from address in `encode` function. + */ +export class WrongFromAddressError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, WrongFromAddressError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/blockchain/wrong-receiver-address.error.ts b/SDK-mstr/src/common/errors/blockchain/wrong-receiver-address.error.ts new file mode 100644 index 0000000..be93c99 --- /dev/null +++ b/SDK-mstr/src/common/errors/blockchain/wrong-receiver-address.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when passed wrong receiver address. + */ +export class WrongReceiverAddressError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, WrongReceiverAddressError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/cross-chain/cross-chain-is-unavailable.error.ts b/SDK-mstr/src/common/errors/cross-chain/cross-chain-is-unavailable.error.ts new file mode 100644 index 0000000..b71d560 --- /dev/null +++ b/SDK-mstr/src/common/errors/cross-chain/cross-chain-is-unavailable.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when cross-chain contracts are on pause or there is not enough crypto balance. + */ +export class CrossChainIsUnavailableError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, CrossChainIsUnavailableError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/cross-chain/index.ts b/SDK-mstr/src/common/errors/cross-chain/index.ts new file mode 100644 index 0000000..849caf6 --- /dev/null +++ b/SDK-mstr/src/common/errors/cross-chain/index.ts @@ -0,0 +1,6 @@ +export * from './cross-chain-is-unavailable.error'; +export * from './insufficient-funds-gas-price-value.error'; +export * from './low-to-slippage.error'; +export * from './max-gas-price-overflow.error'; +export * from './too-low-amount.error'; +export * from './unsupported-receiver-address.error'; diff --git a/SDK-mstr/src/common/errors/cross-chain/insufficient-funds-gas-price-value.error.ts b/SDK-mstr/src/common/errors/cross-chain/insufficient-funds-gas-price-value.error.ts new file mode 100644 index 0000000..ff816d4 --- /dev/null +++ b/SDK-mstr/src/common/errors/cross-chain/insufficient-funds-gas-price-value.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when user doesn't have enough native token balance for gas fee plus `value`. + */ +export class InsufficientFundsGasPriceValueError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, InsufficientFundsGasPriceValueError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/cross-chain/low-to-slippage.error.ts b/SDK-mstr/src/common/errors/cross-chain/low-to-slippage.error.ts new file mode 100644 index 0000000..9866fa7 --- /dev/null +++ b/SDK-mstr/src/common/errors/cross-chain/low-to-slippage.error.ts @@ -0,0 +1,6 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when toSlippage tolerance is too low to calculate Celer trade. + */ +export class LowToSlippageError extends RubicSdkError {} diff --git a/SDK-mstr/src/common/errors/cross-chain/max-gas-price-overflow.error.ts b/SDK-mstr/src/common/errors/cross-chain/max-gas-price-overflow.error.ts new file mode 100644 index 0000000..686bfed --- /dev/null +++ b/SDK-mstr/src/common/errors/cross-chain/max-gas-price-overflow.error.ts @@ -0,0 +1,12 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when current gas price is higher, than max gas price on cross-chain contract + * in target network. + */ +export class MaxGasPriceOverflowError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, MaxGasPriceOverflowError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/cross-chain/too-low-amount.error.ts b/SDK-mstr/src/common/errors/cross-chain/too-low-amount.error.ts new file mode 100644 index 0000000..c6b6499 --- /dev/null +++ b/SDK-mstr/src/common/errors/cross-chain/too-low-amount.error.ts @@ -0,0 +1,13 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when amount of tokens don't cover provider's fee + */ +export class TooLowAmountError extends RubicSdkError { + constructor() { + super( + "The swap can't be executed with the entered amount of tokens. Please change it to the greater amount." + ); + Object.setPrototypeOf(this, TooLowAmountError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/cross-chain/unsupported-receiver-address.error.ts b/SDK-mstr/src/common/errors/cross-chain/unsupported-receiver-address.error.ts new file mode 100644 index 0000000..23ac978 --- /dev/null +++ b/SDK-mstr/src/common/errors/cross-chain/unsupported-receiver-address.error.ts @@ -0,0 +1,8 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +export class UnsupportedReceiverAddressError extends RubicSdkError { + constructor() { + super('This provider doesn’t support the receiver address'); + Object.setPrototypeOf(this, UnsupportedReceiverAddressError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/cross-chain/updated-rates-error.ts b/SDK-mstr/src/common/errors/cross-chain/updated-rates-error.ts new file mode 100644 index 0000000..9cf4897 --- /dev/null +++ b/SDK-mstr/src/common/errors/cross-chain/updated-rates-error.ts @@ -0,0 +1,12 @@ +import { RubicSdkError } from 'src/common/errors'; + +/** + * Thrown, when current gas price is higher, than max gas price on cross-chain contract + * in target network. + */ +export class UpdatedRatesError extends RubicSdkError { + constructor(public readonly oldAmount: string, public readonly newAmount: string) { + super(); + Object.setPrototypeOf(this, UpdatedRatesError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/index.ts b/SDK-mstr/src/common/errors/index.ts new file mode 100644 index 0000000..1fa0906 --- /dev/null +++ b/SDK-mstr/src/common/errors/index.ts @@ -0,0 +1,5 @@ +export * from './blockchain'; +export * from './cross-chain'; +export * from './rubic-sdk.error'; +export * from './swap'; +export * from './utils'; diff --git a/SDK-mstr/src/common/errors/on-chain/index.ts b/SDK-mstr/src/common/errors/on-chain/index.ts new file mode 100644 index 0000000..9c2b203 --- /dev/null +++ b/SDK-mstr/src/common/errors/on-chain/index.ts @@ -0,0 +1 @@ +export * from './on-chain-is-unavailable.error'; diff --git a/SDK-mstr/src/common/errors/on-chain/on-chain-is-unavailable.error.ts b/SDK-mstr/src/common/errors/on-chain/on-chain-is-unavailable.error.ts new file mode 100644 index 0000000..94ff34f --- /dev/null +++ b/SDK-mstr/src/common/errors/on-chain/on-chain-is-unavailable.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when on-chain contracts are on pause. + */ +export class OnChainIsUnavailableError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, OnChainIsUnavailableError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/proxy/unapproved-contract-error.ts b/SDK-mstr/src/common/errors/proxy/unapproved-contract-error.ts new file mode 100644 index 0000000..b1c25cf --- /dev/null +++ b/SDK-mstr/src/common/errors/proxy/unapproved-contract-error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors'; + +/** + * Thrown, when contract is not whitelisted on proxy contract. + */ +export class UnapprovedContractError extends RubicSdkError { + constructor(public readonly method: string, public readonly contract: string) { + super(); + Object.setPrototypeOf(this, UnapprovedContractError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/proxy/unapproved-method-error.ts b/SDK-mstr/src/common/errors/proxy/unapproved-method-error.ts new file mode 100644 index 0000000..47ed925 --- /dev/null +++ b/SDK-mstr/src/common/errors/proxy/unapproved-method-error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors'; + +/** + * Thrown, when method is not whitelisted on proxy contract. + */ +export class UnapprovedMethodError extends RubicSdkError { + constructor(public readonly method: string, public readonly contract: string) { + super(); + Object.setPrototypeOf(this, UnapprovedMethodError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/rubic-sdk.error.ts b/SDK-mstr/src/common/errors/rubic-sdk.error.ts new file mode 100644 index 0000000..53d865a --- /dev/null +++ b/SDK-mstr/src/common/errors/rubic-sdk.error.ts @@ -0,0 +1,9 @@ +/** + * Base class for all errors that can be thrown in sdk. + */ +export class RubicSdkError extends Error { + constructor(message?: string, errorPotions?: ErrorOptions) { + super(message, errorPotions); + Object.setPrototypeOf(this, RubicSdkError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/bridgers-pair-is-unavailable.error.ts b/SDK-mstr/src/common/errors/swap/bridgers-pair-is-unavailable.error.ts new file mode 100644 index 0000000..9207997 --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/bridgers-pair-is-unavailable.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when `quote` request in bridgers is failed. + */ +export class BridgersPairIsUnavailableError extends RubicSdkError { + constructor() { + super('The swap using this pair is currently unavailable. Please try again later.'); + Object.setPrototypeOf(this, BridgersPairIsUnavailableError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/failed-to-check-for-transaction-receipt.error.ts b/SDK-mstr/src/common/errors/swap/failed-to-check-for-transaction-receipt.error.ts new file mode 100644 index 0000000..efc0f36 --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/failed-to-check-for-transaction-receipt.error.ts @@ -0,0 +1,12 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * @internal + * Thrown, when transaction is passed, but web3 cannot retrieve transaction receipt. + */ +export class FailedToCheckForTransactionReceiptError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, FailedToCheckForTransactionReceiptError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/index.ts b/SDK-mstr/src/common/errors/swap/index.ts new file mode 100644 index 0000000..180e217 --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/index.ts @@ -0,0 +1,17 @@ +export * from './bridgers-pair-is-unavailable.error'; +export * from './failed-to-check-for-transaction-receipt.error'; +export * from './insufficient-funds.error'; +export * from './insufficient-funds-oneinch.error'; +export * from './insufficient-liquidity.error'; +export * from './lifi-pair-is-unavailable.error'; +export * from './low-slippage.error'; +export * from './low-slippage-deflationary-token.error'; +export * from './max-amount.error'; +export * from './min-amount.error'; +export * from './not-supported-blockchain'; +export * from './not-supported-tokens.error'; +export * from './not-whitelisted-provider.error'; +export * from './swap-request.error'; +export * from './unnecessary-approve.error'; +export * from './wallet-not-connected.error'; +export * from './wrong-network.error'; diff --git a/SDK-mstr/src/common/errors/swap/insufficient-funds-oneinch.error.ts b/SDK-mstr/src/common/errors/swap/insufficient-funds-oneinch.error.ts new file mode 100644 index 0000000..115cc35 --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/insufficient-funds-oneinch.error.ts @@ -0,0 +1,12 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +/** + * Thrown by 1inch, if user doesn't have enough balance. + */ +export class InsufficientFundsOneinchError extends RubicSdkError { + constructor(public readonly blockchain: BlockchainName) { + super(); + Object.setPrototypeOf(this, InsufficientFundsOneinchError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/insufficient-funds.error.ts b/SDK-mstr/src/common/errors/swap/insufficient-funds.error.ts new file mode 100644 index 0000000..03d310a --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/insufficient-funds.error.ts @@ -0,0 +1,22 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; +import { Token } from 'src/common/tokens'; + +/** + * Thrown, when user doesn't have enough balance. + */ +export class InsufficientFundsError extends RubicSdkError { + /** + * @param token Token to swap. + * @param balance Token's balance on user wallet in Eth units. + * @param requiredBalance Required token's amount to swap in Eth units. + */ + constructor( + public readonly token: Token, + public readonly balance: BigNumber, + public readonly requiredBalance: BigNumber + ) { + super(); + Object.setPrototypeOf(this, InsufficientFundsError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/insufficient-liquidity.error.ts b/SDK-mstr/src/common/errors/swap/insufficient-liquidity.error.ts new file mode 100644 index 0000000..4a707eb --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/insufficient-liquidity.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when tokens' pair doesn't have enough liquidity. + */ +export class InsufficientLiquidityError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, InsufficientLiquidityError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/lifi-pair-is-unavailable.error.ts b/SDK-mstr/src/common/errors/swap/lifi-pair-is-unavailable.error.ts new file mode 100644 index 0000000..bee4bdb --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/lifi-pair-is-unavailable.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when `swap` transaction in lifi is failed. + */ +export class LifiPairIsUnavailableError extends RubicSdkError { + constructor() { + super('The swap using this pair is currently unavailable. Please try again later.'); + Object.setPrototypeOf(this, LifiPairIsUnavailableError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/low-slippage-deflationary-token.error.ts b/SDK-mstr/src/common/errors/swap/low-slippage-deflationary-token.error.ts new file mode 100644 index 0000000..8020edf --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/low-slippage-deflationary-token.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when user is selling deflationary token with too low slippage. + */ +export class LowSlippageDeflationaryTokenError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, LowSlippageDeflationaryTokenError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/low-slippage.error.ts b/SDK-mstr/src/common/errors/swap/low-slippage.error.ts new file mode 100644 index 0000000..ae0642c --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/low-slippage.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when slippage tolerance is too low for selected token. + */ +export class LowSlippageError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, LowSlippageError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/max-amount.error.ts b/SDK-mstr/src/common/errors/swap/max-amount.error.ts new file mode 100644 index 0000000..2338c18 --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/max-amount.error.ts @@ -0,0 +1,9 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +export class MaxAmountError extends RubicSdkError { + constructor(public readonly maxAmount: BigNumber, public readonly tokenSymbol: string) { + super(`Max amount is ${new BigNumber(maxAmount).toFixed()} ${tokenSymbol}`); + Object.setPrototypeOf(this, MaxAmountError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/min-amount.error.ts b/SDK-mstr/src/common/errors/swap/min-amount.error.ts new file mode 100644 index 0000000..8e51a87 --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/min-amount.error.ts @@ -0,0 +1,9 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +export class MinAmountError extends RubicSdkError { + constructor(public readonly minAmount: BigNumber, public readonly tokenSymbol: string) { + super(`Min amount is ${new BigNumber(minAmount).toFixed()} ${tokenSymbol}`); + Object.setPrototypeOf(this, MinAmountError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/no-linked-account-erros.ts b/SDK-mstr/src/common/errors/swap/no-linked-account-erros.ts new file mode 100644 index 0000000..a5f121e --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/no-linked-account-erros.ts @@ -0,0 +1,8 @@ +import { RubicSdkError } from '../rubic-sdk.error'; + +export class NoLinkedAccountError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, NoLinkedAccountError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/not-supported-blockchain.ts b/SDK-mstr/src/common/errors/swap/not-supported-blockchain.ts new file mode 100644 index 0000000..d45e8f6 --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/not-supported-blockchain.ts @@ -0,0 +1,12 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * @internal + * Thrown, when provider does not support provided blockchain. + */ +export class NotSupportedBlockchain extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, NotSupportedBlockchain.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/not-supported-tokens.error.ts b/SDK-mstr/src/common/errors/swap/not-supported-tokens.error.ts new file mode 100644 index 0000000..ef12db7 --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/not-supported-tokens.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when provider does not support provided tokens. + */ +export class NotSupportedTokensError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, NotSupportedTokensError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/not-whitelisted-provider.error.ts b/SDK-mstr/src/common/errors/swap/not-whitelisted-provider.error.ts new file mode 100644 index 0000000..bd2786f --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/not-whitelisted-provider.error.ts @@ -0,0 +1,15 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, via provider is not whitelisted in . + */ +export class NotWhitelistedProviderError extends RubicSdkError { + constructor( + public readonly providerRouter: string, + public readonly providerGateway?: string, + public readonly cause?: string + ) { + super(); + Object.setPrototypeOf(this, NotWhitelistedProviderError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/swap-error-on-provider-side.ts b/SDK-mstr/src/common/errors/swap/swap-error-on-provider-side.ts new file mode 100644 index 0000000..264c018 --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/swap-error-on-provider-side.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when provider rejects swap by internal reason. + */ +export class SdkSwapErrorOnProviderSide extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, SdkSwapErrorOnProviderSide.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/swap-request.error.ts b/SDK-mstr/src/common/errors/swap/swap-request.error.ts new file mode 100644 index 0000000..28ad6e8 --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/swap-request.error.ts @@ -0,0 +1,13 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when `swap` request to API is failed. + */ +export class SwapRequestError extends RubicSdkError { + constructor() { + super( + "Unfortunately, the provider couldn't generate the transaction. Please try again later." + ); + Object.setPrototypeOf(this, SwapRequestError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/unnecessary-approve.error.ts b/SDK-mstr/src/common/errors/swap/unnecessary-approve.error.ts new file mode 100644 index 0000000..65b4a88 --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/unnecessary-approve.error.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when approve method is called, but there is enough allowance. + */ +export class UnnecessaryApproveError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, UnnecessaryApproveError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/wallet-not-connected.error.ts b/SDK-mstr/src/common/errors/swap/wallet-not-connected.error.ts new file mode 100644 index 0000000..da24e9e --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/wallet-not-connected.error.ts @@ -0,0 +1,12 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +/** + * Thrown, when method, which requires connected wallet, is called without + * wallet being connected. + */ +export class WalletNotConnectedError extends RubicSdkError { + constructor() { + super(); + Object.setPrototypeOf(this, WalletNotConnectedError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/swap/wrong-network.error.ts b/SDK-mstr/src/common/errors/swap/wrong-network.error.ts new file mode 100644 index 0000000..52f97bb --- /dev/null +++ b/SDK-mstr/src/common/errors/swap/wrong-network.error.ts @@ -0,0 +1,13 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +/** + * Thrown during swap, if user's selected network does not match source blockchain + * in swap parameters. + */ +export class WrongNetworkError extends RubicSdkError { + constructor(public readonly requiredBlockchain: BlockchainName) { + super(); + Object.setPrototypeOf(this, WrongNetworkError.prototype); + } +} diff --git a/SDK-mstr/src/common/errors/utils/index.ts b/SDK-mstr/src/common/errors/utils/index.ts new file mode 100644 index 0000000..d61a479 --- /dev/null +++ b/SDK-mstr/src/common/errors/utils/index.ts @@ -0,0 +1 @@ +export * from './timeout.error'; diff --git a/SDK-mstr/src/common/errors/utils/timeout.error.ts b/SDK-mstr/src/common/errors/utils/timeout.error.ts new file mode 100644 index 0000000..5c9d195 --- /dev/null +++ b/SDK-mstr/src/common/errors/utils/timeout.error.ts @@ -0,0 +1,10 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; + +export class TimeoutError extends RubicSdkError { + constructor(message?: string) { + super(message); + this.name = 'TimeoutError'; + + Object.setPrototypeOf(this, TimeoutError.prototype); + } +} diff --git a/SDK-mstr/src/common/tokens/constants/native-tokens.ts b/SDK-mstr/src/common/tokens/constants/native-tokens.ts new file mode 100644 index 0000000..b00a8bb --- /dev/null +++ b/SDK-mstr/src/common/tokens/constants/native-tokens.ts @@ -0,0 +1,471 @@ +import { Token } from 'src/common/tokens/token'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + TestnetEvmBlockchain +} from 'src/core/blockchain/models/blockchain-name'; +import { BitcoinWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/bitcoin-web3-pure'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { IcpWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/icp-web3-pure'; +import { KavaCosmosWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/kava-cosmos-web3-pure'; +import { SolanaWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/solana-web3-pure/solana-web3-pure'; +import { TronWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/tron-web3-pure'; + +const testnetNativeTokens: Record = { + [BLOCKCHAIN_NAME.FUJI]: new Token({ + blockchain: BLOCKCHAIN_NAME.FUJI, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'AVAX', + symbol: 'AVAX', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MUMBAI]: new Token({ + blockchain: BLOCKCHAIN_NAME.MUMBAI, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Matic Network', + symbol: 'MATIC', + decimals: 18 + }), + [BLOCKCHAIN_NAME.GOERLI]: new Token({ + blockchain: BLOCKCHAIN_NAME.GOERLI, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Ethereum', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN_TESTNET]: new Token({ + blockchain: BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN_TESTNET, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Test Binance Coin', + symbol: 'tBNB', + decimals: 18 + }), + [BLOCKCHAIN_NAME.SCROLL_SEPOLIA]: new Token({ + blockchain: BLOCKCHAIN_NAME.SCROLL_SEPOLIA, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ETH', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ARTHERA]: new Token({ + blockchain: BLOCKCHAIN_NAME.ARTHERA, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Arthera', + symbol: 'AA', + decimals: 18 + }), + [BLOCKCHAIN_NAME.SEPOLIA]: new Token({ + blockchain: BLOCKCHAIN_NAME.SEPOLIA, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Ether', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BERACHAIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.BERACHAIN, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'BERA', + symbol: 'BERA', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BLAST_TESTNET]: new Token({ + blockchain: BLOCKCHAIN_NAME.BLAST_TESTNET, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Ether', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.HOLESKY]: new Token({ + blockchain: BLOCKCHAIN_NAME.HOLESKY, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Ether', + symbol: 'ETH', + decimals: 18 + }) +}; + +export const nativeTokensList: Record = { + ...Object.values(BLOCKCHAIN_NAME).reduce( + (acc, blockchain) => ({ ...acc, [blockchain]: blockchain }), + {} as Record + ), + ...testnetNativeTokens, + [BLOCKCHAIN_NAME.ETHEREUM]: new Token({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Ethereum', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Binance Coin', + symbol: 'BNB', + decimals: 18 + }), + [BLOCKCHAIN_NAME.POLYGON]: new Token({ + blockchain: BLOCKCHAIN_NAME.POLYGON, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Matic Network', + symbol: 'MATIC', + decimals: 18 + }), + [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: new Token({ + blockchain: BLOCKCHAIN_NAME.POLYGON_ZKEVM, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ETH', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.AVALANCHE]: new Token({ + blockchain: BLOCKCHAIN_NAME.AVALANCHE, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'AVAX', + symbol: 'AVAX', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MOONRIVER]: new Token({ + blockchain: BLOCKCHAIN_NAME.MOONRIVER, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'MOVR', + symbol: 'MOVR', + decimals: 18 + }), + [BLOCKCHAIN_NAME.FANTOM]: new Token({ + blockchain: BLOCKCHAIN_NAME.FANTOM, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'FTM', + symbol: 'FTM', + decimals: 18 + }), + [BLOCKCHAIN_NAME.HARMONY]: new Token({ + blockchain: BLOCKCHAIN_NAME.HARMONY, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ONE', + symbol: 'ONE', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ARBITRUM]: new Token({ + blockchain: BLOCKCHAIN_NAME.ARBITRUM, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'AETH', + symbol: 'AETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.AURORA]: new Token({ + blockchain: BLOCKCHAIN_NAME.AURORA, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'aETH', + symbol: 'aETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.TELOS]: new Token({ + blockchain: BLOCKCHAIN_NAME.TELOS, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'TLOS', + symbol: 'TLOS', + decimals: 18 + }), + [BLOCKCHAIN_NAME.OPTIMISM]: new Token({ + blockchain: BLOCKCHAIN_NAME.OPTIMISM, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ETH', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.CRONOS]: new Token({ + blockchain: BLOCKCHAIN_NAME.CRONOS, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'CRO', + symbol: 'CRO', + decimals: 18 + }), + [BLOCKCHAIN_NAME.OKE_X_CHAIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.OKE_X_CHAIN, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'OKT', + symbol: 'OKT', + decimals: 18 + }), + [BLOCKCHAIN_NAME.GNOSIS]: new Token({ + blockchain: BLOCKCHAIN_NAME.GNOSIS, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'xDAI', + symbol: 'xDAI', + decimals: 18 + }), + [BLOCKCHAIN_NAME.FUSE]: new Token({ + blockchain: BLOCKCHAIN_NAME.FUSE, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'FUSE', + symbol: 'FUSE', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MOONBEAM]: new Token({ + blockchain: BLOCKCHAIN_NAME.MOONBEAM, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'GLMR', + symbol: 'GLMR', + decimals: 18 + }), + [BLOCKCHAIN_NAME.CELO]: new Token({ + blockchain: BLOCKCHAIN_NAME.CELO, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'CELO', + symbol: 'CELO', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BOBA]: new Token({ + blockchain: BLOCKCHAIN_NAME.BOBA, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'BOBA', + symbol: 'BOBA', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BOBA_BSC]: new Token({ + blockchain: BLOCKCHAIN_NAME.BOBA_BSC, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'BOBA', + symbol: 'BOBA', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ASTAR_EVM]: new Token({ + blockchain: BLOCKCHAIN_NAME.ASTAR_EVM, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ASTR', + symbol: 'ASTR', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ETHEREUM_POW]: new Token({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM_POW, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Ethereum PoW', + symbol: 'ETHW', + decimals: 18 + }), + [BLOCKCHAIN_NAME.KAVA]: new Token({ + blockchain: BLOCKCHAIN_NAME.KAVA, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'KAVA', + symbol: 'KAVA', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BITCOIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.BITCOIN, + address: BitcoinWeb3Pure.nativeTokenAddress, + name: 'Bitcoin', + symbol: 'BTC', + decimals: 8 + }), + [BLOCKCHAIN_NAME.TRON]: new Token({ + blockchain: BLOCKCHAIN_NAME.TRON, + address: TronWeb3Pure.nativeTokenAddress, + name: 'TRX', + symbol: 'TRX', + decimals: 6 + }), + [BLOCKCHAIN_NAME.BITGERT]: new Token({ + blockchain: BLOCKCHAIN_NAME.BITGERT, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Brise', + symbol: 'BRISE', + decimals: 18 + }), + [BLOCKCHAIN_NAME.OASIS]: new Token({ + blockchain: BLOCKCHAIN_NAME.OASIS, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ROSE', + symbol: 'ROSE', + decimals: 18 + }), + [BLOCKCHAIN_NAME.METIS]: new Token({ + blockchain: BLOCKCHAIN_NAME.METIS, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Metis token', + symbol: 'METIS', + decimals: 18 + }), + [BLOCKCHAIN_NAME.DFK]: new Token({ + blockchain: BLOCKCHAIN_NAME.DFK, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'JEWEL', + symbol: 'JEWEL', + decimals: 18 + }), + [BLOCKCHAIN_NAME.KLAYTN]: new Token({ + blockchain: BLOCKCHAIN_NAME.KLAYTN, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Klaytn', + symbol: 'KLAY', + decimals: 18 + }), + [BLOCKCHAIN_NAME.VELAS]: new Token({ + blockchain: BLOCKCHAIN_NAME.VELAS, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Velas', + symbol: 'VLX', + decimals: 18 + }), + [BLOCKCHAIN_NAME.SYSCOIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.SYSCOIN, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Syscoin', + symbol: 'SYS', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ICP]: new Token({ + blockchain: BLOCKCHAIN_NAME.ICP, + address: IcpWeb3Pure.nativeTokenAddress, + name: 'Internet Computer', + symbol: 'ICP', + decimals: 8 + }), + [BLOCKCHAIN_NAME.KAVA_COSMOS]: new Token({ + blockchain: BLOCKCHAIN_NAME.KAVA_COSMOS, + address: KavaCosmosWeb3Pure.nativeTokenAddress, + name: 'Kava', + symbol: 'KAVA', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ZK_SYNC]: new Token({ + blockchain: BLOCKCHAIN_NAME.ZK_SYNC, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ETH', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.PULSECHAIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.PULSECHAIN, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'PLS', + symbol: 'PLS', + decimals: 18 + }), + [BLOCKCHAIN_NAME.LINEA]: new Token({ + blockchain: BLOCKCHAIN_NAME.LINEA, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ETH', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BASE]: new Token({ + blockchain: BLOCKCHAIN_NAME.BASE, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ETH', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MANTLE]: new Token({ + blockchain: BLOCKCHAIN_NAME.MANTLE, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Mantle', + symbol: 'MNT', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MANTA_PACIFIC]: new Token({ + blockchain: BLOCKCHAIN_NAME.MANTA_PACIFIC, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ETH', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.SCROLL]: new Token({ + blockchain: BLOCKCHAIN_NAME.SCROLL, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ETH', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ZETACHAIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.ZETACHAIN, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Zeta', + symbol: 'ZETA', + decimals: 18 + }), + [BLOCKCHAIN_NAME.SOLANA]: new Token({ + blockchain: BLOCKCHAIN_NAME.SOLANA, + address: SolanaWeb3Pure.nativeTokenAddress, + name: 'Solana', + symbol: 'SOL', + decimals: 9 + }), + [BLOCKCHAIN_NAME.BLAST]: new Token({ + blockchain: BLOCKCHAIN_NAME.BLAST, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Ether', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.KROMA]: new Token({ + blockchain: BLOCKCHAIN_NAME.KROMA, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ETH', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.HORIZEN_EON]: new Token({ + blockchain: BLOCKCHAIN_NAME.HORIZEN_EON, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ZEN', + symbol: 'ZEN', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MERLIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.MERLIN, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Bitcoin', + symbol: 'BTC', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ROOTSTOCK]: new Token({ + blockchain: BLOCKCHAIN_NAME.ROOTSTOCK, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'RBTC', + symbol: 'RBTC', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MODE]: new Token({ + blockchain: BLOCKCHAIN_NAME.MODE, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ETH', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ZK_FAIR]: new Token({ + blockchain: BLOCKCHAIN_NAME.ZK_FAIR, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'USDC', + symbol: 'USDC', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ZK_LINK]: new Token({ + blockchain: BLOCKCHAIN_NAME.ZK_LINK, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'ETH', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.XLAYER]: new Token({ + blockchain: BLOCKCHAIN_NAME.XLAYER, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'OK Token', + symbol: 'OKB', + decimals: 18 + }), + [BLOCKCHAIN_NAME.TAIKO]: new Token({ + blockchain: BLOCKCHAIN_NAME.TAIKO, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Ether', + symbol: 'ETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.SEI]: new Token({ + blockchain: BLOCKCHAIN_NAME.SEI, + address: EvmWeb3Pure.nativeTokenAddress, + name: 'Sei', + symbol: 'SEI', + decimals: 18 + }) +}; diff --git a/SDK-mstr/src/common/tokens/constants/wrapped-addresses.ts b/SDK-mstr/src/common/tokens/constants/wrapped-addresses.ts new file mode 100644 index 0000000..4303970 --- /dev/null +++ b/SDK-mstr/src/common/tokens/constants/wrapped-addresses.ts @@ -0,0 +1,61 @@ +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +export const wrappedAddress: Partial> = { + [BLOCKCHAIN_NAME.ETHEREUM]: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: '0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c', + [BLOCKCHAIN_NAME.POLYGON]: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', + [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: '0x4F9A0e7FD2Bf6067db6994CF12E4495Df938E6e9', + [BLOCKCHAIN_NAME.AVALANCHE]: '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7', + [BLOCKCHAIN_NAME.MOONRIVER]: '0x98878b06940ae243284ca214f92bb71a2b032b8a', + [BLOCKCHAIN_NAME.FANTOM]: '0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83', + [BLOCKCHAIN_NAME.HARMONY]: '0xcf664087a5bb0237a0bad6742852ec6c8d69a27a', + [BLOCKCHAIN_NAME.ARBITRUM]: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', + [BLOCKCHAIN_NAME.AURORA]: '0xC9BdeEd33CD01541e1eeD10f90519d2C06Fe3feB', + [BLOCKCHAIN_NAME.TELOS]: '0xd102ce6a4db07d247fcc28f366a623df0938ca9e', + [BLOCKCHAIN_NAME.OPTIMISM]: '0x4200000000000000000000000000000000000006', + [BLOCKCHAIN_NAME.CRONOS]: '0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23', + [BLOCKCHAIN_NAME.OKE_X_CHAIN]: '0x8f8526dbfd6e38e3d8307702ca8469bae6c56c15', + [BLOCKCHAIN_NAME.GNOSIS]: '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d', + [BLOCKCHAIN_NAME.FUSE]: '0x0BE9e53fd7EDaC9F859882AfdDa116645287C629', + [BLOCKCHAIN_NAME.MOONBEAM]: '0xAcc15dC74880C9944775448304B263D191c6077F', + [BLOCKCHAIN_NAME.CELO]: '0x122013fd7df1c6f636a5bb8f03108e876548b455', + [BLOCKCHAIN_NAME.BOBA]: '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000', + [BLOCKCHAIN_NAME.BOBA_BSC]: '0xC58aaD327D6D58D979882601ba8DDa0685B505eA', + [BLOCKCHAIN_NAME.ASTAR_EVM]: '0xAeaaf0e2c81Af264101B9129C00F4440cCF0F720', + [BLOCKCHAIN_NAME.ETHEREUM_POW]: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + [BLOCKCHAIN_NAME.KAVA]: '0xc86c7C0eFbd6A49B35E8714C5f59D99De09A225b', + [BLOCKCHAIN_NAME.BITGERT]: '0x0eb9036cbE0f052386f36170c6b07eF0a0E3f710', + [BLOCKCHAIN_NAME.OASIS]: '0x21C718C22D52d0F3a789b752D4c2fD5908a8A733', + [BLOCKCHAIN_NAME.METIS]: '0x75cb093E4D61d2A2e65D8e0BBb01DE8d89b53481', + [BLOCKCHAIN_NAME.DFK]: '0xCCb93dABD71c8Dad03Fc4CE5559dC3D89F67a260', + [BLOCKCHAIN_NAME.KLAYTN]: '0xe4f05a66ec68b54a58b17c22107b02e0232cc817', + [BLOCKCHAIN_NAME.VELAS]: '0xc579D1f3CF86749E05CD06f7ADe17856c2CE3126', + [BLOCKCHAIN_NAME.SYSCOIN]: '0xd3e822f3ef011Ca5f17D82C956D952D8d7C3A1BB', + [BLOCKCHAIN_NAME.ZK_SYNC]: '0x5aea5775959fbc2557cc8789bc1bf90a239d9a91', + [BLOCKCHAIN_NAME.PULSECHAIN]: '0xa1077a294dde1b09bb078844df40758a5d0f9a27', + [BLOCKCHAIN_NAME.LINEA]: '0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f', + [BLOCKCHAIN_NAME.BASE]: '0x4200000000000000000000000000000000000006', + [BLOCKCHAIN_NAME.MANTLE]: '0x78c1b0C915c4FAA5FffA6CAbf0219DA63d7f4cb8', + [BLOCKCHAIN_NAME.MANTA_PACIFIC]: '0x0Dc808adcE2099A9F62AA87D9670745AbA741746', + [BLOCKCHAIN_NAME.SCROLL]: '0x5300000000000000000000000000000000000004', + [BLOCKCHAIN_NAME.ZETACHAIN]: '0x5F0b1a82749cb4E2278EC87F8BF6B618dC71a8bf', + [BLOCKCHAIN_NAME.BLAST]: '0x4300000000000000000000000000000000000004', + [BLOCKCHAIN_NAME.KROMA]: '0x4200000000000000000000000000000000000001', + [BLOCKCHAIN_NAME.HORIZEN_EON]: '0xF5cB8652a84329A2016A386206761f455bCEDab6', + [BLOCKCHAIN_NAME.MERLIN]: '0xF6D226f9Dc15d9bB51182815b320D3fBE324e1bA', + [BLOCKCHAIN_NAME.ROOTSTOCK]: '0x542fda317318ebf1d3deaf76e0b632741a7e677d', + [BLOCKCHAIN_NAME.MODE]: '0x4200000000000000000000000000000000000006', + [BLOCKCHAIN_NAME.ZK_FAIR]: '0xD33Db7EC50A98164cC865dfaa64666906d79319C', + [BLOCKCHAIN_NAME.ZK_LINK]: '0x8280a4e7d5b3b658ec4580d3bc30f5e50454f169', + [BLOCKCHAIN_NAME.XLAYER]: '0xe538905cf8410324e03A5A23C1c177a474D59b2b', + [BLOCKCHAIN_NAME.TAIKO]: '0xa51894664a773981c6c112c43ce576f315d5b1b6', + [BLOCKCHAIN_NAME.SEI]: '0xE30feDd158A2e3b13e9badaeABaFc5516e95e8C7', + // Testnet + [BLOCKCHAIN_NAME.GOERLI]: '0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', + [BLOCKCHAIN_NAME.SCROLL_SEPOLIA]: '0x5300000000000000000000000000000000000004', + [BLOCKCHAIN_NAME.ARTHERA]: '0xC7A183Ad373301d68f7E0Ee824c8c727C7D5B21d', + [BLOCKCHAIN_NAME.SEPOLIA]: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + [BLOCKCHAIN_NAME.BERACHAIN]: '0x5806E416dA447b267cEA759358cF22Cc41FAE80F', + [BLOCKCHAIN_NAME.BLAST_TESTNET]: '0x4200000000000000000000000000000000000023', + [BLOCKCHAIN_NAME.HOLESKY]: '' +}; diff --git a/SDK-mstr/src/common/tokens/constants/wrapped-native-tokens.ts b/SDK-mstr/src/common/tokens/constants/wrapped-native-tokens.ts new file mode 100644 index 0000000..1a052c7 --- /dev/null +++ b/SDK-mstr/src/common/tokens/constants/wrapped-native-tokens.ts @@ -0,0 +1,454 @@ +import { Token } from 'src/common/tokens/token'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +export const wrappedNativeTokensList: Partial> = { + // @TODO REFACTOR + [BLOCKCHAIN_NAME.ETHEREUM]: new Token({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM, + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + address: '0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c', + name: 'Wrapped BNB', + symbol: 'WBNB', + decimals: 18 + }), + [BLOCKCHAIN_NAME.POLYGON]: new Token({ + blockchain: BLOCKCHAIN_NAME.POLYGON, + address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', + name: 'Wrapped Matic', + symbol: 'WMATIC', + decimals: 18 + }), + [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: new Token({ + blockchain: BLOCKCHAIN_NAME.POLYGON_ZKEVM, + address: '0x4F9A0e7FD2Bf6067db6994CF12E4495Df938E6e9', + name: 'Wrapped Ether', + symbol: 'Weth', + decimals: 18 + }), + [BLOCKCHAIN_NAME.AVALANCHE]: new Token({ + blockchain: BLOCKCHAIN_NAME.AVALANCHE, + address: '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7', + name: 'WAVAX', + symbol: 'WAVAX', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MOONRIVER]: new Token({ + blockchain: BLOCKCHAIN_NAME.MOONRIVER, + address: '0xf50225a84382c74cbdea10b0c176f71fc3de0c4d', + name: 'Wrapped MOVR', + symbol: 'WMOVR', + decimals: 18 + }), + [BLOCKCHAIN_NAME.FANTOM]: new Token({ + blockchain: BLOCKCHAIN_NAME.FANTOM, + address: '0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83', + name: 'Wrapped Fantom', + symbol: 'WFTM', + decimals: 18 + }), + [BLOCKCHAIN_NAME.HARMONY]: new Token({ + blockchain: BLOCKCHAIN_NAME.HARMONY, + address: '0xcf664087a5bb0237a0bad6742852ec6c8d69a27a', + name: 'Wrapped ONE', + symbol: 'WONE', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ARBITRUM]: new Token({ + blockchain: BLOCKCHAIN_NAME.ARBITRUM, + address: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.AURORA]: new Token({ + blockchain: BLOCKCHAIN_NAME.AURORA, + address: '0xC9BdeEd33CD01541e1eeD10f90519d2C06Fe3feB', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.TELOS]: new Token({ + blockchain: BLOCKCHAIN_NAME.TELOS, + address: '0xd102ce6a4db07d247fcc28f366a623df0938ca9e', + name: 'Wrapped Telos', + symbol: 'WTLOS', + decimals: 18 + }), + [BLOCKCHAIN_NAME.OPTIMISM]: new Token({ + blockchain: BLOCKCHAIN_NAME.OPTIMISM, + address: '0x4200000000000000000000000000000000000006', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.CRONOS]: new Token({ + blockchain: BLOCKCHAIN_NAME.CRONOS, + address: '0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23', + name: 'Wrapped CRO', + symbol: 'WCRO', + decimals: 18 + }), + [BLOCKCHAIN_NAME.OKE_X_CHAIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.OKE_X_CHAIN, + address: '0x8f8526dbfd6e38e3d8307702ca8469bae6c56c15', + name: 'Wrapped OKT', + symbol: 'WOKT', + decimals: 18 + }), + [BLOCKCHAIN_NAME.GNOSIS]: new Token({ + blockchain: BLOCKCHAIN_NAME.GNOSIS, + address: '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d', + name: 'Wrapped XDAI', + symbol: 'WXDAI', + decimals: 18 + }), + [BLOCKCHAIN_NAME.FUSE]: new Token({ + blockchain: BLOCKCHAIN_NAME.FUSE, + address: '0x0BE9e53fd7EDaC9F859882AfdDa116645287C629', + name: 'Wrapped Fuse', + symbol: 'WFUSE', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MOONBEAM]: new Token({ + blockchain: BLOCKCHAIN_NAME.MOONBEAM, + address: '0xAcc15dC74880C9944775448304B263D191c6077F', + name: 'Wrapped GLMR', + symbol: 'WGLMR', + decimals: 18 + }), + [BLOCKCHAIN_NAME.CELO]: new Token({ + blockchain: BLOCKCHAIN_NAME.CELO, + address: '0x122013fd7df1c6f636a5bb8f03108e876548b455', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BOBA]: new Token({ + blockchain: BLOCKCHAIN_NAME.BOBA, + address: '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BOBA_BSC]: new Token({ + blockchain: BLOCKCHAIN_NAME.BOBA_BSC, + address: '0xC58aaD327D6D58D979882601ba8DDa0685B505eA', + name: 'Wrapped Boba', + symbol: 'WBOBA', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ASTAR_EVM]: new Token({ + blockchain: BLOCKCHAIN_NAME.ASTAR_EVM, + address: '0xAeaaf0e2c81Af264101B9129C00F4440cCF0F720', + name: 'Wrapped Astar', + symbol: 'WASTR', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ETHEREUM_POW]: new Token({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM_POW, + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + name: 'Wrapped Ether PoW', + symbol: 'WETHw', + decimals: 18 + }), + [BLOCKCHAIN_NAME.KAVA]: new Token({ + blockchain: BLOCKCHAIN_NAME.KAVA, + address: '0xc86c7C0eFbd6A49B35E8714C5f59D99De09A225b', + name: 'Wrapped Kava', + symbol: 'WKAVA', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BITGERT]: new Token({ + blockchain: BLOCKCHAIN_NAME.BITGERT, + address: '0x0eb9036cbE0f052386f36170c6b07eF0a0E3f710', + name: 'Wrapped BRISE', + symbol: 'WBRISE', + decimals: 18 + }), + [BLOCKCHAIN_NAME.OASIS]: new Token({ + blockchain: BLOCKCHAIN_NAME.OASIS, + address: '0x21C718C22D52d0F3a789b752D4c2fD5908a8A733', + name: 'Wrapped ROSE', + symbol: 'WROSE', + decimals: 18 + }), + [BLOCKCHAIN_NAME.METIS]: new Token({ + blockchain: BLOCKCHAIN_NAME.METIS, + address: '0x75cb093E4D61d2A2e65D8e0BBb01DE8d89b53481', + name: 'Wrapped METIS', + symbol: 'WMETIS', + decimals: 18 + }), + [BLOCKCHAIN_NAME.DFK]: new Token({ + blockchain: BLOCKCHAIN_NAME.DFK, + address: '0xCCb93dABD71c8Dad03Fc4CE5559dC3D89F67a260', + name: 'Wrapped JEWEL', + symbol: 'WJEWEL', + decimals: 18 + }), + [BLOCKCHAIN_NAME.KLAYTN]: new Token({ + blockchain: BLOCKCHAIN_NAME.KLAYTN, + address: '0xe4f05a66ec68b54a58b17c22107b02e0232cc817', + name: 'Wrapped KLAY', + symbol: 'WKLAY', + decimals: 18 + }), + [BLOCKCHAIN_NAME.VELAS]: new Token({ + blockchain: BLOCKCHAIN_NAME.VELAS, + address: '0xc579D1f3CF86749E05CD06f7ADe17856c2CE3126', + name: 'Wrapped VLX', + symbol: 'WVLX', + decimals: 18 + }), + [BLOCKCHAIN_NAME.SYSCOIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.SYSCOIN, + address: '0xd3e822f3ef011Ca5f17D82C956D952D8d7C3A1BB', + name: 'Wrapped SYS', + symbol: 'WSYS', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ZK_SYNC]: new Token({ + blockchain: BLOCKCHAIN_NAME.ZK_SYNC, + address: '0x5aea5775959fbc2557cc8789bc1bf90a239d9a91', + name: 'Wrapped Ether', + symbol: 'Weth', + decimals: 18 + }), + [BLOCKCHAIN_NAME.PULSECHAIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.PULSECHAIN, + address: '0xa1077a294dde1b09bb078844df40758a5d0f9a27', + name: 'Wrapped PLS', + symbol: 'WPLS', + decimals: 18 + }), + [BLOCKCHAIN_NAME.LINEA]: new Token({ + blockchain: BLOCKCHAIN_NAME.LINEA, + address: '0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f', + name: 'Wrapped ETH', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BASE]: new Token({ + blockchain: BLOCKCHAIN_NAME.BASE, + address: '0x4200000000000000000000000000000000000006', + name: 'Wrapped ETH', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MANTLE]: new Token({ + blockchain: BLOCKCHAIN_NAME.MANTLE, + address: '0x78c1b0C915c4FAA5FffA6CAbf0219DA63d7f4cb8', + name: 'Wrapped Mantle', + symbol: 'WMNT', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN_TESTNET]: new Token({ + blockchain: BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN_TESTNET, + address: '0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MUMBAI]: new Token({ + blockchain: BLOCKCHAIN_NAME.MUMBAI, + address: '0x9c3c9283d3e44854697cd22d3faa240cfb032889', + name: 'Wrapped Matic', + symbol: 'WMATIC', + decimals: 18 + }), + [BLOCKCHAIN_NAME.FUJI]: new Token({ + blockchain: BLOCKCHAIN_NAME.FUJI, + address: '0x1d308089a2d1ced3f1ce36b1fcaf815b07217be3', + name: 'Wrapped Avax', + symbol: 'WAVAX', + decimals: 18 + }), + [BLOCKCHAIN_NAME.GOERLI]: new Token({ + blockchain: BLOCKCHAIN_NAME.GOERLI, + address: '0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.SCROLL_SEPOLIA]: new Token({ + blockchain: BLOCKCHAIN_NAME.SCROLL_SEPOLIA, + address: '0x5300000000000000000000000000000000000004', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ARTHERA]: new Token({ + blockchain: BLOCKCHAIN_NAME.ARTHERA, + address: '0xC7A183Ad373301d68f7E0Ee824c8c727C7D5B21d', + name: 'Wrapped Arthera', + symbol: 'WAA', + decimals: 18 + }), + [BLOCKCHAIN_NAME.SEPOLIA]: new Token({ + blockchain: BLOCKCHAIN_NAME.SEPOLIA, + address: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ETHEREUM_CLASSIC]: new Token({ + blockchain: BLOCKCHAIN_NAME.ETHEREUM_CLASSIC, + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.FLARE]: new Token({ + blockchain: BLOCKCHAIN_NAME.FLARE, + address: '0x1D80c49BbBCd1C0911346656B529DF9E5c2F783d', + name: 'Wrapper Flare', + symbol: 'WFLR', + decimals: 18 + }), + [BLOCKCHAIN_NAME.IOTEX]: new Token({ + blockchain: BLOCKCHAIN_NAME.IOTEX, + address: '0xa00744882684c3e4747faefd68d283ea44099d03', + name: 'Wrapped IoTeX', + symbol: 'WIOTX', + decimals: 18 + }), + [BLOCKCHAIN_NAME.THETA]: new Token({ + blockchain: BLOCKCHAIN_NAME.THETA, + address: '0xaf537fb7e4c77c97403de94ce141b7edb9f7fcf0', + name: 'Wrapped Theta', + symbol: 'wTHETA', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BITCOIN_CASH]: new Token({ + blockchain: BLOCKCHAIN_NAME.BITCOIN_CASH, + address: '0x3743eC0673453E5009310C727Ba4eaF7b3a1cc04', + name: 'Wrapped BCH', + symbol: 'WBCH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MANTA_PACIFIC]: new Token({ + blockchain: BLOCKCHAIN_NAME.MANTA_PACIFIC, + address: '0x0Dc808adcE2099A9F62AA87D9670745AbA741746', + name: 'Wrapped ETH', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.SCROLL]: new Token({ + blockchain: BLOCKCHAIN_NAME.SCROLL, + address: '0x5300000000000000000000000000000000000004', + name: 'Wrapped ETH', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BERACHAIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.BERACHAIN, + address: '0x5806E416dA447b267cEA759358cF22Cc41FAE80F', + name: 'Wrapped BERA', + symbol: 'WBERA', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BLAST_TESTNET]: new Token({ + blockchain: BLOCKCHAIN_NAME.BLAST_TESTNET, + address: '0x4200000000000000000000000000000000000023', + name: 'Wrapped ETH', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ZETACHAIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.ZETACHAIN, + address: '0x5F0b1a82749cb4E2278EC87F8BF6B618dC71a8bf', + name: 'Wrapped Zeta', + symbol: 'WZETA', + decimals: 18 + }), + [BLOCKCHAIN_NAME.HOLESKY]: new Token({ + blockchain: BLOCKCHAIN_NAME.HOLESKY, + address: '', + name: 'Wrapped ETH', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.BLAST]: new Token({ + blockchain: BLOCKCHAIN_NAME.BLAST, + address: '0x4300000000000000000000000000000000000004', + name: 'Wrapped ETH', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.KROMA]: new Token({ + blockchain: BLOCKCHAIN_NAME.KROMA, + address: '0x4200000000000000000000000000000000000001', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.HORIZEN_EON]: new Token({ + blockchain: BLOCKCHAIN_NAME.HORIZEN_EON, + address: '0xF5cB8652a84329A2016A386206761f455bCEDab6', + name: 'Wrapped ZEN', + symbol: 'WZEN', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MERLIN]: new Token({ + blockchain: BLOCKCHAIN_NAME.MERLIN, + address: '0xF6D226f9Dc15d9bB51182815b320D3fBE324e1bA', + name: 'Wrapped BTC', + symbol: 'WBTC', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ROOTSTOCK]: new Token({ + blockchain: BLOCKCHAIN_NAME.ROOTSTOCK, + address: '0x542fda317318ebf1d3deaf76e0b632741a7e677d', + name: 'Wrapped BTC', + symbol: 'WBTC', + decimals: 18 + }), + [BLOCKCHAIN_NAME.MODE]: new Token({ + blockchain: BLOCKCHAIN_NAME.MODE, + address: '0x4200000000000000000000000000000000000006', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ZK_FAIR]: new Token({ + blockchain: BLOCKCHAIN_NAME.ZK_FAIR, + address: '0xD33Db7EC50A98164cC865dfaa64666906d79319C', + name: 'Wrapped USDC', + symbol: 'WUSDC', + decimals: 18 + }), + [BLOCKCHAIN_NAME.ZK_LINK]: new Token({ + blockchain: BLOCKCHAIN_NAME.ZK_LINK, + address: '0x8280a4e7d5b3b658ec4580d3bc30f5e50454f169', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.XLAYER]: new Token({ + blockchain: BLOCKCHAIN_NAME.XLAYER, + address: '0xe538905cf8410324e03A5A23C1c177a474D59b2b', + name: 'Wrapped OKB', + symbol: 'WOKB', + decimals: 18 + }), + [BLOCKCHAIN_NAME.TAIKO]: new Token({ + blockchain: BLOCKCHAIN_NAME.TAIKO, + address: '0xa51894664a773981c6c112c43ce576f315d5b1b6', + name: 'Wrapped Ether', + symbol: 'WETH', + decimals: 18 + }), + [BLOCKCHAIN_NAME.SEI]: new Token({ + blockchain: BLOCKCHAIN_NAME.SEI, + address: '0xE30feDd158A2e3b13e9badaeABaFc5516e95e8C7', + name: 'Wrapped Sei', + symbol: 'WSEI', + decimals: 18 + }) +}; diff --git a/SDK-mstr/src/common/tokens/index.ts b/SDK-mstr/src/common/tokens/index.ts new file mode 100644 index 0000000..b253d67 --- /dev/null +++ b/SDK-mstr/src/common/tokens/index.ts @@ -0,0 +1,4 @@ +export { PriceToken } from './price-token'; +export { PriceTokenAmount } from './price-token-amount'; +export { Token } from './token'; +export { TokenAmount } from './token-amount'; diff --git a/SDK-mstr/src/common/tokens/models/token-base-struct.ts b/SDK-mstr/src/common/tokens/models/token-base-struct.ts new file mode 100644 index 0000000..e10e959 --- /dev/null +++ b/SDK-mstr/src/common/tokens/models/token-base-struct.ts @@ -0,0 +1,9 @@ +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +/** + * Stores basic information of token. + */ +export interface TokenBaseStruct { + address: string; + blockchain: T; +} diff --git a/SDK-mstr/src/common/tokens/price-token-amount.ts b/SDK-mstr/src/common/tokens/price-token-amount.ts new file mode 100644 index 0000000..7a67045 --- /dev/null +++ b/SDK-mstr/src/common/tokens/price-token-amount.ts @@ -0,0 +1,152 @@ +import BigNumber from 'bignumber.js'; +import { TokenBaseStruct } from 'src/common/tokens/models/token-base-struct'; +import { PriceToken, PriceTokenStruct } from 'src/common/tokens/price-token'; +import { TokenStruct } from 'src/common/tokens/token'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; + +import { TokenUtils } from '../utils/token-utils'; + +export type PriceTokenAmountStruct = + PriceTokenStruct & ({ weiAmount: BigNumber } | { tokenAmount: BigNumber }); + +export type PriceTokenAmountBaseStruct = + TokenBaseStruct & ({ weiAmount: BigNumber } | { tokenAmount: BigNumber }); + +/** + * Contains token structure with price and amount. + */ +export class PriceTokenAmount extends PriceToken { + /** + * Creates PriceTokenAmount based on token's address and blockchain. + * @param tokenAmountBaseStruct Base token structure with amount. + */ + public static async createToken( + tokenAmountBaseStruct: PriceTokenAmountBaseStruct + ): Promise> { + const token = await super.createToken(tokenAmountBaseStruct); + return new PriceTokenAmount({ + ...tokenAmountBaseStruct, + ...token.asStruct + }); + } + + /** + * Creates PriceTokenAmount, fetching token's price. + * @param tokenAmount Token structure with amount. + */ + public static async createFromToken( + tokenAmount: TokenStruct & ({ weiAmount: BigNumber } | { tokenAmount: BigNumber }) + ): Promise> { + const priceToken = await super.createFromToken(tokenAmount); + return new PriceTokenAmount({ + ...tokenAmount, + price: priceToken.price + }); + } + + private readonly _weiAmount: BigNumber; + + /** + * Gets set amount in wei. + */ + get weiAmount(): BigNumber { + return this._weiAmount; + } + + /** + * Gets set amount in wei and converted to string. + */ + get stringWeiAmount(): string { + return this._weiAmount.toFixed(0); + } + + /** + * Gets set amount with decimals. + */ + get tokenAmount(): BigNumber { + return new BigNumber(this._weiAmount).div(new BigNumber(10).pow(this.decimals)); + } + + /** + * Serializes priceTokenAmount to struct object. + */ + public get asStructWithAmount(): PriceTokenAmountStruct { + return { + ...this, + price: this.price, + weiAmount: this.weiAmount + }; + } + + constructor(tokenStruct: PriceTokenAmountStruct) { + super(tokenStruct); + if ('weiAmount' in tokenStruct) { + this._weiAmount = tokenStruct.weiAmount; + } else { + this._weiAmount = new BigNumber( + Web3Pure.toWei(tokenStruct.tokenAmount, tokenStruct.decimals) + ); + } + } + + /** + * Returns wei amount decreased by (1 - slippage) times. + * @param slippage Slippage in range from 0 to 1. + */ + public weiAmountMinusSlippage(slippage: number): BigNumber { + return TokenUtils.getMinWeiAmount(this._weiAmount, slippage); + } + + /** + * Returns wei amount increased by (1 - slippage) times. + * @param slippage Slippage in range from 0 to 1. + */ + public weiAmountPlusSlippage(slippage: number): BigNumber { + return TokenUtils.getMaxWeiAmount(this._weiAmount, slippage); + } + + public async cloneAndCreate( + tokenStruct?: Partial + ): Promise { + const priceToken = await PriceToken.prototype.cloneAndCreate.call(this, tokenStruct); + return new PriceTokenAmount({ + ...priceToken.asStruct, + weiAmount: this.weiAmount, + ...tokenStruct + }); + } + + public clone(tokenStruct?: Partial): PriceTokenAmount { + return new PriceTokenAmount({ ...this, ...tokenStruct }); + } + + /** + * Calculates trade price impact percent if instance token is selling token, and parameter is buying token. + * If selling usd amount is less than buying usd amount, returns 0. + * @param toToken Token to buy. + */ + public calculatePriceImpactPercent(toToken: PriceTokenAmount): number | null { + if ( + !this.price?.isFinite() || + !toToken.price?.isFinite() || + !this.tokenAmount?.isFinite() || + !toToken.tokenAmount?.isFinite() || + !this.price?.gt(0) || + !toToken.price?.gt(0) + ) { + return null; + } + + const fromTokenCost = this.tokenAmount.multipliedBy(this.price); + const toTokenCost = toToken.tokenAmount.multipliedBy(toToken.price); + const impact = fromTokenCost + .minus(toTokenCost) + .dividedBy(fromTokenCost) + .multipliedBy(100) + .dp(2, BigNumber.ROUND_HALF_UP) + .toNumber(); + + return impact > 0 ? impact : 0; + } +} diff --git a/SDK-mstr/src/common/tokens/price-token.ts b/SDK-mstr/src/common/tokens/price-token.ts new file mode 100644 index 0000000..76951b9 --- /dev/null +++ b/SDK-mstr/src/common/tokens/price-token.ts @@ -0,0 +1,95 @@ +import BigNumber from 'bignumber.js'; +import { TokenBaseStruct } from 'src/common/tokens/models/token-base-struct'; +import { Token, TokenStruct } from 'src/common/tokens/token'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Injector } from 'src/core/injector/injector'; + +export type PriceTokenStruct = TokenStruct & { + price: BigNumber; +}; + +/** + * Contains token structure with price in usd per 1 unit. + */ +export class PriceToken extends Token { + /** + * Creates PriceToken based on token's address and blockchain. + * @param tokenBaseStruct Base token structure. + */ + public static async createToken( + tokenBaseStruct: TokenBaseStruct + ): Promise> { + const { coingeckoApi } = Injector; + + const tokenPromise = super.createToken(tokenBaseStruct); + const pricePromise = coingeckoApi + .getTokenPrice(tokenBaseStruct) + .catch(_err => new BigNumber(NaN)); + const results = await Promise.all([tokenPromise, pricePromise]); + + return new PriceToken({ ...results[0], price: results[1] }); + } + + /** + * Creates PriceToken, fetching token's price. + * @param token Token structure. + */ + public static async createFromToken( + token: TokenStruct + ): Promise> { + const { coingeckoApi } = Injector; + + const price = await coingeckoApi.getTokenPrice(token).catch(_err => new BigNumber(NaN)); + + return new PriceToken({ ...token, price }); + } + + private _price: BigNumber; + + public get price(): BigNumber { + return this._price; + } + + /** + * Serializes priceToken and its price to struct object. + */ + public get asStruct(): PriceTokenStruct { + return { + ...this, + price: this.price + }; + } + + constructor(tokenStruct: PriceTokenStruct) { + super(tokenStruct); + this._price = tokenStruct.price; + } + + /** + * Fetches current token price and saves it into token. + */ + public async getAndUpdateTokenPrice(): Promise { + await this.updateTokenPrice(); + return this.price; + } + + private async updateTokenPrice(): Promise { + const { coingeckoApi } = Injector; + this._price = await coingeckoApi.getTokenPrice({ ...this }).catch(_err => this._price); + } + + /** + * Clones token with fetching new price. + */ + public async cloneAndCreate(tokenStruct?: Partial): Promise { + const { coingeckoApi } = Injector; + + const price = await coingeckoApi.getTokenPrice(this).catch(_err => this._price); + + return new PriceToken({ ...this.asStruct, price, ...tokenStruct }); + } + + public clone(tokenStruct?: Partial): PriceToken { + return new PriceToken({ ...this, ...tokenStruct }); + } +} diff --git a/SDK-mstr/src/common/tokens/token-amount.ts b/SDK-mstr/src/common/tokens/token-amount.ts new file mode 100644 index 0000000..5182641 --- /dev/null +++ b/SDK-mstr/src/common/tokens/token-amount.ts @@ -0,0 +1,94 @@ +import BigNumber from 'bignumber.js'; +import { TokenBaseStruct } from 'src/common/tokens/models/token-base-struct'; +import { Token, TokenStruct } from 'src/common/tokens/token'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; + +export type TokenAmountStruct = TokenStruct & + ({ weiAmount: BigNumber } | { tokenAmount: BigNumber }); + +export type TokenAmountBaseStruct = TokenBaseStruct & + ({ weiAmount: BigNumber } | { tokenAmount: BigNumber }); + +/** + * Contains token structure with price and amount. + */ +export class TokenAmount extends Token { + /** + * Creates PriceTokenAmount based on token's address and blockchain. + * @param tokenAmountBaseStruct Base token structure with amount. + */ + public static async createToken( + tokenAmountBaseStruct: TokenAmountBaseStruct + ): Promise> { + const token = await super.createToken(tokenAmountBaseStruct); + return new TokenAmount({ + ...tokenAmountBaseStruct, + ...token + }); + } + + private readonly _weiAmount: BigNumber; + + /** + * Gets set amount in wei. + */ + get weiAmount(): BigNumber { + return new BigNumber(this._weiAmount); + } + + /** + * Gets set amount in wei and converted to string. + */ + get stringWeiAmount(): string { + return this._weiAmount.toFixed(0); + } + + /** + * Gets set amount with decimals. + */ + get tokenAmount(): BigNumber { + return new BigNumber(this._weiAmount).div(new BigNumber(10).pow(this.decimals)); + } + + /** + * Serializes priceTokenAmount to struct object. + */ + public get asStruct(): TokenAmountStruct { + return { + ...this, + weiAmount: this.weiAmount + }; + } + + constructor(tokenStruct: TokenAmountStruct) { + super(tokenStruct); + if ('weiAmount' in tokenStruct) { + this._weiAmount = new BigNumber(tokenStruct.weiAmount); + } else { + this._weiAmount = new BigNumber( + Web3Pure.toWei(tokenStruct.tokenAmount, tokenStruct.decimals) + ); + } + } + + /** + * Returns wei amount decreased by (1 - slippage) times. + * @param slippage Slippage in range from 0 to 1. + */ + public weiAmountMinusSlippage(slippage: number): BigNumber { + return new BigNumber(this._weiAmount).multipliedBy(new BigNumber(1).minus(slippage)); + } + + /** + * Returns wei amount increased by (1 - slippage) times. + * @param slippage Slippage in range from 0 to 1. + */ + public weiAmountPlusSlippage(slippage: number): BigNumber { + return new BigNumber(this._weiAmount).multipliedBy(new BigNumber(1).plus(slippage)); + } + + public clone(tokenStruct?: Partial): TokenAmount { + return new TokenAmount({ ...this.asStruct, ...tokenStruct }); + } +} diff --git a/SDK-mstr/src/common/tokens/token.ts b/SDK-mstr/src/common/tokens/token.ts new file mode 100644 index 0000000..df5d749 --- /dev/null +++ b/SDK-mstr/src/common/tokens/token.ts @@ -0,0 +1,158 @@ +import { RubicSdkError } from 'src/common/errors/rubic-sdk.error'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { TokenBaseStruct } from 'src/common/tokens/models/token-base-struct'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { BLOCKCHAIN_NAME, BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { ChainType } from 'src/core/blockchain/models/chain-type'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { Web3PublicService } from 'src/core/blockchain/web3-public-service/web3-public-service'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; + +import { wrappedAddress } from './constants/wrapped-addresses'; + +export type TokenStruct = { + blockchain: T; + address: string; + name: string; + symbol: string; + decimals: number; +}; + +/** + * Contains main token's fields. + */ +export class Token { + /** + * Creates Token based on token's address and blockchain. + * @param tokenBaseStruct Base token structure. + */ + public static async createToken( + tokenBaseStruct: TokenBaseStruct + ): Promise> { + if (tokenBaseStruct.blockchain === BLOCKCHAIN_NAME.BITCOIN) { + return nativeTokensList[BLOCKCHAIN_NAME.BITCOIN] as Token; + } + if (tokenBaseStruct.blockchain === BLOCKCHAIN_NAME.ICP) { + return nativeTokensList[BLOCKCHAIN_NAME.ICP] as Token; + } + + if (!Web3PublicService.isSupportedBlockchain(tokenBaseStruct.blockchain)) { + throw new RubicSdkError( + `${tokenBaseStruct.blockchain} blockchain is not supported in Token class` + ); + } + const web3Public = Injector.web3PublicService.getWeb3Public(tokenBaseStruct.blockchain); + const tokenInfo = await web3Public.callForTokenInfo(tokenBaseStruct.address); + + if ( + tokenInfo.decimals === undefined || + tokenInfo.name === undefined || + tokenInfo.symbol === undefined + ) { + throw new RubicSdkError('Error while loading token'); + } + + return new Token({ + ...tokenBaseStruct, + name: tokenInfo.name, + symbol: tokenInfo.symbol, + decimals: parseInt(tokenInfo.decimals) + }); + } + + /** + * Creates array of Tokens based on tokens' addresses and blockchain. + */ + public static async createTokens( + tokensAddresses: string[] | ReadonlyArray, + blockchain: T + ): Promise[]> { + if (!Web3PublicService.isSupportedBlockchain(blockchain)) { + throw new RubicSdkError(`${blockchain} blockchain is not supported in Token class`); + } + const web3Public = Injector.web3PublicService.getWeb3Public(blockchain); + const tokenInfo = await web3Public.callForTokensInfo(tokensAddresses); + + return tokenInfo.map((tokenInfo, index) => { + if ( + tokenInfo.decimals === undefined || + tokenInfo.name === undefined || + tokenInfo.symbol === undefined + ) { + throw new RubicSdkError('Error while loading token'); + } + + const address = tokensAddresses?.[index]; + if (!address) { + throw new RubicSdkError('Address has to be defined'); + } + + return new Token({ + address, + blockchain, + name: tokenInfo.name, + symbol: tokenInfo.symbol, + decimals: parseInt(tokenInfo.decimals) + }); + }); + } + + /** + * Maps provided tokens to their addresses. + */ + public static tokensToAddresses(tokens: Token[]): string[] { + return tokens.map(token => token.address); + } + + public readonly blockchain: T; + + public readonly address: string; + + public readonly name: string; + + public readonly symbol: string; + + public readonly decimals: number; + + public get isNative(): boolean { + const chainType: ChainType = BlockchainsInfo.getChainType(this.blockchain); + + if (chainType && Web3Pure[chainType].isNativeAddress(this.address)) { + return Web3Pure[chainType].isNativeAddress(this.address); + } + + return this.address === Web3Pure[chainType].nativeTokenAddress; + } + + public get isWrapped(): boolean { + const address = wrappedAddress[this.blockchain] as string; + if (!address) { + return false; + } + + return compareAddresses(this.address, address); + } + + constructor(tokenStruct: TokenStruct) { + this.blockchain = tokenStruct.blockchain; + this.address = tokenStruct.address; + this.name = tokenStruct.name; + this.symbol = tokenStruct.symbol; + this.decimals = tokenStruct.decimals; + } + + public isEqualTo(token: TokenBaseStruct): boolean { + return ( + token.blockchain === this.blockchain && compareAddresses(token.address, this.address) + ); + } + + public isEqualToTokens(tokens: TokenBaseStruct[]): boolean { + return tokens.some(token => this.isEqualTo(token)); + } + + public clone(tokenStruct?: Partial): Token { + return new Token({ ...this, ...tokenStruct }); + } +} diff --git a/SDK-mstr/src/common/utils/blockchain.ts b/SDK-mstr/src/common/utils/blockchain.ts new file mode 100644 index 0000000..ac03914 --- /dev/null +++ b/SDK-mstr/src/common/utils/blockchain.ts @@ -0,0 +1,6 @@ +/** + * Compares provided addresses case insensitive. + */ +export function compareAddresses(address0: string, address1: string): boolean { + return address0.toLowerCase() === address1.toLowerCase(); +} diff --git a/SDK-mstr/src/common/utils/decorators/cache-decorator/cache.decorator.ts b/SDK-mstr/src/common/utils/decorators/cache-decorator/cache.decorator.ts new file mode 100644 index 0000000..9116ec6 --- /dev/null +++ b/SDK-mstr/src/common/utils/decorators/cache-decorator/cache.decorator.ts @@ -0,0 +1,206 @@ +import { RubicSdkError } from 'src/common/errors'; +import { CacheConfig } from 'src/common/utils/decorators/cache-decorator/models/cache-config'; +import { ConditionalResult } from 'src/common/utils/decorators/cache-decorator/models/conditional-result'; + +type DecoratorSignature = ( + _: Object, + __: string | symbol, + descriptor: TypedPropertyDescriptor +) => TypedPropertyDescriptor | void; + +type CacheItem = { + validUntilTimestamp: number; + value: T | Promise; +}; + +function generateKey(...args: unknown[]): string { + return args.reduce( + (acc, arg) => (Object(arg) === arg ? acc + JSON.stringify(arg) : acc + String(arg)), + '' + ) as string; +} + +function saveResult( + storage: Map>, + key: string, + result: T, + maxAge?: number +): void { + const validUntilTimestamp = maxAge ? Date.now() + maxAge : Infinity; + storage.set(key, { validUntilTimestamp, value: result }); +} + +function buildGetterCacheDescriptor( + propertyKey: string | symbol, + { get, enumerable }: { get: () => T; enumerable?: boolean } +): TypedPropertyDescriptor { + return { + enumerable, + get(): T { + const value = get.call(this); + + Object.defineProperty(this, propertyKey, { + enumerable, + value + }); + + return value; + } + }; +} + +function modifyMethodCacheDescriptor( + cacheConfig: CacheConfig, + descriptor: TypedPropertyDescriptor +): TypedPropertyDescriptor { + const originalMethod = descriptor.value; + const storage = new WeakMap>>(); + + descriptor.value = function method(this: Function, ...args: unknown[]): unknown { + if (!storage.has(this)) { + storage.set(this, new Map>()); + } + + const instanceStore = storage.get(this)!; + const key = generateKey(args); + if (instanceStore.has(key)) { + const cacheItem = instanceStore.get(key)!; + if (cacheItem.validUntilTimestamp > Date.now()) { + return cacheItem.value; + } + instanceStore.delete(key); + } + + let result: T | ConditionalResult = (originalMethod as unknown as Function).apply( + this, + args + ); + + if (cacheConfig.conditionalCache) { + if (result instanceof Promise) { + const handledPromise = result + .then((resolved: ConditionalResult) => { + if (resolved.notSave) { + instanceStore.delete(key); + } + return resolved.value; + }) + .catch(err => { + instanceStore.delete(key); + throw err; + }) as unknown as T; + saveResult(instanceStore, key, handledPromise, cacheConfig.maxAge); + return handledPromise; + } + + result = result as ConditionalResult; + if (result.notSave) { + instanceStore.delete(key); + } else { + saveResult(instanceStore, key, result.value, cacheConfig.maxAge); + } + return result.value; + } + + if (result instanceof Promise) { + const handledPromise = result.catch(err => { + instanceStore.delete(key); + throw err; + }) as unknown as T; + saveResult(instanceStore, key, handledPromise, cacheConfig.maxAge); + return handledPromise; + } + saveResult(instanceStore, key, result as T, cacheConfig.maxAge); + return result; + } as unknown as T; + + return descriptor; +} + +function CacheBuilder(cacheConfig: CacheConfig): DecoratorSignature { + return function cacheBuilder( + _: Object, + propertyKey: string | symbol, + descriptor: TypedPropertyDescriptor + ): TypedPropertyDescriptor | void { + const { get, value: originalMethod } = descriptor; + if (get) { + return buildGetterCacheDescriptor(propertyKey, { + get, + enumerable: descriptor.enumerable + }); + } + + if (!originalMethod) { + throw new RubicSdkError('Descriptor value is undefined.'); + } + + return modifyMethodCacheDescriptor(cacheConfig, descriptor); + }; +} + +/** + * Decorator, used to cache calculated result of functions. + */ +export function Cache(cacheConfigOrTarget: CacheConfig): DecoratorSignature; +export function Cache( + cacheConfigOrTarget: Object, + propertyKey: string | symbol, + descriptor: TypedPropertyDescriptor +): TypedPropertyDescriptor | void; +export function Cache( + cacheConfigOrTarget: Object, + propertyKey?: string | symbol, + descriptor?: TypedPropertyDescriptor +): DecoratorSignature | TypedPropertyDescriptor | void { + const defaultCacheConfig = {}; + + // if decorator called with config as @Cache({ ... }) + if (!propertyKey) { + return CacheBuilder(cacheConfigOrTarget); + } + + // decorator called as @Cache + + if (!descriptor) { + throw new RubicSdkError('Descriptor is undefined'); + } + return CacheBuilder(defaultCacheConfig)(cacheConfigOrTarget, propertyKey, descriptor); +} + +/** + * Decorated function should return {@link ConditionalResult}. + * You have to check types by yourself {@see https://github.com/microsoft/TypeScript/issues/4881} + */ +export function PConditionalCache( + _: Object, + __: string | symbol, + descriptor: TypedPropertyDescriptor +): TypedPropertyDescriptor | void { + const originalMethod = descriptor.value; + if (!originalMethod) { + throw new RubicSdkError('Descriptor value is undefined'); + } + + const storage = new WeakMap>(); + + descriptor.value = async function method(this: Function, ...args: unknown[]): Promise { + if (!storage.has(this)) { + storage.set(this, new Map()); + } + + const instanceStore = storage.get(this)!; + const key = generateKey(args); + if (instanceStore.has(key)) { + return instanceStore.get(key); + } + + const result = await (originalMethod as unknown as Function).apply(this, args); + if (result.notSave) { + instanceStore.delete(key); + } else { + instanceStore.set(key, result.value); + } + return result.value; + } as unknown as T; +} diff --git a/SDK-mstr/src/common/utils/decorators/cache-decorator/models/cache-config.ts b/SDK-mstr/src/common/utils/decorators/cache-decorator/models/cache-config.ts new file mode 100644 index 0000000..8fa28d0 --- /dev/null +++ b/SDK-mstr/src/common/utils/decorators/cache-decorator/models/cache-config.ts @@ -0,0 +1,15 @@ +/** + * Configuration, used for cache decorator. + */ +export interface CacheConfig { + /** + * Amount of time, during which cached result is relevant. + */ + maxAge?: number; + + /** + * If true, then results must be of type {@link ConditionalResult}, + * defining whether to cache calculated result. + */ + conditionalCache?: boolean; +} diff --git a/SDK-mstr/src/common/utils/decorators/cache-decorator/models/conditional-result.ts b/SDK-mstr/src/common/utils/decorators/cache-decorator/models/conditional-result.ts new file mode 100644 index 0000000..e47426d --- /dev/null +++ b/SDK-mstr/src/common/utils/decorators/cache-decorator/models/conditional-result.ts @@ -0,0 +1,8 @@ +/** + * Used for {@link Cache} decorator. + * User `notSave` field to define whether to cache calculated result. + */ +export type ConditionalResult = { + notSave: boolean; + value: T; +}; diff --git a/SDK-mstr/src/common/utils/decorators/index.ts b/SDK-mstr/src/common/utils/decorators/index.ts new file mode 100644 index 0000000..db3dcf9 --- /dev/null +++ b/SDK-mstr/src/common/utils/decorators/index.ts @@ -0,0 +1,2 @@ +export { Cache } from './cache-decorator/cache.decorator'; +export { staticImplements } from './static-implements'; diff --git a/SDK-mstr/src/common/utils/decorators/static-implements.ts b/SDK-mstr/src/common/utils/decorators/static-implements.ts new file mode 100644 index 0000000..91628d8 --- /dev/null +++ b/SDK-mstr/src/common/utils/decorators/static-implements.ts @@ -0,0 +1,6 @@ +/** + * Decorator for classes, which allows to implement static methods through interface. + */ +export function staticImplements() { + return (constructor: U) => constructor; +} diff --git a/SDK-mstr/src/common/utils/errors.ts b/SDK-mstr/src/common/utils/errors.ts new file mode 100644 index 0000000..f6da071 --- /dev/null +++ b/SDK-mstr/src/common/utils/errors.ts @@ -0,0 +1,8 @@ +import { RubicSdkError } from 'src/common/errors'; + +export function parseError(err: unknown, defaultMessage?: string): RubicSdkError { + if (err instanceof RubicSdkError) { + return err; + } + return new RubicSdkError((err as Error)?.message || defaultMessage || 'Unknown error'); +} diff --git a/SDK-mstr/src/common/utils/functions.ts b/SDK-mstr/src/common/utils/functions.ts new file mode 100644 index 0000000..49bd74a --- /dev/null +++ b/SDK-mstr/src/common/utils/functions.ts @@ -0,0 +1,57 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Tuple } from 'ts-essentials'; + +export type SuccessfulCall = { + success: true; + value: T; +}; + +export type ErrorCall = { + success: false; + error: unknown; +}; + +/** + * Wraps result of function in {@link SuccessfulCall} or {@link ErrorCall}. + * @param func Function to calculate. + * @param parameters Parameter of function to calculate. + */ +export function tryExecute( + func: (...args: any[]) => unknown, + parameters: Parameters[] +): SuccessfulCall> | ErrorCall { + try { + const value = func(...parameters); + return { + success: true, + value + }; + } catch (error) { + return { + success: false, + error + }; + } +} +/** + * Wraps result of async function in {@link SuccessfulCall} or {@link ErrorCall}. + * @param func Async function to calculate. + * @param parameters Parameter of function to calculate. + */ +export async function tryExecuteAsync( + func: (...args: T) => Promise, + parameters: Parameters +): Promise | ErrorCall> { + try { + const value = await func(...parameters); + return { + success: true, + value + }; + } catch (error) { + return { + success: false, + error + }; + } +} diff --git a/SDK-mstr/src/common/utils/object.ts b/SDK-mstr/src/common/utils/object.ts new file mode 100644 index 0000000..f9bdfe5 --- /dev/null +++ b/SDK-mstr/src/common/utils/object.ts @@ -0,0 +1,10 @@ +export function cloneObject(obj: T): T { + return JSON.parse(JSON.stringify(obj)); +} + +/** + * Filters object, that can possible be `null`. + */ +export function notNull(obj: T | null): obj is T { + return obj !== null; +} diff --git a/SDK-mstr/src/common/utils/options.ts b/SDK-mstr/src/common/utils/options.ts new file mode 100644 index 0000000..6ad7479 --- /dev/null +++ b/SDK-mstr/src/common/utils/options.ts @@ -0,0 +1,48 @@ +import { EvmTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-transaction-options'; +import { Web3Private } from 'src/core/blockchain/web3-private-service/web3-private/web3-private'; +import { + EIP1559Gas, + SingleGasPrice +} from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/gas-price'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; + +export function combineOptions( + options: Partial | undefined, + defaultOptions: T +): T { + return { + ...defaultOptions, + ...options + }; +} + +export function deadlineMinutesTimestamp(deadlineMinutes: number): number { + return Math.floor(Date.now() / 1000 + 60 * deadlineMinutes); +} + +export function getGasOptions( + options: SwapTransactionOptions | EvmTransactionOptions +): EIP1559Gas | SingleGasPrice | Record { + const { gasPriceOptions } = options; + + if (!gasPriceOptions) return {}; + + if ('gasPrice' in gasPriceOptions && gasPriceOptions.gasPrice) { + return { + gasPrice: Web3Private.stringifyAmount(gasPriceOptions.gasPrice) + }; + } + + if ( + 'maxPriorityFeePerGas' in gasPriceOptions && + gasPriceOptions.maxPriorityFeePerGas && + gasPriceOptions.maxFeePerGas + ) { + return { + maxPriorityFeePerGas: Web3Private.stringifyAmount(gasPriceOptions.maxPriorityFeePerGas), + maxFeePerGas: Web3Private.stringifyAmount(gasPriceOptions.maxFeePerGas) + }; + } + + return {}; +} diff --git a/SDK-mstr/src/common/utils/p-timeout.ts b/SDK-mstr/src/common/utils/p-timeout.ts new file mode 100644 index 0000000..410953c --- /dev/null +++ b/SDK-mstr/src/common/utils/p-timeout.ts @@ -0,0 +1,109 @@ +import { TimeoutError } from 'src/common/errors'; + +export interface ClearablePromise extends Promise { + clear: () => void; +} + +export type PTimeoutOptions = { + readonly customTimers?: { + setTimeout: typeof setTimeout; + clearTimeout: typeof clearTimeout; + }; +}; + +type CancelablePromise = Promise & { cancel: () => void }; + +function isCancelablePromise(promise: PromiseLike): promise is CancelablePromise { + return ( + 'cancel' in promise && + typeof (promise as Promise & { cancel: unknown }).cancel === 'function' + ); +} + +export default function pTimeout( + promise: PromiseLike, + milliseconds: number, + message?: string | Error, + options?: PTimeoutOptions +): ClearablePromise; +export default function pTimeout( + promise: PromiseLike, + milliseconds: number, + fallback?: () => FallbackReturnType | Promise, + options?: PTimeoutOptions +): ClearablePromise; +export default function pTimeout( + promise: PromiseLike, + milliseconds: number, + fallback?: FallbackReturnType extends never + ? string | Error + : () => FallbackReturnType | Promise, + options?: PTimeoutOptions +): ClearablePromise { + let timer: NodeJS.Timeout; + const clearablePromise: ClearablePromise = new Promise((resolve, reject) => { + if (Math.sign(milliseconds) !== 1) { + throw new TypeError( + `Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\`` + ); + } + + if (milliseconds === Number.POSITIVE_INFINITY) { + resolve(promise); + return; + } + + options = { + customTimers: { setTimeout, clearTimeout }, + ...options + }; + + timer = options.customTimers!.setTimeout.call( + undefined, + () => { + if (typeof fallback === 'function') { + try { + resolve(fallback()); + } catch (error) { + reject(error); + } + + return; + } + + const message = + typeof fallback === 'string' + ? fallback + : `Promise timed out after ${milliseconds} milliseconds`; + const timeoutError = + fallback instanceof Error ? fallback : new TimeoutError(message); + + if (isCancelablePromise(promise)) { + promise.cancel(); + } + + reject(timeoutError); + }, + milliseconds + ); + + (async () => { + try { + resolve(await promise); + } catch (error) { + reject(error); + } finally { + options.customTimers!.clearTimeout.call(undefined, timer); + } + })(); + }) as ClearablePromise; + + clearablePromise.clear = () => { + clearTimeout(timer); + timer = undefined as unknown as NodeJS.Timeout; + }; + + return clearablePromise as ClearablePromise< + FallbackReturnType extends never ? ValueType : ValueType | FallbackReturnType + >; +} diff --git a/SDK-mstr/src/common/utils/token-utils.ts b/SDK-mstr/src/common/utils/token-utils.ts new file mode 100644 index 0000000..862548d --- /dev/null +++ b/SDK-mstr/src/common/utils/token-utils.ts @@ -0,0 +1,19 @@ +import BigNumber from 'bignumber.js'; + +export class TokenUtils { + public static getMinWeiAmount(weiAmount: BigNumber, slippage: number): BigNumber { + return weiAmount.multipliedBy(new BigNumber(1).minus(slippage)); + } + + public static getMaxWeiAmount(weiAmount: BigNumber, slippage: number): BigNumber { + return weiAmount.multipliedBy(new BigNumber(1).plus(slippage)); + } + + public static getMinWeiAmountString(weiAmount: BigNumber, slippage: number): string { + return TokenUtils.getMinWeiAmount(weiAmount, slippage).toFixed(0); + } + + public static getMaxWeiAmountString(weiAmount: BigNumber, slippage: number): string { + return TokenUtils.getMaxWeiAmount(weiAmount, slippage).toFixed(0); + } +} diff --git a/SDK-mstr/src/common/utils/types/abstract-constructor-parameters.ts b/SDK-mstr/src/common/utils/types/abstract-constructor-parameters.ts new file mode 100644 index 0000000..0d776da --- /dev/null +++ b/SDK-mstr/src/common/utils/types/abstract-constructor-parameters.ts @@ -0,0 +1,2 @@ +type AbstractConstructorHelper = (new (...args: unknown[]) => { [x: string]: unknown }) & T; +export type AbstractConstructorParameters = ConstructorParameters>; diff --git a/SDK-mstr/src/common/utils/types/any.ts b/SDK-mstr/src/common/utils/types/any.ts new file mode 100644 index 0000000..839f2df --- /dev/null +++ b/SDK-mstr/src/common/utils/types/any.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line +export type Any = any; diff --git a/SDK-mstr/src/common/utils/types/constructor.ts b/SDK-mstr/src/common/utils/types/constructor.ts new file mode 100644 index 0000000..57b71fc --- /dev/null +++ b/SDK-mstr/src/common/utils/types/constructor.ts @@ -0,0 +1,3 @@ +import { Tuple } from 'ts-essentials'; + +export type Constructor = new (...args: A) => R; diff --git a/SDK-mstr/src/common/utils/types/index.ts b/SDK-mstr/src/common/utils/types/index.ts new file mode 100644 index 0000000..29db6c7 --- /dev/null +++ b/SDK-mstr/src/common/utils/types/index.ts @@ -0,0 +1,5 @@ +export { AbstractConstructorParameters } from './abstract-constructor-parameters'; +export { Any } from './any'; +export { Constructor } from './constructor'; +export { InfiniteArray } from './infinite-array'; +export { Mutable } from './mutable'; diff --git a/SDK-mstr/src/common/utils/types/infinite-array.ts b/SDK-mstr/src/common/utils/types/infinite-array.ts new file mode 100644 index 0000000..0bb0048 --- /dev/null +++ b/SDK-mstr/src/common/utils/types/infinite-array.ts @@ -0,0 +1 @@ +export type InfiniteArray = (T | InfiniteArray)[]; diff --git a/SDK-mstr/src/common/utils/types/mutable.ts b/SDK-mstr/src/common/utils/types/mutable.ts new file mode 100644 index 0000000..9f97b8f --- /dev/null +++ b/SDK-mstr/src/common/utils/types/mutable.ts @@ -0,0 +1,3 @@ +export type Mutable = { + -readonly [P in keyof T]: T[P]; +}; diff --git a/SDK-mstr/src/common/utils/waitFor.ts b/SDK-mstr/src/common/utils/waitFor.ts new file mode 100644 index 0000000..f4c067b --- /dev/null +++ b/SDK-mstr/src/common/utils/waitFor.ts @@ -0,0 +1,3 @@ +export async function waitFor(milliseconds: number): Promise { + return new Promise(resolve => setTimeout(resolve, milliseconds)); +} diff --git a/SDK-mstr/src/core/blockchain/constants/healthcheck.ts b/SDK-mstr/src/core/blockchain/constants/healthcheck.ts new file mode 100644 index 0000000..8c1d44a --- /dev/null +++ b/SDK-mstr/src/core/blockchain/constants/healthcheck.ts @@ -0,0 +1,82 @@ +import { BLOCKCHAIN_NAME, BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { ERC20_TOKEN_ABI } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/erc-20-token-abi'; +import { TRC20_CONTRACT_ABI } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/constants/trc-20-contract-abi'; + +export const HEALTHCHECK = { + [BLOCKCHAIN_NAME.ETHEREUM]: { + contractAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7', + contractAbi: ERC20_TOKEN_ABI, + method: 'symbol', + expected: 'USDT' + }, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: { + contractAddress: '0xe9e7cea3dedca5984780bafc599bd69add087d56', + contractAbi: ERC20_TOKEN_ABI, + method: 'symbol', + expected: 'BUSD' + }, + [BLOCKCHAIN_NAME.POLYGON]: { + contractAddress: '0x7FFB3d637014488b63fb9858E279385685AFc1e2', + contractAbi: ERC20_TOKEN_ABI, + method: 'symbol', + expected: 'USDT' + }, + [BLOCKCHAIN_NAME.AVALANCHE]: { + contractAddress: '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', + contractAbi: ERC20_TOKEN_ABI, + method: 'symbol', + expected: 'USDT.e' + }, + [BLOCKCHAIN_NAME.MOONRIVER]: { + contractAddress: '0xB44a9B6905aF7c801311e8F4E76932ee959c663C', + contractAbi: ERC20_TOKEN_ABI, + method: 'symbol', + expected: 'USDT' + }, + [BLOCKCHAIN_NAME.FANTOM]: { + contractAddress: '0x049d68029688eabf473097a2fc38ef61633a3c7a', + contractAbi: ERC20_TOKEN_ABI, + method: 'symbol', + expected: 'fUSDT' + }, + [BLOCKCHAIN_NAME.HARMONY]: { + contractAddress: '0x3c2b8be99c50593081eaa2a724f0b8285f5aba8f', + contractAbi: ERC20_TOKEN_ABI, + method: 'symbol', + expected: '1USDT' + }, + [BLOCKCHAIN_NAME.ARBITRUM]: { + contractAddress: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', + contractAbi: ERC20_TOKEN_ABI, + method: 'symbol', + expected: 'USDT' + }, + [BLOCKCHAIN_NAME.AURORA]: { + contractAddress: '0x4988a896b1227218e4A686fdE5EabdcAbd91571f', + contractAbi: ERC20_TOKEN_ABI, + method: 'symbol', + expected: 'USDT' + }, + [BLOCKCHAIN_NAME.TELOS]: { + contractAddress: '0xefaeee334f0fd1712f9a8cc375f427d9cdd40d73', + contractAbi: ERC20_TOKEN_ABI, + method: 'symbol', + expected: 'USDT' + }, + [BLOCKCHAIN_NAME.TRON]: { + contractAddress: 'TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8', + contractAbi: TRC20_CONTRACT_ABI, + method: 'symbol', + expected: 'USDC' + } +}; + +export type HealthcheckAvailableBlockchain = keyof typeof HEALTHCHECK; + +export const healthcheckAvailableBlockchains = Object.keys(HEALTHCHECK); + +export function isBlockchainHealthcheckAvailable( + blockchainName: BlockchainName +): blockchainName is HealthcheckAvailableBlockchain { + return healthcheckAvailableBlockchains.includes(blockchainName); +} diff --git a/SDK-mstr/src/core/blockchain/constants/solana/native-solana-mint-address.ts b/SDK-mstr/src/core/blockchain/constants/solana/native-solana-mint-address.ts new file mode 100644 index 0000000..370c0b8 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/constants/solana/native-solana-mint-address.ts @@ -0,0 +1 @@ +export const NATIVE_SOLANA_MINT_ADDRESS = 'So11111111111111111111111111111111111111111'; diff --git a/SDK-mstr/src/core/blockchain/constants/tron/tron-web.ts b/SDK-mstr/src/core/blockchain/constants/tron/tron-web.ts new file mode 100644 index 0000000..f27be45 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/constants/tron/tron-web.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line global-require +export const TronWeb = require('tronweb'); diff --git a/SDK-mstr/src/core/blockchain/models/backend-blockchains.ts b/SDK-mstr/src/core/blockchain/models/backend-blockchains.ts new file mode 100644 index 0000000..508e590 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/models/backend-blockchains.ts @@ -0,0 +1,161 @@ +import { BLOCKCHAIN_NAME, BlockchainName } from './blockchain-name'; + +const BLOCKCHAINS_MAPPING = { + [BLOCKCHAIN_NAME.ETHEREUM]: 'ethereum', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: 'binance-smart-chain', + [BLOCKCHAIN_NAME.POLYGON]: 'polygon', + [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: 'polygon-zkevm', + [BLOCKCHAIN_NAME.HARMONY]: 'harmony', + [BLOCKCHAIN_NAME.AVALANCHE]: 'avalanche', + [BLOCKCHAIN_NAME.MOONRIVER]: 'moonriver', + [BLOCKCHAIN_NAME.FANTOM]: 'fantom', + [BLOCKCHAIN_NAME.ARBITRUM]: 'arbitrum', + [BLOCKCHAIN_NAME.AURORA]: 'aurora', + [BLOCKCHAIN_NAME.SOLANA]: 'solana', + [BLOCKCHAIN_NAME.NEAR]: 'near', + [BLOCKCHAIN_NAME.TELOS]: 'telos-evm', + [BLOCKCHAIN_NAME.OPTIMISM]: 'optimistic-ethereum', + [BLOCKCHAIN_NAME.CRONOS]: 'cronos', + [BLOCKCHAIN_NAME.OKE_X_CHAIN]: 'okex-chain', + [BLOCKCHAIN_NAME.GNOSIS]: 'xdai', + [BLOCKCHAIN_NAME.FUSE]: 'fuse', + [BLOCKCHAIN_NAME.MOONBEAM]: 'moonbeam', + [BLOCKCHAIN_NAME.CELO]: 'celo', + [BLOCKCHAIN_NAME.BOBA]: 'boba', + [BLOCKCHAIN_NAME.BOBA_BSC]: 'boba-bsc', + [BLOCKCHAIN_NAME.ASTAR_EVM]: 'astar-evm', + [BLOCKCHAIN_NAME.ASTAR]: 'astar', + [BLOCKCHAIN_NAME.BITCOIN]: 'bitcoin', + [BLOCKCHAIN_NAME.ETHEREUM_POW]: 'ethereum-pow', + [BLOCKCHAIN_NAME.TRON]: 'tron', + [BLOCKCHAIN_NAME.KAVA]: 'kava', + [BLOCKCHAIN_NAME.BITGERT]: 'bitgert', + [BLOCKCHAIN_NAME.OASIS]: 'oasis', + [BLOCKCHAIN_NAME.METIS]: 'metis', + [BLOCKCHAIN_NAME.DFK]: 'defikingdoms', + [BLOCKCHAIN_NAME.KLAYTN]: 'klaytn', + [BLOCKCHAIN_NAME.VELAS]: 'velas', + [BLOCKCHAIN_NAME.SYSCOIN]: 'syscoin', + [BLOCKCHAIN_NAME.EOS]: 'eos', + [BLOCKCHAIN_NAME.ETHEREUM_CLASSIC]: 'ethereum-classic', + [BLOCKCHAIN_NAME.FILECOIN]: 'filecoin', + [BLOCKCHAIN_NAME.FLARE]: 'flare', + [BLOCKCHAIN_NAME.IOTEX]: 'iotex', + [BLOCKCHAIN_NAME.ONTOLOGY]: 'ontology', + [BLOCKCHAIN_NAME.THETA]: 'theta', + [BLOCKCHAIN_NAME.XDC]: 'xdc', + [BLOCKCHAIN_NAME.BITCOIN_CASH]: 'bitcoin-cash', + [BLOCKCHAIN_NAME.ICP]: 'icp', + [BLOCKCHAIN_NAME.CARDANO]: 'cardano', + [BLOCKCHAIN_NAME.AION]: 'aion', + [BLOCKCHAIN_NAME.ALGORAND]: 'algorand', + [BLOCKCHAIN_NAME.APTOS]: 'aptos', + [BLOCKCHAIN_NAME.ARDOR]: 'ardor', + [BLOCKCHAIN_NAME.ARK]: 'ark', + [BLOCKCHAIN_NAME.COSMOS]: 'cosmos', + [BLOCKCHAIN_NAME.BAND_PROTOCOL]: 'band-protocol', + [BLOCKCHAIN_NAME.BITCOIN_DIAMOND]: 'bitcoin-diamond', + [BLOCKCHAIN_NAME.BSV]: 'bsv', + [BLOCKCHAIN_NAME.BITCOIN_GOLD]: 'bitcoin-gold', + [BLOCKCHAIN_NAME.CASPER]: 'casper', + [BLOCKCHAIN_NAME.DASH]: 'dash', + [BLOCKCHAIN_NAME.DECRED]: 'decred', + [BLOCKCHAIN_NAME.DIGI_BYTE]: 'digibyte', + [BLOCKCHAIN_NAME.DIVI]: 'divi', + [BLOCKCHAIN_NAME.DOGECOIN]: 'dogecoin', + [BLOCKCHAIN_NAME.POLKADOT]: 'polkadot', + [BLOCKCHAIN_NAME.MULTIVERS_X]: 'multiversx', + [BLOCKCHAIN_NAME.FIO_PROTOCOL]: 'fio-protocol', + [BLOCKCHAIN_NAME.FIRO]: 'firo', + [BLOCKCHAIN_NAME.FLOW]: 'flow', + [BLOCKCHAIN_NAME.HEDERA]: 'hedera', + [BLOCKCHAIN_NAME.HELIUM]: 'helium', + [BLOCKCHAIN_NAME.ICON]: 'icon', + [BLOCKCHAIN_NAME.IOST]: 'iost', + [BLOCKCHAIN_NAME.IOTA]: 'iota', + [BLOCKCHAIN_NAME.KADENA]: 'kadena', + [BLOCKCHAIN_NAME.KOMODO]: 'komodo', + [BLOCKCHAIN_NAME.KUSAMA]: 'kusama', + [BLOCKCHAIN_NAME.LISK]: 'lisk', + [BLOCKCHAIN_NAME.LITECOIN]: 'litecoin', + [BLOCKCHAIN_NAME.TERRA]: 'terra', + [BLOCKCHAIN_NAME.TERRA_CLASSIC]: 'terra-classic', + [BLOCKCHAIN_NAME.MINA_PROTOCOL]: 'mina-protocol', + [BLOCKCHAIN_NAME.NANO]: 'nano', + [BLOCKCHAIN_NAME.NEO]: 'neo', + [BLOCKCHAIN_NAME.OSMOSIS]: 'osmosis', + [BLOCKCHAIN_NAME.PIVX]: 'pivx', + [BLOCKCHAIN_NAME.POLYX]: 'polyx', + [BLOCKCHAIN_NAME.QTUM]: 'qtum', + [BLOCKCHAIN_NAME.THOR_CHAIN]: 'thorchain', + [BLOCKCHAIN_NAME.RAVENCOIN]: 'ravencoin', + [BLOCKCHAIN_NAME.SIA]: 'sia', + [BLOCKCHAIN_NAME.SECRET]: 'secret', + [BLOCKCHAIN_NAME.STACKS]: 'stacks', + [BLOCKCHAIN_NAME.STARKNET]: 'starknet', + [BLOCKCHAIN_NAME.STEEM]: 'steem', + [BLOCKCHAIN_NAME.STRATIS]: 'stratis', + [BLOCKCHAIN_NAME.SOLAR]: 'solar', + [BLOCKCHAIN_NAME.TON]: 'ton', + [BLOCKCHAIN_NAME.VE_CHAIN]: 'vechain', + [BLOCKCHAIN_NAME.WAVES]: 'waves', + [BLOCKCHAIN_NAME.WAX]: 'wax', + [BLOCKCHAIN_NAME.DX_CHAIN]: 'dxchain', + [BLOCKCHAIN_NAME.E_CASH]: 'ecash', + [BLOCKCHAIN_NAME.NEM]: 'nem', + [BLOCKCHAIN_NAME.STELLAR]: 'stellar', + [BLOCKCHAIN_NAME.MONERO]: 'monero', + [BLOCKCHAIN_NAME.RIPPLE]: 'ripple', + [BLOCKCHAIN_NAME.TEZOS]: 'tezos', + [BLOCKCHAIN_NAME.VERGE]: 'verge', + [BLOCKCHAIN_NAME.SYMBOL]: 'symbol', + [BLOCKCHAIN_NAME.ZCASH]: 'zcash', + [BLOCKCHAIN_NAME.HORIZEN]: 'horizen', + [BLOCKCHAIN_NAME.ZILLIQA]: 'zilliqa', + [BLOCKCHAIN_NAME.KAVA_COSMOS]: 'kava-cosmos', + [BLOCKCHAIN_NAME.ZK_SYNC]: 'zksync', + [BLOCKCHAIN_NAME.PULSECHAIN]: 'pulsechain', + [BLOCKCHAIN_NAME.LINEA]: 'linea', + [BLOCKCHAIN_NAME.BASE]: 'base', + [BLOCKCHAIN_NAME.MANTLE]: 'mantle', + [BLOCKCHAIN_NAME.MUMBAI]: 'polygon-mumbai', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN_TESTNET]: 'binance-smart-chain-testnet', + [BLOCKCHAIN_NAME.GOERLI]: 'goerli', + [BLOCKCHAIN_NAME.FUJI]: 'avalanche-fuji', + [BLOCKCHAIN_NAME.SCROLL_SEPOLIA]: 'scroll-sepolia-testnet', + [BLOCKCHAIN_NAME.ARTHERA]: 'arthera-testnet', + [BLOCKCHAIN_NAME.ZETACHAIN]: 'zetachain', + [BLOCKCHAIN_NAME.SEPOLIA]: 'sepolia', + [BLOCKCHAIN_NAME.MANTA_PACIFIC]: 'manta-pacific', + [BLOCKCHAIN_NAME.SCROLL]: 'scroll', + [BLOCKCHAIN_NAME.BERACHAIN]: 'berachain', + [BLOCKCHAIN_NAME.BLAST_TESTNET]: 'blast', + [BLOCKCHAIN_NAME.BLAST]: 'blast', + [BLOCKCHAIN_NAME.HOLESKY]: 'holesky', + [BLOCKCHAIN_NAME.ROOTSTOCK]: 'rootstock', + [BLOCKCHAIN_NAME.KROMA]: 'kroma', + [BLOCKCHAIN_NAME.HORIZEN_EON]: 'horizen-eon', + [BLOCKCHAIN_NAME.MERLIN]: 'merlin', + [BLOCKCHAIN_NAME.MODE]: 'mode', + [BLOCKCHAIN_NAME.ZK_FAIR]: 'zkfair', + [BLOCKCHAIN_NAME.ZK_LINK]: 'zklink', + [BLOCKCHAIN_NAME.XLAYER]: 'xlayer', + [BLOCKCHAIN_NAME.TAIKO]: 'taiko', + [BLOCKCHAIN_NAME.SEI]: 'sei' +} as const; + +export const TO_BACKEND_BLOCKCHAINS: Record = { + ...BLOCKCHAINS_MAPPING +} as const; + +export type BackendBlockchain = (typeof BLOCKCHAINS_MAPPING)[keyof typeof BLOCKCHAINS_MAPPING]; + +export const FROM_BACKEND_BLOCKCHAINS: Record = ( + Object.keys(BLOCKCHAINS_MAPPING) as BlockchainName[] +).reduce( + (acc, blockchain) => ({ + ...acc, + [BLOCKCHAINS_MAPPING[blockchain]]: blockchain + }), + {} as Record +); diff --git a/SDK-mstr/src/core/blockchain/models/blockchain-name.ts b/SDK-mstr/src/core/blockchain/models/blockchain-name.ts new file mode 100644 index 0000000..6250f94 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/models/blockchain-name.ts @@ -0,0 +1,162 @@ +export const TEST_EVM_BLOCKCHAIN_NAME = { + MUMBAI: 'MUMBAI', + BINANCE_SMART_CHAIN_TESTNET: 'BSCT', + GOERLI: 'GOERLI', + FUJI: 'FUJI', + SCROLL_SEPOLIA: 'SCROLL_SEPOLIA', + ARTHERA: 'ARTHERA', + SEPOLIA: 'SEPOLIA', + BERACHAIN: 'BERACHAIN', + BLAST_TESTNET: 'BLAST_TESTNET', + HOLESKY: 'HOLESKY' +} as const; + +export const EVM_BLOCKCHAIN_NAME = { + ...TEST_EVM_BLOCKCHAIN_NAME, + ETHEREUM: 'ETH', + BINANCE_SMART_CHAIN: 'BSC', + POLYGON: 'POLYGON', + POLYGON_ZKEVM: 'POLYGON_ZKEVM', + AVALANCHE: 'AVALANCHE', + MOONRIVER: 'MOONRIVER', + FANTOM: 'FANTOM', + HARMONY: 'HARMONY', + ARBITRUM: 'ARBITRUM', + AURORA: 'AURORA', + TELOS: 'TELOS', + OPTIMISM: 'OPTIMISM', + CRONOS: 'CRONOS', + OKE_X_CHAIN: 'OKX', + GNOSIS: 'GNOSIS', + FUSE: 'FUSE', + MOONBEAM: 'MOONBEAM', + CELO: 'CELO', + BOBA: 'BOBA', + BOBA_BSC: 'BOBA_BSC', + ASTAR_EVM: 'ASTAR_EVM', + ETHEREUM_POW: 'ETHW', + KAVA: 'KAVA', + BITGERT: 'BITGERT', + OASIS: 'OASIS', + METIS: 'METIS', + DFK: 'DEFIKINGDOMS', + KLAYTN: 'KLAYTN', + VELAS: 'VELAS', + STARKNET: 'STARKNET', + SYSCOIN: 'SYSCOIN', + ETHEREUM_CLASSIC: 'ETHEREUM_CLASSIC', + FLARE: 'FLARE', + IOTEX: 'IOTEX', + THETA: 'THETA', + BITCOIN_CASH: 'BITCOIN_CASH', + ZK_SYNC: 'ZK_SYNC', + PULSECHAIN: 'PULSECHAIN', + LINEA: 'LINEA', + BASE: 'BASE', + MANTLE: 'MANTLE', + MANTA_PACIFIC: 'MANTA_PACIFIC', + SCROLL: 'SCROLL', + ZETACHAIN: 'ZETACHAIN', + BLAST: 'BLAST', + KROMA: 'KROMA', + HORIZEN_EON: 'HORIZEN_EON', + MERLIN: 'MERLIN', + ROOTSTOCK: 'ROOTSTOCK', + MODE: 'MODE', + ZK_FAIR: 'ZK_FAIR', + ZK_LINK: 'ZK_LINK', + XLAYER: 'XLAYER', + TAIKO: 'TAIKO', + SEI: 'SEI' +} as const; + +export const BLOCKCHAIN_NAME = { + ...EVM_BLOCKCHAIN_NAME, + SOLANA: 'SOLANA', + NEAR: 'NEAR', + BITCOIN: 'BITCOIN', + TRON: 'TRON', + ICP: 'ICP', + CARDANO: 'CARDANO', + AION: 'AION', + ALGORAND: 'ALGORAND', + APTOS: 'APTOS', + ARDOR: 'ARDOR', + ARK: 'ARK', + ASTAR: 'ASTAR', + COSMOS: 'COSMOS', + BAND_PROTOCOL: 'BAND_PROTOCOL', + BITCOIN_DIAMOND: 'BITCOIN_DIAMOND', + BSV: 'BSV', + BITCOIN_GOLD: 'BITCOIN_GOLD', + CASPER: 'CASPER', + DASH: 'DASH', + DECRED: 'DECRED', + DIGI_BYTE: 'DIGI_BYTE', + DIVI: 'DIVI', + DOGECOIN: 'DOGECOIN', + POLKADOT: 'POLKADOT', + MULTIVERS_X: 'MULTIVERS_X', + FIO_PROTOCOL: 'FIO_PROTOCOL', + FIRO: 'FIRO', + FLOW: 'FLOW', + HEDERA: 'HEDERA', + HELIUM: 'HELIUM', + ICON: 'ICON', + IOST: 'IOST', + IOTA: 'IOTA', + KADENA: 'KADENA', + KOMODO: 'KOMODO', + KUSAMA: 'KUSAMA', + LISK: 'LISK', + LITECOIN: 'LITECOIN', + TERRA: 'TERRA', + TERRA_CLASSIC: 'TERRA_CLASSIC', + MINA_PROTOCOL: 'MINA_PROTOCOL', + NANO: 'NANO', + NEO: 'NEO', + OSMOSIS: 'OSMOSIS', + PIVX: 'PIVX', + POLYX: 'POLYX', + QTUM: 'QTUM', + THOR_CHAIN: 'THOR_CHAIN', + RAVENCOIN: 'RAVENCOIN', + SIA: 'SIA', + SECRET: 'SECRET', + STEEM: 'STEEM', + STRATIS: 'STRATIS', + STACKS: 'STACKS', + SOLAR: 'SOLAR', + TON: 'TON', + VE_CHAIN: 'VE_CHAIN', + WAVES: 'WAVES', + WAX: 'WAX', + DX_CHAIN: 'DX_CHAIN', + E_CASH: 'E_CASH', + NEM: 'NEM', + STELLAR: 'STELLAR', + MONERO: 'MONERO', + RIPPLE: 'RIPPLE', + TEZOS: 'TEZOS', + VERGE: 'VERGE', + SYMBOL: 'SYMBOL', + ZCASH: 'ZCASH', + HORIZEN: 'HORIZEN', + ZILLIQA: 'ZILLIQA', + KAVA_COSMOS: 'KAVA_COSMOS', + FILECOIN: 'FILECOIN', + EOS: 'EOS', + ONTOLOGY: 'ONTOLOGY', + XDC: 'XDC' +} as const; + +export type BlockchainName = (typeof BLOCKCHAIN_NAME)[keyof typeof BLOCKCHAIN_NAME]; + +export type TestnetEvmBlockchain = + (typeof TEST_EVM_BLOCKCHAIN_NAME)[keyof typeof TEST_EVM_BLOCKCHAIN_NAME]; +export type EvmBlockchainName = (typeof EVM_BLOCKCHAIN_NAME)[keyof typeof EVM_BLOCKCHAIN_NAME]; +export type SolanaBlockchainName = typeof BLOCKCHAIN_NAME.SOLANA; +export type NearBlockchainName = typeof BLOCKCHAIN_NAME.NEAR; +export type BitcoinBlockchainName = typeof BLOCKCHAIN_NAME.BITCOIN; +export type TronBlockchainName = typeof BLOCKCHAIN_NAME.TRON; +export type IcpBlockchainName = typeof BLOCKCHAIN_NAME.ICP; diff --git a/SDK-mstr/src/core/blockchain/models/chain-type.ts b/SDK-mstr/src/core/blockchain/models/chain-type.ts new file mode 100644 index 0000000..d2bdc80 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/models/chain-type.ts @@ -0,0 +1,81 @@ +export const CHAIN_TYPE = { + EVM: 'EVM', + BITCOIN: 'BITCOIN', + TRON: 'TRON', + ICP: 'ICP', + SOLANA: 'SOLANA', + NEAR: 'NEAR', + CARDANO: 'CARDANO', + AION: 'AION', + ALGORAND: 'ALGORAND', + APTOS: 'APTOS', + ARDOR: 'ARDOR', + ARK: 'ARK', + ASTAR: 'ASTAR', + COSMOS: 'COSMOS', + BAND_PROTOCOL: 'BAND_PROTOCOL', + BITCOIN_DIAMOND: 'BITCOIN_DIAMOND', + BSV: 'BSV', + BITCOIN_GOLD: 'BITCOIN_GOLD', + CASPER: 'CASPER', + DASH: 'DASH', + DECRED: 'DECRED', + DIGI_BYTE: 'DIGI_BYTE', + DIVI: 'DIVI', + DOGECOIN: 'DOGECOIN', + POLKADOT: 'POLKADOT', + MULTIVERS_X: 'MULTIVERS_X', + FIO_PROTOCOL: 'FIO_PROTOCOL', + FIRO: 'FIRO', + FLOW: 'FLOW', + HEDERA: 'HEDERA', + HELIUM: 'HELIUM', + ICON: 'ICON', + IOST: 'IOST', + IOTA: 'IOTA', + KADENA: 'KADENA', + KOMODO: 'KOMODO', + KUSAMA: 'KUSAMA', + LISK: 'LISK', + LITECOIN: 'LITECOIN', + TERRA: 'TERRA', + TERRA_CLASSIC: 'TERRA_CLASSIC', + MINA_PROTOCOL: 'MINA_PROTOCOL', + NANO: 'NANO', + NEO: 'NEO', + OSMOSIS: 'OSMOSIS', + PIVX: 'PIVX', + POLYX: 'POLYX', + QTUM: 'QTUM', + THOR_CHAIN: 'THOR_CHAIN', + RAVENCOIN: 'RAVENCOIN', + SIA: 'SIA', + SECRET: 'SECRET', + STACKS: 'STACKS', + STEEM: 'STEEM', + STRATIS: 'STRATIS', + SOLAR: 'SOLAR', + TON: 'TON', + VE_CHAIN: 'VE_CHAIN', + WAVES: 'WAVES', + WAX: 'WAX', + DX_CHAIN: 'DX_CHAIN', + E_CASH: 'E_CASH', + NEM: 'NEM', + STELLAR: 'STELLAR', + MONERO: 'MONERO', + RIPPLE: 'RIPPLE', + TEZOS: 'TEZOS', + VERGE: 'VERGE', + SYMBOL: 'SYMBOL', + ZCASH: 'ZCASH', + HORIZEN: 'HORIZEN', + ZILLIQA: 'ZILLIQA', + KAVA_COSMOS: 'KAVA_COSMOS', + XDC: 'XDC', + ONTOLOGY: 'ONTOLOGY', + EOS: 'EOS', + FILECOIN: 'FILECOIN' +} as const; + +export type ChainType = (typeof CHAIN_TYPE)[keyof typeof CHAIN_TYPE]; diff --git a/SDK-mstr/src/core/blockchain/models/solana-web3-types.ts b/SDK-mstr/src/core/blockchain/models/solana-web3-types.ts new file mode 100644 index 0000000..34a647c --- /dev/null +++ b/SDK-mstr/src/core/blockchain/models/solana-web3-types.ts @@ -0,0 +1,23 @@ +import { AccountInfo, PublicKey, RpcResponseAndContext } from '@solana/web3.js'; + +/** + * RPC response value. + */ +export type ReturnValue = Promise<{ + result: RpcResponseAndContext< + Array<{ + pubkey: PublicKey; + account: AccountInfo<{ + parsed: { + info: { + tokenAmount: { + amount: number; + decimals: number; + }; + mint: string; + }; + }; + }>; + }> + >; +}>; diff --git a/SDK-mstr/src/core/blockchain/models/web3-primitive-type.ts b/SDK-mstr/src/core/blockchain/models/web3-primitive-type.ts new file mode 100644 index 0000000..bfe32af --- /dev/null +++ b/SDK-mstr/src/core/blockchain/models/web3-primitive-type.ts @@ -0,0 +1,14 @@ +import { BigNumber as EthersBigNumber } from 'ethers'; + +export type Web3PrimitiveType = + | string + | boolean + | Web3PrimitiveType[] + | { [key: string]: Web3PrimitiveType }; + +export type TronWeb3PrimitiveType = + | number + | EthersBigNumber + | Web3PrimitiveType + | TronWeb3PrimitiveType[] + | { [key: string]: TronWeb3PrimitiveType }; diff --git a/SDK-mstr/src/core/blockchain/utils/blockchains-info/blockchains-info.ts b/SDK-mstr/src/core/blockchain/utils/blockchains-info/blockchains-info.ts new file mode 100644 index 0000000..b8933a7 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/utils/blockchains-info/blockchains-info.ts @@ -0,0 +1,78 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { + BitcoinBlockchainName, + BLOCKCHAIN_NAME, + BlockchainName, + EVM_BLOCKCHAIN_NAME, + EvmBlockchainName, + SolanaBlockchainName, + TEST_EVM_BLOCKCHAIN_NAME, + TestnetEvmBlockchain, + TronBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { ChainType } from 'src/core/blockchain/models/chain-type'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { chainTypeByBlockchain } from 'src/core/blockchain/utils/blockchains-info/constants/chain-type-by-blockchain'; + +/** + * Works with list of all used in project blockchains. + * Contains method to find info about certain blockchain. + */ +export class BlockchainsInfo { + /** + * Finds blockchain name, based on provided chain id. + */ + public static getBlockchainNameById(chainId: string | number): BlockchainName | undefined { + const chainIdNumber = new BigNumber(chainId).toNumber(); + return Object.entries(blockchainId).find( + ([_, id]) => id === chainIdNumber + )?.[0] as BlockchainName; + } + + public static getChainType(blockchainName: BlockchainName): ChainType { + const chainType = chainTypeByBlockchain[blockchainName]; + if (!chainType) { + throw new RubicSdkError(`No supported chain type for ${blockchainName}`); + } + return chainType; + } + + public static isBlockchainName(chain: string): chain is BlockchainName { + return Object.values(BLOCKCHAIN_NAME).some(blockchainName => blockchainName === chain); + } + + public static isEvmBlockchainName( + blockchainName: BlockchainName + ): blockchainName is EvmBlockchainName { + return Object.values(EVM_BLOCKCHAIN_NAME).some( + evmBlockchainName => evmBlockchainName === blockchainName + ); + } + + public static isTestBlockchainName( + blockchainName: BlockchainName + ): blockchainName is TestnetEvmBlockchain { + return Object.values(TEST_EVM_BLOCKCHAIN_NAME).some( + testBlockchainName => testBlockchainName === blockchainName + ); + } + + public static isBitcoinBlockchainName( + blockchainName: BlockchainName + ): blockchainName is BitcoinBlockchainName { + return blockchainName === BLOCKCHAIN_NAME.BITCOIN; + } + + public static isTronBlockchainName( + blockchainName: BlockchainName + ): blockchainName is TronBlockchainName { + return blockchainName === BLOCKCHAIN_NAME.TRON; + } + + public static isSolanaBlockchainName( + blockchainName: BlockchainName + ): blockchainName is SolanaBlockchainName { + return blockchainName === BLOCKCHAIN_NAME.SOLANA; + } +} diff --git a/SDK-mstr/src/core/blockchain/utils/blockchains-info/constants/blockchain-id.ts b/SDK-mstr/src/core/blockchain/utils/blockchains-info/constants/blockchain-id.ts new file mode 100644 index 0000000..c348fac --- /dev/null +++ b/SDK-mstr/src/core/blockchain/utils/blockchains-info/constants/blockchain-id.ts @@ -0,0 +1,84 @@ +import { BLOCKCHAIN_NAME, BlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +const otherChains = Object.values(BLOCKCHAIN_NAME).reduce( + (acc, blockchain) => ({ ...acc, [blockchain]: NaN }), + {} as Record +); + +export const blockchainId: Record = { + ...otherChains, + // EVN blockchains + [BLOCKCHAIN_NAME.ETHEREUM]: 1, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: 56, + [BLOCKCHAIN_NAME.POLYGON]: 137, + [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: 1101, + [BLOCKCHAIN_NAME.AVALANCHE]: 43114, + [BLOCKCHAIN_NAME.MOONRIVER]: 1285, + [BLOCKCHAIN_NAME.FANTOM]: 250, + [BLOCKCHAIN_NAME.HARMONY]: 1666600000, + [BLOCKCHAIN_NAME.ARBITRUM]: 42161, + [BLOCKCHAIN_NAME.AURORA]: 1313161554, + [BLOCKCHAIN_NAME.TELOS]: 40, + [BLOCKCHAIN_NAME.OPTIMISM]: 10, + [BLOCKCHAIN_NAME.CRONOS]: 25, + [BLOCKCHAIN_NAME.OKE_X_CHAIN]: 66, + [BLOCKCHAIN_NAME.GNOSIS]: 100, + [BLOCKCHAIN_NAME.FUSE]: 122, + [BLOCKCHAIN_NAME.MOONBEAM]: 1284, + [BLOCKCHAIN_NAME.CELO]: 42220, + [BLOCKCHAIN_NAME.BOBA]: 288, + [BLOCKCHAIN_NAME.BOBA_BSC]: 56288, + [BLOCKCHAIN_NAME.ASTAR_EVM]: 592, + [BLOCKCHAIN_NAME.ETHEREUM_POW]: 10001, + [BLOCKCHAIN_NAME.KAVA]: 2222, + [BLOCKCHAIN_NAME.TRON]: 195, + [BLOCKCHAIN_NAME.BITGERT]: 32520, + [BLOCKCHAIN_NAME.OASIS]: 42262, + [BLOCKCHAIN_NAME.METIS]: 1088, + [BLOCKCHAIN_NAME.DFK]: 53935, + [BLOCKCHAIN_NAME.KLAYTN]: 8217, + [BLOCKCHAIN_NAME.VELAS]: 106, + [BLOCKCHAIN_NAME.SYSCOIN]: 57, + [BLOCKCHAIN_NAME.EOS]: 59, + [BLOCKCHAIN_NAME.ETHEREUM_CLASSIC]: 61, + [BLOCKCHAIN_NAME.FLARE]: 14, + [BLOCKCHAIN_NAME.IOTEX]: 4689, + [BLOCKCHAIN_NAME.ONTOLOGY]: 58, + [BLOCKCHAIN_NAME.THETA]: 361, + [BLOCKCHAIN_NAME.XDC]: 50, + [BLOCKCHAIN_NAME.BITCOIN_CASH]: 10000, + [BLOCKCHAIN_NAME.ZK_SYNC]: 324, + [BLOCKCHAIN_NAME.PULSECHAIN]: 369, + [BLOCKCHAIN_NAME.LINEA]: 59144, + [BLOCKCHAIN_NAME.BASE]: 8453, + [BLOCKCHAIN_NAME.MANTLE]: 5000, + [BLOCKCHAIN_NAME.MANTA_PACIFIC]: 169, + [BLOCKCHAIN_NAME.SCROLL]: 534352, + [BLOCKCHAIN_NAME.ZETACHAIN]: 7000, + [BLOCKCHAIN_NAME.BLAST]: 81457, + [BLOCKCHAIN_NAME.KROMA]: 255, + [BLOCKCHAIN_NAME.HORIZEN_EON]: 7332, + [BLOCKCHAIN_NAME.MERLIN]: 4200, + [BLOCKCHAIN_NAME.ROOTSTOCK]: 30, + [BLOCKCHAIN_NAME.MODE]: 34443, + [BLOCKCHAIN_NAME.ZK_FAIR]: 42766, + [BLOCKCHAIN_NAME.ZK_LINK]: 810180, + [BLOCKCHAIN_NAME.XLAYER]: 196, + [BLOCKCHAIN_NAME.TAIKO]: 167000, + [BLOCKCHAIN_NAME.SEI]: 1329, + // Tesnents + [BLOCKCHAIN_NAME.GOERLI]: 5, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN_TESTNET]: 87, + [BLOCKCHAIN_NAME.MUMBAI]: 80001, + [BLOCKCHAIN_NAME.FUJI]: 43113, + [BLOCKCHAIN_NAME.SCROLL_SEPOLIA]: 534351, + [BLOCKCHAIN_NAME.ARTHERA]: 10243, + [BLOCKCHAIN_NAME.SEPOLIA]: 11155111, + [BLOCKCHAIN_NAME.BERACHAIN]: 80085, + [BLOCKCHAIN_NAME.BLAST_TESTNET]: 168587773, + [BLOCKCHAIN_NAME.HOLESKY]: 17000, + // Non EVN blockchains + [BLOCKCHAIN_NAME.BITCOIN]: 5555, + [BLOCKCHAIN_NAME.FILECOIN]: 314, + [BLOCKCHAIN_NAME.SOLANA]: 7565164 +}; diff --git a/SDK-mstr/src/core/blockchain/utils/blockchains-info/constants/chain-type-by-blockchain.ts b/SDK-mstr/src/core/blockchain/utils/blockchains-info/constants/chain-type-by-blockchain.ts new file mode 100644 index 0000000..a8c8166 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/utils/blockchains-info/constants/chain-type-by-blockchain.ts @@ -0,0 +1,95 @@ +import { + BLOCKCHAIN_NAME, + BlockchainName, + EVM_BLOCKCHAIN_NAME, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { CHAIN_TYPE, ChainType } from 'src/core/blockchain/models/chain-type'; + +export const chainTypeByBlockchain: Record = { + ...Object.values(EVM_BLOCKCHAIN_NAME).reduce( + (acc, evmBlockchainName) => ({ + ...acc, + [evmBlockchainName]: CHAIN_TYPE.EVM + }), + {} as Record + ), + [BLOCKCHAIN_NAME.BITCOIN]: CHAIN_TYPE.BITCOIN, + [BLOCKCHAIN_NAME.TRON]: CHAIN_TYPE.TRON, + [BLOCKCHAIN_NAME.ICP]: CHAIN_TYPE.ICP, + + [BLOCKCHAIN_NAME.SOLANA]: CHAIN_TYPE.SOLANA, + [BLOCKCHAIN_NAME.NEAR]: CHAIN_TYPE.NEAR, + [BLOCKCHAIN_NAME.CARDANO]: CHAIN_TYPE.CARDANO, + [BLOCKCHAIN_NAME.AION]: CHAIN_TYPE.AION, + [BLOCKCHAIN_NAME.ALGORAND]: CHAIN_TYPE.ALGORAND, + [BLOCKCHAIN_NAME.APTOS]: CHAIN_TYPE.APTOS, + [BLOCKCHAIN_NAME.ARDOR]: CHAIN_TYPE.ARDOR, + [BLOCKCHAIN_NAME.ARK]: CHAIN_TYPE.ARK, + [BLOCKCHAIN_NAME.ASTAR]: CHAIN_TYPE.ASTAR, + [BLOCKCHAIN_NAME.COSMOS]: CHAIN_TYPE.COSMOS, + [BLOCKCHAIN_NAME.BAND_PROTOCOL]: CHAIN_TYPE.BAND_PROTOCOL, + [BLOCKCHAIN_NAME.BITCOIN_DIAMOND]: CHAIN_TYPE.BITCOIN_DIAMOND, + [BLOCKCHAIN_NAME.BSV]: CHAIN_TYPE.BSV, + [BLOCKCHAIN_NAME.BITCOIN_GOLD]: CHAIN_TYPE.BITCOIN_GOLD, + [BLOCKCHAIN_NAME.CASPER]: CHAIN_TYPE.CASPER, + [BLOCKCHAIN_NAME.DASH]: CHAIN_TYPE.DASH, + [BLOCKCHAIN_NAME.DECRED]: CHAIN_TYPE.DECRED, + [BLOCKCHAIN_NAME.DIGI_BYTE]: CHAIN_TYPE.DIGI_BYTE, + [BLOCKCHAIN_NAME.DIVI]: CHAIN_TYPE.DIVI, + [BLOCKCHAIN_NAME.DOGECOIN]: CHAIN_TYPE.DOGECOIN, + [BLOCKCHAIN_NAME.POLKADOT]: CHAIN_TYPE.POLKADOT, + [BLOCKCHAIN_NAME.MULTIVERS_X]: CHAIN_TYPE.MULTIVERS_X, + [BLOCKCHAIN_NAME.FIO_PROTOCOL]: CHAIN_TYPE.FIO_PROTOCOL, + [BLOCKCHAIN_NAME.FIRO]: CHAIN_TYPE.FIRO, + [BLOCKCHAIN_NAME.FLOW]: CHAIN_TYPE.FLOW, + [BLOCKCHAIN_NAME.HEDERA]: CHAIN_TYPE.HEDERA, + [BLOCKCHAIN_NAME.HELIUM]: CHAIN_TYPE.HELIUM, + [BLOCKCHAIN_NAME.ICON]: CHAIN_TYPE.ICON, + [BLOCKCHAIN_NAME.IOST]: CHAIN_TYPE.IOST, + [BLOCKCHAIN_NAME.IOTA]: CHAIN_TYPE.IOTA, + [BLOCKCHAIN_NAME.KADENA]: CHAIN_TYPE.KADENA, + [BLOCKCHAIN_NAME.KOMODO]: CHAIN_TYPE.KOMODO, + [BLOCKCHAIN_NAME.KUSAMA]: CHAIN_TYPE.KUSAMA, + [BLOCKCHAIN_NAME.LISK]: CHAIN_TYPE.LISK, + [BLOCKCHAIN_NAME.LITECOIN]: CHAIN_TYPE.LITECOIN, + [BLOCKCHAIN_NAME.TERRA]: CHAIN_TYPE.TERRA, + [BLOCKCHAIN_NAME.TERRA_CLASSIC]: CHAIN_TYPE.TERRA_CLASSIC, + [BLOCKCHAIN_NAME.MINA_PROTOCOL]: CHAIN_TYPE.MINA_PROTOCOL, + [BLOCKCHAIN_NAME.NANO]: CHAIN_TYPE.NANO, + [BLOCKCHAIN_NAME.NEO]: CHAIN_TYPE.NEO, + [BLOCKCHAIN_NAME.OSMOSIS]: CHAIN_TYPE.OSMOSIS, + [BLOCKCHAIN_NAME.PIVX]: CHAIN_TYPE.PIVX, + [BLOCKCHAIN_NAME.POLYX]: CHAIN_TYPE.POLYX, + [BLOCKCHAIN_NAME.QTUM]: CHAIN_TYPE.QTUM, + [BLOCKCHAIN_NAME.THOR_CHAIN]: CHAIN_TYPE.THOR_CHAIN, + [BLOCKCHAIN_NAME.RAVENCOIN]: CHAIN_TYPE.RAVENCOIN, + [BLOCKCHAIN_NAME.SIA]: CHAIN_TYPE.SIA, + [BLOCKCHAIN_NAME.SECRET]: CHAIN_TYPE.SECRET, + [BLOCKCHAIN_NAME.STEEM]: CHAIN_TYPE.STEEM, + [BLOCKCHAIN_NAME.STRATIS]: CHAIN_TYPE.STRATIS, + [BLOCKCHAIN_NAME.STACKS]: CHAIN_TYPE.STACKS, + [BLOCKCHAIN_NAME.SOLAR]: CHAIN_TYPE.SOLAR, + [BLOCKCHAIN_NAME.TON]: CHAIN_TYPE.TON, + [BLOCKCHAIN_NAME.VE_CHAIN]: CHAIN_TYPE.VE_CHAIN, + [BLOCKCHAIN_NAME.WAVES]: CHAIN_TYPE.WAVES, + [BLOCKCHAIN_NAME.WAX]: CHAIN_TYPE.WAX, + [BLOCKCHAIN_NAME.DX_CHAIN]: CHAIN_TYPE.DX_CHAIN, + [BLOCKCHAIN_NAME.E_CASH]: CHAIN_TYPE.E_CASH, + [BLOCKCHAIN_NAME.NEM]: CHAIN_TYPE.NEM, + [BLOCKCHAIN_NAME.STELLAR]: CHAIN_TYPE.STELLAR, + [BLOCKCHAIN_NAME.MONERO]: CHAIN_TYPE.MONERO, + [BLOCKCHAIN_NAME.RIPPLE]: CHAIN_TYPE.RIPPLE, + [BLOCKCHAIN_NAME.TEZOS]: CHAIN_TYPE.TEZOS, + [BLOCKCHAIN_NAME.VERGE]: CHAIN_TYPE.VERGE, + [BLOCKCHAIN_NAME.SYMBOL]: CHAIN_TYPE.SYMBOL, + [BLOCKCHAIN_NAME.ZCASH]: CHAIN_TYPE.ZCASH, + [BLOCKCHAIN_NAME.HORIZEN]: CHAIN_TYPE.HORIZEN, + [BLOCKCHAIN_NAME.ZILLIQA]: CHAIN_TYPE.ZILLIQA, + [BLOCKCHAIN_NAME.KAVA_COSMOS]: CHAIN_TYPE.KAVA_COSMOS, + [BLOCKCHAIN_NAME.ZK_SYNC]: CHAIN_TYPE.EVM, + [BLOCKCHAIN_NAME.XDC]: CHAIN_TYPE.XDC, + [BLOCKCHAIN_NAME.ONTOLOGY]: CHAIN_TYPE.ONTOLOGY, + [BLOCKCHAIN_NAME.EOS]: CHAIN_TYPE.EOS, + [BLOCKCHAIN_NAME.FILECOIN]: CHAIN_TYPE.FILECOIN +}; diff --git a/SDK-mstr/src/core/blockchain/utils/changenow-receiver-address-validator.ts b/SDK-mstr/src/core/blockchain/utils/changenow-receiver-address-validator.ts new file mode 100644 index 0000000..3f45192 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/utils/changenow-receiver-address-validator.ts @@ -0,0 +1,18 @@ +import { Injector } from 'src/core/injector/injector'; +import { nonEvmChainAddressCorrectResponse } from 'src/features/common/models/non-evm-chain-address-correct-response'; + +export const isChangenowReceiverAddressCorrect = async ( + address: string, + chain: string, + regEx: RegExp +): Promise => { + try { + const response = await Injector.httpClient.get( + `https://api.changenow.io/v2/validate/address?currency=${chain}&address=${address}` + ); + + return response.result; + } catch (error) { + return regEx.test(address); + } +}; diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/models/create-web3-private.ts b/SDK-mstr/src/core/blockchain/web3-private-service/models/create-web3-private.ts new file mode 100644 index 0000000..c2e7783 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/models/create-web3-private.ts @@ -0,0 +1,21 @@ +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { Web3PrivateSupportedChainType } from 'src/core/blockchain/web3-private-service/models/web-private-supported-chain-type'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { SolanaWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/solana-web3-private/solana-web3-private'; +import { TronWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/tron-web3-private/tron-web3-private'; +import { Web3Private } from 'src/core/blockchain/web3-private-service/web3-private/web3-private'; +import { + EvmWalletProviderCore, + SolanaWalletProviderCore, + TronWalletProviderCore, + WalletProviderCore +} from 'src/core/sdk/models/wallet-provider'; + +export type CreateWeb3Private = Record< + Web3PrivateSupportedChainType, + (walletProviderCore: WalletProviderCore) => Web3Private +> & { + [CHAIN_TYPE.EVM]: (walletProviderCore: EvmWalletProviderCore) => EvmWeb3Private; + [CHAIN_TYPE.TRON]: (walletProviderCore: TronWalletProviderCore) => TronWeb3Private; + [CHAIN_TYPE.SOLANA]: (walletProviderCore: SolanaWalletProviderCore) => SolanaWeb3Private; +}; diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/models/web-private-supported-blockchain.ts b/SDK-mstr/src/core/blockchain/web3-private-service/models/web-private-supported-blockchain.ts new file mode 100644 index 0000000..3fb9c63 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/models/web-private-supported-blockchain.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME, EVM_BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const web3PrivateSupportedBlockchain = [ + ...Object.values(EVM_BLOCKCHAIN_NAME), + BLOCKCHAIN_NAME.TRON, + BLOCKCHAIN_NAME.SOLANA +] as const; + +export type Web3PrivateSupportedBlockchain = (typeof web3PrivateSupportedBlockchain)[number]; diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/models/web-private-supported-chain-type.ts b/SDK-mstr/src/core/blockchain/web3-private-service/models/web-private-supported-chain-type.ts new file mode 100644 index 0000000..1040461 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/models/web-private-supported-chain-type.ts @@ -0,0 +1,9 @@ +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; + +export const web3PrivateSupportedChainTypes = [ + CHAIN_TYPE.EVM, + CHAIN_TYPE.TRON, + CHAIN_TYPE.SOLANA +] as const; + +export type Web3PrivateSupportedChainType = (typeof web3PrivateSupportedChainTypes)[number]; diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/models/web3-private-storage.ts b/SDK-mstr/src/core/blockchain/web3-private-service/models/web3-private-storage.ts new file mode 100644 index 0000000..57aea29 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/models/web3-private-storage.ts @@ -0,0 +1,12 @@ +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { Web3PrivateSupportedChainType } from 'src/core/blockchain/web3-private-service/models/web-private-supported-chain-type'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { SolanaWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/solana-web3-private/solana-web3-private'; +import { TronWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/tron-web3-private/tron-web3-private'; +import { Web3Private } from 'src/core/blockchain/web3-private-service/web3-private/web3-private'; + +export type Web3PrivateStorage = Record & { + [CHAIN_TYPE.EVM]: EvmWeb3Private | undefined; + [CHAIN_TYPE.TRON]: TronWeb3Private | undefined; + [CHAIN_TYPE.SOLANA]: SolanaWeb3Private | undefined; +}; diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private-service.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private-service.ts new file mode 100644 index 0000000..60b156b --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private-service.ts @@ -0,0 +1,136 @@ +import { RubicSdkError } from 'src/common/errors'; +import { + BlockchainName, + EvmBlockchainName, + SolanaBlockchainName, + TronBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { CHAIN_TYPE, ChainType } from 'src/core/blockchain/models/chain-type'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { CreateWeb3Private } from 'src/core/blockchain/web3-private-service/models/create-web3-private'; +import { Web3PrivateSupportedBlockchain } from 'src/core/blockchain/web3-private-service/models/web-private-supported-blockchain'; +import { + Web3PrivateSupportedChainType, + web3PrivateSupportedChainTypes +} from 'src/core/blockchain/web3-private-service/models/web-private-supported-chain-type'; +import { Web3PrivateStorage } from 'src/core/blockchain/web3-private-service/models/web3-private-storage'; +import { EmptyWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/empty-web3-private'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { SolanaWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/solana-web3-private/solana-web3-private'; +import { TronWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/tron-web3-private/tron-web3-private'; +import { Web3Private } from 'src/core/blockchain/web3-private-service/web3-private/web3-private'; +import { + EvmWalletProviderCore, + SolanaWalletProviderCore, + TronWalletProviderCore, + WalletProvider, + WalletProviderCore +} from 'src/core/sdk/models/wallet-provider'; +import Web3 from 'web3'; + +export class Web3PrivateService { + public static isSupportedChainType( + chainType: ChainType + ): chainType is Web3PrivateSupportedChainType { + return web3PrivateSupportedChainTypes.some( + supportedChainType => supportedChainType === chainType + ); + } + + private web3PrivateStorage: Web3PrivateStorage; + + private readonly createWeb3Private: CreateWeb3Private = { + [CHAIN_TYPE.EVM]: this.createEvmWeb3Private.bind(this), + [CHAIN_TYPE.TRON]: this.createTronWeb3Private.bind(this), + [CHAIN_TYPE.SOLANA]: this.createSolanaWeb3Private.bind(this) + }; + + constructor(walletProvider: WalletProvider) { + this.web3PrivateStorage = this.createWeb3PrivateStorage(walletProvider); + } + + public getWeb3Private(chainType: 'EVM'): EvmWeb3Private; + public getWeb3Private(chainType: 'TRON'): TronWeb3Private; + public getWeb3Private(chainType: 'SOLANA'): SolanaWeb3Private; + public getWeb3Private(chainType: ChainType): never; + public getWeb3Private(chainType: ChainType) { + if (!Web3PrivateService.isSupportedChainType(chainType)) { + throw new RubicSdkError(`Chain type ${chainType} is not supported in web3 private`); + } + + const web3Private = this.web3PrivateStorage[chainType]; + if (!web3Private) { + return new EmptyWeb3Private(); + } + return web3Private; + } + + public getWeb3PrivateByBlockchain(blockchain: EvmBlockchainName): EvmWeb3Private; + public getWeb3PrivateByBlockchain(blockchain: SolanaBlockchainName): SolanaWeb3Private; + public getWeb3PrivateByBlockchain(blockchain: TronBlockchainName): TronWeb3Private; + public getWeb3PrivateByBlockchain(blockchain: Web3PrivateSupportedBlockchain): Web3Private; + public getWeb3PrivateByBlockchain(blockchain: BlockchainName): never; + public getWeb3PrivateByBlockchain(blockchain: BlockchainName) { + return this.getWeb3Private(BlockchainsInfo.getChainType(blockchain)); + } + + private createWeb3PrivateStorage(walletProvider: WalletProvider): Web3PrivateStorage { + return web3PrivateSupportedChainTypes.reduce((acc, chainType) => { + const walletProviderCore = walletProvider?.[chainType]; + if (!walletProviderCore) { + return acc; + } + const createWeb3 = this.createWeb3Private[chainType]; + + return { + ...acc, + [chainType]: createWeb3(walletProviderCore) + }; + }, {} as Web3PrivateStorage); + } + + private createEvmWeb3Private(evmWalletProviderCore: EvmWalletProviderCore): EvmWeb3Private { + let { core } = evmWalletProviderCore; + if (!(core instanceof Web3)) { + core = new Web3(core); + } + const web3 = core as Web3; + if (!web3) { + throw new RubicSdkError('Web3 is not initialized'); + } + + const address = web3.utils.toChecksumAddress(evmWalletProviderCore.address); + + return new EvmWeb3Private({ + core: web3, + address + }); + } + + private createTronWeb3Private(tronWalletProviderCore: TronWalletProviderCore): TronWeb3Private { + return new TronWeb3Private(tronWalletProviderCore); + } + + public updateWeb3PrivateAddress(chainType: Web3PrivateSupportedChainType, address: string) { + this.web3PrivateStorage[chainType]?.setAddress(address); + } + + private createSolanaWeb3Private(solanaWallet: SolanaWalletProviderCore): SolanaWeb3Private { + let { core } = solanaWallet; + return new SolanaWeb3Private(core); + } + + public updateWeb3PrivateStorage(walletProvider: WalletProvider) { + this.web3PrivateStorage = this.createWeb3PrivateStorage(walletProvider); + } + + public updateWeb3Private( + chainType: Web3PrivateSupportedChainType, + walletProviderCore: WalletProviderCore + ) { + this.web3PrivateStorage = { + ...this.web3PrivateStorage, + [chainType]: this.createWeb3Private[chainType](walletProviderCore) + }; + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/empty-web3-private.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/empty-web3-private.ts new file mode 100644 index 0000000..3d7af56 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/empty-web3-private.ts @@ -0,0 +1,16 @@ +import { RubicSdkError } from 'src/common/errors'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3Private } from 'src/core/blockchain/web3-private-service/web3-private/web3-private'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; + +export class EmptyWeb3Private extends Web3Private { + protected readonly Web3Pure = undefined as unknown as TypedWeb3Pure; + + constructor() { + super(undefined as unknown as string); + } + + public getBlockchainName(): Promise { + throw new RubicSdkError('Trying to call empty web3 private'); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private.ts new file mode 100644 index 0000000..789bdff --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private.ts @@ -0,0 +1,449 @@ +import BigNumber from 'bignumber.js'; +import { + FailedToCheckForTransactionReceiptError, + InsufficientFundsGasPriceValueError, + LowGasError, + LowSlippageDeflationaryTokenError, + LowSlippageError, + RubicSdkError, + TransactionRevertedError, + UserRejectError +} from 'src/common/errors'; +import { parseError } from 'src/common/utils/errors'; +import { getGasOptions } from 'src/common/utils/options'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { EvmTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-transaction-options'; +import { Web3Error } from 'src/core/blockchain/web3-private-service/web3-private/models/web3.error'; +import { Web3Private } from 'src/core/blockchain/web3-private-service/web3-private/web3-private'; +import { ERC20_TOKEN_ABI } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/erc-20-token-abi'; +import { UNI_V3_PERMIT_2_ABI } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/uni-v3-permit2-abi'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { WalletProviderCore } from 'src/core/sdk/models/wallet-provider'; +import { proxyHashErrors } from 'src/features/cross-chain/calculation-manager/providers/common/constants/proxy-hash-errors'; +import Web3 from 'web3'; +import { TransactionConfig } from 'web3-core'; +import { TransactionReceipt } from 'web3-eth'; +import { AbiItem } from 'web3-utils'; + +export class EvmWeb3Private extends Web3Private { + /** + * Parses web3 error by its code or message. + * @param err Web3 error to parse. + */ + public static parseError(err: Web3Error): RubicSdkError { + if (err.message.includes('Transaction has been reverted by the EVM')) { + return new TransactionRevertedError(); + } + if (err.message.includes('execution reverted: UNIV3R: min return')) { + return new LowSlippageError(); + } + if ( + err.message.includes('execution reverted: Address: low-level call with value failed') || + err.message.includes('Low native value') + ) { + return new InsufficientFundsGasPriceValueError(); + } + if (err.message.includes('Failed to check for transaction receipt')) { + return new FailedToCheckForTransactionReceiptError(); + } + if (err.code === -32603) { + return new LowGasError(); + } + if (err.code === 4001) { + return new UserRejectError(); + } + try { + const error = EvmWeb3Private.tryParseProxyError(err); + if (error) { + return error; + } + if (err.message.includes('0x6c544f')) { + return new InsufficientFundsGasPriceValueError(); + } + if ( + err.message.includes('0xf32bec2f') || + err.message.includes( + 'execution reverted: Received amount of tokens are less then expected' + ) || + err.message.includes('0x275c273c') + ) { + return new LowSlippageDeflationaryTokenError(); + } + const errorMessage = JSON.parse(err.message.slice(24)).message; + if (errorMessage) { + return new Error(errorMessage); + } + } catch {} + return parseError(err); + } + + private static tryParseProxyError(err: Error | { data: string }): RubicSdkError | undefined { + if ('data' in err) { + const error = proxyHashErrors.find(error => error.hash === err.data); + if (error) { + return new RubicSdkError(error.text); + } + } + + return undefined; + } + + protected readonly Web3Pure = EvmWeb3Pure; + + /** + * Instance of web3, initialized with ethereum wallet, e.g. Metamask, WalletConnect. + */ + public readonly web3: Web3; + + constructor(walletProviderCore: WalletProviderCore) { + super(walletProviderCore.address); + this.web3 = walletProviderCore.core; + + this.checkAddressCorrect(); + } + + public async getBlockchainName(): Promise { + const userChainId = await this.web3.eth.getChainId(); + return BlockchainsInfo.getBlockchainNameById(userChainId); + } + + /** + * Sends Eth in transaction and resolve the promise when the transaction is included in the block. + * @param toAddress Eth receiver address. + * @param [options] Additional options. + * @returns Transaction receipt. + */ + public async sendTransaction( + toAddress: string, + options: EvmTransactionOptions = {} + ): Promise { + return new Promise((resolve, reject) => { + this.web3.eth + .sendTransaction({ + from: this.address, + to: toAddress, + value: Web3Private.stringifyAmount(options.value || 0), + ...(options.gas && { gas: Web3Private.stringifyAmount(options.gas) }), + ...getGasOptions(options), + ...(options.data && { data: options.data }), + ...(options.chainId && { chainId: options.chainId }) + } as TransactionConfig) + .on('transactionHash', options.onTransactionHash || (() => {})) + .on('receipt', receipt => resolve(receipt)) + .on('error', err => { + console.error(`Send transaction error. ${err}`); + reject(EvmWeb3Private.parseError(err as Web3Error)); + }); + }); + } + + /** + * Tries to send Eth in transaction and resolve the promise when the transaction is included in the block or rejects the error. + * @param toAddress Eth receiver address. + * @param [options] Additional options. + * @returns Transaction receipt. + */ + public async trySendTransaction( + toAddress: string, + options: EvmTransactionOptions = {} + ): Promise { + try { + const gaslessParams = { + from: this.address, + to: toAddress, + value: Web3Private.stringifyAmount(options.value || 0), + ...(options.data && { data: options.data }), + ...(options?.chainId && { chainId: options.chainId }) + }; + + const gas = await this.web3.eth.estimateGas(gaslessParams as TransactionConfig); + + const gasfulParams = { + ...gaslessParams, + ...getGasOptions(options), + gas: Web3Private.stringifyAmount(gas, 1.05) + }; + + try { + await this.web3.eth.estimateGas(gasfulParams as TransactionConfig); + } catch { + throw new RubicSdkError('Low native value'); + } + + const sendParams = { + ...options, + ...gasfulParams + }; + + return this.sendTransaction(toAddress, sendParams); + } catch (err) { + console.debug('Call tokens transfer error', err); + const shouldIgnore = this.shouldIgnoreError(err); + if (shouldIgnore) { + return this.sendTransaction(toAddress, options); + } + throw EvmWeb3Private.parseError(err as Web3Error); + } + } + + /** + * Executes method of smart-contract and resolve the promise when the transaction is included in the block. + * @param contractAddress Address of smart-contract which method is to be executed. + * @param contractAbi Abi of smart-contract which method is to be executed. + * @param methodName Method name to execute. + * @param methodArguments Method arguments. + * @param [options] Additional options. + * @returns Smart-contract method returned value. + */ + public async executeContractMethod( + contractAddress: string, + contractAbi: AbiItem[], + methodName: string, + methodArguments: unknown[], + options: EvmTransactionOptions = {} + ): Promise { + const contract = new this.web3.eth.Contract(contractAbi, contractAddress); + + return new Promise((resolve, reject) => { + contract.methods[methodName](...methodArguments) + .send({ + from: this.address, + ...(options.value && { + value: Web3Private.stringifyAmount(options.value) + }), + ...(options.gas && { + gas: Web3Private.stringifyAmount(options.gas) + }), + ...getGasOptions(options), + ...(options.chainId && { chainId: options.chainId }) + }) + .on('transactionHash', options.onTransactionHash || (() => {})) + .on('receipt', resolve) + .on('error', (err: Web3Error) => { + console.error(`Method execution error. ${err}`); + reject(EvmWeb3Private.parseError(err)); + }); + }); + } + + /** + * Tries to execute method of smart-contract and resolve the promise when the transaction is included in the block or rejects the error. + * @param contractAddress Address of smart-contract which method is to be executed. + * @param contractAbi Abi of smart-contract which method is to be executed. + * @param methodName Method name to execute. + * @param methodArguments Method arguments. + * @param [options] Additional options. + * @param allowError Check error and decides to execute contact if error is allowed. + */ + public async tryExecuteContractMethod( + contractAddress: string, + contractAbi: AbiItem[], + methodName: string, + methodArguments: unknown[], + options: EvmTransactionOptions = {}, + allowError?: (err: Web3Error) => boolean + ): Promise { + const contract = new this.web3.eth.Contract(contractAbi, contractAddress); + + try { + const gaslessParams = { + from: this.address, + ...(options.value && { value: Web3Private.stringifyAmount(options.value) }), + ...(options.chainId && { chainId: options.chainId }) + }; + + const gas = await contract.methods[methodName](...methodArguments).estimateGas( + gaslessParams + ); + + const gasfulParams = { + ...gaslessParams, + ...getGasOptions(options), + gas: Web3Private.stringifyAmount(gas, 1.05) + }; + + try { + await contract.methods[methodName](...methodArguments).estimateGas(gasfulParams); + } catch { + throw new RubicSdkError('Low native value'); + } + + const sendParams = { + ...options, + ...gasfulParams + }; + + return this.executeContractMethod( + contractAddress, + contractAbi, + methodName, + methodArguments, + sendParams + ); + } catch (err) { + if ((allowError && allowError(err as Web3Error)) || this.shouldIgnoreError(err)) { + return this.executeContractMethod( + contractAddress, + contractAbi, + methodName, + methodArguments, + options + ); + } + console.error('Method execution error: ', err); + throw EvmWeb3Private.parseError(err as Web3Error); + } + } + + private shouldIgnoreError(error: Web3Error): boolean { + const ignoreCallErrors = [ + 'STF', + 'execution reverted: ERC20: transfer amount exceeds allowance', + 'Anyswaperc20: request exceed allowance', + 'gas required exceeds allowance', + 'execution reverted: SafeERC20: low-level call failed' + ]; + + const test = ignoreCallErrors.some(err => + error?.message?.toLowerCase().includes(err.toLowerCase()) + ); + console.debug(test); + return test; + } + + /** + * Executes approve method in ERC-20 token contract. + * @param tokenAddress Address of the smart-contract corresponding to the token. + * @param spenderAddress Wallet or contract address to approve. + * @param amount Token amount to approve in wei. + * @param [options] Additional options. + * @returns Approval transaction receipt. + */ + public async approveTokens( + tokenAddress: string, + spenderAddress: string, + amount: BigNumber | 'infinity' = 'infinity', + options: EvmTransactionOptions = {} + ): Promise { + const contract = new this.web3.eth.Contract(ERC20_TOKEN_ABI, tokenAddress); + const rawValue = amount === 'infinity' ? new BigNumber(2).pow(256).minus(1) : amount; + const gaslessParams = { from: this.address }; + + const gas = await contract.methods + .approve(spenderAddress, rawValue.toFixed(0)) + .estimateGas(gaslessParams); + + const gasfullParams = { + ...gaslessParams, + ...getGasOptions(options), + gas: Web3Private.stringifyAmount(gas, 1) + }; + + await contract.methods + .approve(spenderAddress, rawValue.toFixed(0)) + .estimateGas(gasfullParams); + + return new Promise((resolve, reject) => { + contract.methods + .approve(spenderAddress, rawValue.toFixed(0)) + .send(gasfullParams) + .on('transactionHash', options.onTransactionHash || (() => {})) + .on('receipt', resolve) + .on('error', (err: Web3Error) => { + console.error(`Tokens approve error. ${err}`); + reject(EvmWeb3Private.parseError(err)); + }); + }); + } + + /** + * @param tokenAddress Token address you want to approve for spending + * @param permit2Address Addres of permit2 contract + * @param spenderAddress Contract address spending your tokens + * @param amount Approved amount + * @param deadline Ms number added to current time (Date.now()) until approve expiration + */ + public async approveOnPermit2( + tokenAddress: string, + permit2Address: string, + spenderAddress: string, + amount: BigNumber | 'infinity' = 'infinity', + deadline: BigNumber = new BigNumber(1_000_000), + options: EvmTransactionOptions = {} + ): Promise { + const contract = new this.web3.eth.Contract(UNI_V3_PERMIT_2_ABI, permit2Address); + const rawValue = amount === 'infinity' ? new BigNumber(2).pow(256).minus(1) : amount; + const gaslessParams = { from: this.address }; + const expiration = new BigNumber(Date.now()).plus(deadline).toFixed(); + + const gas = await contract.methods['approve']( + tokenAddress, + spenderAddress, + rawValue.toFixed(), + expiration + ).estimateGas(); + const gasfullParams = { + ...gaslessParams, + ...getGasOptions(options), + gas: Web3Private.stringifyAmount(gas, 1) + }; + + return new Promise((resolve, reject) => { + contract.methods['approve']( + tokenAddress, + spenderAddress, + rawValue.toFixed(), + expiration + ) + .send(gasfullParams) + .on('receipt', resolve) + .on('error', (err: Web3Error) => { + console.error(`Tokens approve error. ${err}`); + reject(EvmWeb3Private.parseError(err)); + }); + }); + } + + /** + * Build encoded approve transaction config. + * @param tokenAddress Address of the smart-contract corresponding to the token. + * @param spenderAddress Wallet or contract address to approve. + * @param value Amount of tokens in approval window in spending cap field + * @param [options] Additional options. + * @returns Encoded approve transaction config. + */ + public async encodeApprove( + tokenAddress: string, + spenderAddress: string, + value: BigNumber | 'infinity', + options: EvmTransactionOptions = {} + ): Promise { + const rawValue = value === 'infinity' ? new BigNumber(2).pow(256).minus(1) : value; + const contract = new this.web3.eth.Contract(ERC20_TOKEN_ABI, tokenAddress); + const gaslessParams = { from: this.address }; + + const gas = await contract.methods + .approve(spenderAddress, rawValue.toFixed(0)) + .estimateGas(gaslessParams); + + const gasfullParams = { + ...gaslessParams, + ...getGasOptions(options), + gas: Web3Private.stringifyAmount(gas) + }; + + await contract.methods + .approve(spenderAddress, rawValue.toFixed(0)) + .estimateGas(gasfullParams); + + return EvmWeb3Pure.encodeMethodCall( + tokenAddress, + ERC20_TOKEN_ABI, + 'approve', + [spenderAddress, rawValue.toFixed(0)], + undefined, + gasfullParams + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-basic-transaction-options.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-basic-transaction-options.ts new file mode 100644 index 0000000..fd98043 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-basic-transaction-options.ts @@ -0,0 +1,18 @@ +import BigNumber from 'bignumber.js'; +import { BasicTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/models/basic-transaction-options'; +import { + EIP1559Gas, + SingleGasPrice +} from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/gas-price'; + +export interface EvmBasicTransactionOptions extends BasicTransactionOptions { + /** + * Transaction gas limit. + */ + gas?: BigNumber | string | number; + + /** + * Transaction gas price options. + */ + gasPriceOptions?: EIP1559Gas | SingleGasPrice; +} diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-transaction-options.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-transaction-options.ts new file mode 100644 index 0000000..87119fb --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-transaction-options.ts @@ -0,0 +1,19 @@ +import BigNumber from 'bignumber.js'; +import { EvmBasicTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-basic-transaction-options'; + +export interface EvmTransactionOptions extends EvmBasicTransactionOptions { + /** + * Encoded data, which will be executed in transaction. + */ + data?: string; + + /** + * Native token amount in wei. + */ + value?: BigNumber | string; + + /** + * Use in case of eip-155 + */ + chainId?: string; +} diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/models/basic-transaction-options.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/models/basic-transaction-options.ts new file mode 100644 index 0000000..7567f93 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/models/basic-transaction-options.ts @@ -0,0 +1,7 @@ +export interface BasicTransactionOptions { + /** + * Callback to be called, when user confirm swap transaction. + * @param hash Transaction hash. + */ + onTransactionHash?: (hash: string) => void; +} diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/models/web3.error.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/models/web3.error.ts new file mode 100644 index 0000000..dc9fa7c --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/models/web3.error.ts @@ -0,0 +1,6 @@ +/** + * Type of errors, thrown by web3 methods. + */ +export interface Web3Error extends Error { + code: number; +} diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/solana-web3-private/models/solana-transaction-options.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/solana-web3-private/models/solana-transaction-options.ts new file mode 100644 index 0000000..cd14d30 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/solana-web3-private/models/solana-transaction-options.ts @@ -0,0 +1,8 @@ +import { BasicTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/models/basic-transaction-options'; + +export interface SolanaTransactionOptions extends BasicTransactionOptions { + /** + * Encoded data, which will be executed in transaction. + */ + data?: string; +} diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/solana-web3-private/solana-web3-private.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/solana-web3-private/solana-web3-private.ts new file mode 100644 index 0000000..f193627 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/solana-web3-private/solana-web3-private.ts @@ -0,0 +1,38 @@ +import { VersionedTransaction } from '@solana/web3.js'; +import { BLOCKCHAIN_NAME, BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { Web3Error } from 'src/core/blockchain/web3-private-service/web3-private/models/web3.error'; +import { SolanaTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/solana-web3-private/models/solana-transaction-options'; +import { Web3Private } from 'src/core/blockchain/web3-private-service/web3-private/web3-private'; +import { SolanaWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/solana-web3-pure/solana-web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { SolanaWeb3 } from 'src/core/sdk/models/solana-web3'; + +export class SolanaWeb3Private extends Web3Private { + protected readonly Web3Pure = SolanaWeb3Pure; + + public async getBlockchainName(): Promise { + return BLOCKCHAIN_NAME.SOLANA; + } + + public async sendTransaction(options: SolanaTransactionOptions = {}): Promise { + try { + const web3Public = Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.SOLANA); + + const tx = VersionedTransaction.deserialize(Buffer.from(options.data!.slice(2), 'hex')); + const { blockhash } = await web3Public.getRecentBlockhash(); + tx.message.recentBlockhash = blockhash; + + const { signature } = await this.solanaWeb3.signAndSendTransaction(tx); + options.onTransactionHash?.(signature); + return signature; + } catch (err) { + console.error(`Send transaction error. ${err}`); + throw EvmWeb3Private.parseError(err as Web3Error); + } + } + + constructor(private readonly solanaWeb3: SolanaWeb3) { + super(solanaWeb3.publicKey?.toString() || ''); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/tron-web3-private/models/method-parameters.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/tron-web3-private/models/method-parameters.ts new file mode 100644 index 0000000..dc6f264 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/tron-web3-private/models/method-parameters.ts @@ -0,0 +1,4 @@ +export type MethodParameters = { + type: string; + value: MethodParameters; +}[]; diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/tron-web3-private/models/tron-transaction-options.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/tron-web3-private/models/tron-transaction-options.ts new file mode 100644 index 0000000..9281dec --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/tron-web3-private/models/tron-transaction-options.ts @@ -0,0 +1,7 @@ +import { BasicTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/models/basic-transaction-options'; + +export interface TronTransactionOptions extends BasicTransactionOptions { + feeLimit?: number; + + callValue?: number | string; +} diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/tron-web3-private/models/tron-transaction-receipt.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/tron-web3-private/models/tron-transaction-receipt.ts new file mode 100644 index 0000000..6889a00 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/tron-web3-private/models/tron-transaction-receipt.ts @@ -0,0 +1,3 @@ +export interface TronTransactionReceipt { + txid: string; +} diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/tron-web3-private/tron-web3-private.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/tron-web3-private/tron-web3-private.ts new file mode 100644 index 0000000..b027fbd --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/tron-web3-private/tron-web3-private.ts @@ -0,0 +1,157 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError, UserRejectError } from 'src/common/errors'; +import { TronInsufficientNativeBalance } from 'src/common/errors/blockchain/tron-insufficient-native-balance'; +import { TronTransactionExpired } from 'src/common/errors/blockchain/tron-transaction-expired'; +import { parseError } from 'src/common/utils/errors'; +import { TronWeb } from 'src/core/blockchain/constants/tron/tron-web'; +import { BLOCKCHAIN_NAME, BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { TronTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/tron-web3-private/models/tron-transaction-options'; +import { TronTransactionReceipt } from 'src/core/blockchain/web3-private-service/web3-private/tron-web3-private/models/tron-transaction-receipt'; +import { Web3Private } from 'src/core/blockchain/web3-private-service/web3-private/web3-private'; +import { TRC20_CONTRACT_ABI } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/constants/trc-20-contract-abi'; +import { TronParameters } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-parameters'; +import { TronTransactionConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-transaction-config'; +import { TronWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/tron-web3-pure'; +import { WalletProviderCore } from 'src/core/sdk/models/wallet-provider'; +import { AbiItem } from 'web3-utils'; + +export class TronWeb3Private extends Web3Private { + /** + * Parses web3 error by its code or message. + * @param err Web3 error to parse. + */ + private static parseError(err: unknown): RubicSdkError { + if ((err as string)?.includes?.('Confirmation declined by user')) { + throw new UserRejectError(); + } + + const message = (err as { message: string })?.message; + if (message?.includes('balance is not sufficient')) { + throw new TronInsufficientNativeBalance(); + } + if (message?.includes('Transaction expired')) { + throw new TronTransactionExpired(); + } + + return parseError(err); + } + + protected readonly Web3Pure = TronWeb3Pure; + + private readonly tronWeb: typeof TronWeb; + + constructor(walletProviderCore: WalletProviderCore) { + super(walletProviderCore.address); + this.tronWeb = walletProviderCore.core; + + this.checkAddressCorrect(); + } + + public async getBlockchainName(): Promise { + return BLOCKCHAIN_NAME.TRON; + } + + public async approveTokens( + tokenAddress: string, + spenderAddress: string, + amount: BigNumber | 'infinity' = 'infinity', + options: TronTransactionOptions = {} + ): Promise { + try { + const contract = await this.tronWeb.contract(TRC20_CONTRACT_ABI, tokenAddress); + + const rawValue = amount === 'infinity' ? new BigNumber(2).pow(256).minus(1) : amount; + + const transactionHash = await contract + .approve(spenderAddress, rawValue.toFixed(0)) + .send({ + ...(options.feeLimit && { + feeLimit: Web3Private.stringifyAmount(options.feeLimit) + }) + }); + if (options.onTransactionHash) { + options.onTransactionHash(transactionHash); + } + return transactionHash; + } catch (err) { + console.error('Approve execution error: ', err); + throw TronWeb3Private.parseError(err); + } + } + + public async encodeApprove( + tokenAddress: string, + spenderAddress: string, + value: BigNumber | 'infinity', + options: TronTransactionOptions = {} + ): Promise { + const rawValue = value === 'infinity' ? new BigNumber(2).pow(256).minus(1) : value; + + return TronWeb3Pure.encodeMethodCall( + tokenAddress, + TRC20_CONTRACT_ABI, + 'approve', + [spenderAddress, rawValue.toFixed(0)], + '0', + options.feeLimit + ); + } + + public async executeContractMethod( + contractAddress: string, + contractAbi: AbiItem[], + methodName: string, + methodArguments: unknown[], + options: TronTransactionOptions = {} + ): Promise { + try { + const contract = await this.tronWeb.contract(contractAbi, contractAddress); + + const transactionHash = await contract[methodName](...methodArguments).send({ + from: this.address, + ...(options.callValue && { + callValue: Web3Private.stringifyAmount(options.callValue) + }), + ...(options.feeLimit && { + feeLimit: Web3Private.stringifyAmount(options.feeLimit) + }) + }); + if (options.onTransactionHash) { + options.onTransactionHash(transactionHash); + } + return transactionHash; + } catch (err) { + console.error('Method execution error: ', err); + throw TronWeb3Private.parseError(err); + } + } + + public async triggerContract( + contractAddress: string, + methodSignature: string, + parameters: TronParameters, + options: TronTransactionOptions = {} + ): Promise { + try { + const transaction = await this.tronWeb.transactionBuilder.triggerSmartContract( + contractAddress, + methodSignature, + options, + parameters, + this.address + ); + const signedTransaction = await this.tronWeb.trx.sign(transaction.transaction); + + const receipt: TronTransactionReceipt = await this.tronWeb.trx.sendRawTransaction( + signedTransaction + ); + if (options.onTransactionHash) { + options.onTransactionHash(receipt.txid); + } + return receipt.txid; + } catch (err) { + console.error('Method execution error: ', err); + throw TronWeb3Private.parseError(err); + } + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/web3-private.ts b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/web3-private.ts new file mode 100644 index 0000000..8323453 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-private-service/web3-private/web3-private.ts @@ -0,0 +1,58 @@ +import BigNumber from 'bignumber.js'; +import { InvalidAddressError, RubicSdkError, WrongNetworkError } from 'src/common/errors'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; + +/** + * Class containing methods for executing the functions of contracts + * and sending transactions in order to change the state of the blockchain. + * To get information from the blockchain use {@link Web3Public}. + */ +export abstract class Web3Private { + /** + * Converts number, string or BigNumber value to integer string. + * @param amount Value to convert. + * @param multiplier Amount multiplier. + */ + public static stringifyAmount(amount: number | string | BigNumber, multiplier = 1): string { + const bnAmount = new BigNumber(amount); + if (!bnAmount.isInteger()) { + throw new RubicSdkError(`Value ${amount} is not integer`); + } + + return bnAmount.multipliedBy(multiplier).toFixed(0); + } + + protected abstract readonly Web3Pure: TypedWeb3Pure; + + /** + * @param address Current wallet provider address. + */ + protected constructor(public address: string) {} + + public setAddress(address: string): void { + this.address = address; + this.checkAddressCorrect(); + } + + protected checkAddressCorrect(): void { + if (!this.Web3Pure.isAddressCorrect(this.address)) { + throw new InvalidAddressError(this.address); + } + } + + /** + * Gets currently selected blockchain in wallet. + */ + public abstract getBlockchainName(): Promise; + + /** + * Checks, that selected blockchain in wallet is equal to passed blockchain. + */ + public async checkBlockchainCorrect(blockchainName: BlockchainName): Promise { + const userBlockchainName = await this.getBlockchainName(); + if (userBlockchainName !== blockchainName) { + throw new WrongNetworkError(blockchainName); + } + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/constants/rpc-errors.ts b/SDK-mstr/src/core/blockchain/web3-public-service/constants/rpc-errors.ts new file mode 100644 index 0000000..cec919c --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/constants/rpc-errors.ts @@ -0,0 +1,10 @@ +export const rpcErrors = [ + 'not authorized', + 'daily request count exceeded', + 'invalid json rpc response', + "we can't execute this request", + 'your account has been suspended', + 'account suspended, please top up to reenable', + 'your account has been suspended', + 'too many requests, we recommend you to use free api key' +]; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/models/create-web3-public-proxy.ts b/SDK-mstr/src/core/blockchain/web3-public-service/models/create-web3-public-proxy.ts new file mode 100644 index 0000000..398f9aa --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/models/create-web3-public-proxy.ts @@ -0,0 +1,19 @@ +import { Any } from 'src/common/utils/types'; +import { + EvmBlockchainName, + SolanaBlockchainName, + TronBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { Web3PublicSupportedBlockchain } from 'src/core/blockchain/web3-public-service/models/web3-public-storage'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { SolanaWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/solana-web3-public/solana-web3-public'; +import { TronWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/tron-web3-public'; +import { Web3Public } from 'src/core/blockchain/web3-public-service/web3-public/web3-public'; + +export type CreateWeb3Public = Record< + Web3PublicSupportedBlockchain, + (blockchainName?: Any) => Web3Public +> & + Record EvmWeb3Public> & + Record TronWeb3Public> & + Record SolanaWeb3Public>; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/models/web3-public-storage.ts b/SDK-mstr/src/core/blockchain/web3-public-service/models/web3-public-storage.ts new file mode 100644 index 0000000..8f568d3 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/models/web3-public-storage.ts @@ -0,0 +1,22 @@ +import { + BLOCKCHAIN_NAME, + EVM_BLOCKCHAIN_NAME, + EvmBlockchainName, + SolanaBlockchainName, + TronBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { SolanaWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/solana-web3-public/solana-web3-public'; +import { TronWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/tron-web3-public'; + +export const web3PublicSupportedBlockchains = [ + ...Object.values(EVM_BLOCKCHAIN_NAME), + BLOCKCHAIN_NAME.TRON, + BLOCKCHAIN_NAME.SOLANA +] as const; + +export type Web3PublicSupportedBlockchain = (typeof web3PublicSupportedBlockchains)[number]; + +export type Web3PublicStorage = Record & + Record & + Record; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public-service.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public-service.ts new file mode 100644 index 0000000..4857ed6 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public-service.ts @@ -0,0 +1,188 @@ +import { Connection } from '@solana/web3.js'; +import cloneDeep from 'lodash.clonedeep'; +import { HealthcheckError, RubicSdkError, TimeoutError } from 'src/common/errors'; +import pTimeout from 'src/common/utils/p-timeout'; +import { TronWeb } from 'src/core/blockchain/constants/tron/tron-web'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EVM_BLOCKCHAIN_NAME, + EvmBlockchainName, + SolanaBlockchainName, + TronBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { rpcErrors } from 'src/core/blockchain/web3-public-service/constants/rpc-errors'; +import { CreateWeb3Public } from 'src/core/blockchain/web3-public-service/models/create-web3-public-proxy'; +import { + Web3PublicStorage, + Web3PublicSupportedBlockchain, + web3PublicSupportedBlockchains +} from 'src/core/blockchain/web3-public-service/models/web3-public-storage'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { SolanaWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/solana-web3-public/solana-web3-public'; +import { TronWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/tron-web3-public'; +import { Web3Public } from 'src/core/blockchain/web3-public-service/web3-public/web3-public'; +import { RpcProviders } from 'src/core/sdk/models/rpc-provider'; +import Web3 from 'web3'; + +export class Web3PublicService { + public static isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is Web3PublicSupportedBlockchain { + return web3PublicSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + private static readonly mainRpcDefaultTimeout = 10_000; + + private readonly web3PublicStorage: Web3PublicStorage; + + private readonly createWeb3Public: CreateWeb3Public; + + constructor(public readonly rpcProvider: RpcProviders) { + this.createWeb3Public = this.setCreateWeb3Public(); + + this.web3PublicStorage = this.createWeb3PublicStorage(); + } + + public getWeb3Public(blockchainName: EvmBlockchainName): EvmWeb3Public; + public getWeb3Public(blockchainName: TronBlockchainName): TronWeb3Public; + public getWeb3Public(blockchainName: SolanaBlockchainName): SolanaWeb3Public; + public getWeb3Public(blockchainName: Web3PublicSupportedBlockchain): Web3Public; + public getWeb3Public(blockchainName: BlockchainName): never; + public getWeb3Public(blockchainName: BlockchainName) { + if (!Web3PublicService.isSupportedBlockchain(blockchainName)) { + throw new RubicSdkError( + `Blockchain ${blockchainName} is not supported in web3 public.` + ); + } + + const web3Public = this.web3PublicStorage[blockchainName]; + if (!web3Public) { + throw new RubicSdkError( + `Provider for ${blockchainName} was not initialized. Pass rpc link for this blockchain to sdk configuration object.` + ); + } + return web3Public; + } + + private setCreateWeb3Public(): CreateWeb3Public { + return { + ...(Object.values(EVM_BLOCKCHAIN_NAME) as EvmBlockchainName[]).reduce( + (acc, evmBlockchainName) => ({ + ...acc, + [evmBlockchainName]: this.createEvmWeb3PublicProxy.bind(this) + }), + {} as Record< + EvmBlockchainName, + (blockchainName?: EvmBlockchainName) => EvmWeb3Public + > + ), + [BLOCKCHAIN_NAME.TRON]: this.createTronWeb3PublicProxy.bind(this), + [BLOCKCHAIN_NAME.SOLANA]: this.createSolanaWeb3PublicProxy.bind(this) + }; + } + + private createWeb3PublicStorage(): Web3PublicStorage { + return (Object.keys(this.rpcProvider) as BlockchainName[]).reduce((acc, blockchainName) => { + if (!Web3PublicService.isSupportedBlockchain(blockchainName)) { + console.debug(`Blockchain ${blockchainName} is not supported in web3 public.`); + return acc; + } + return { + ...acc, + [blockchainName]: this.createWeb3Public[blockchainName](blockchainName) + }; + }, {} as Web3PublicStorage); + } + + private createEvmWeb3PublicProxy(blockchainName: EvmBlockchainName): EvmWeb3Public { + const rpcProvider = this.rpcProvider[blockchainName]!; + const evmWeb3Public = new EvmWeb3Public(new Web3(rpcProvider.rpcList[0]!), blockchainName); + + return this.createWeb3PublicProxy(blockchainName, evmWeb3Public); + } + + private createTronWeb3PublicProxy(): TronWeb3Public { + const rpcProvider = this.rpcProvider[BLOCKCHAIN_NAME.TRON]!; + const tronWeb3Public = new TronWeb3Public(new TronWeb(rpcProvider.rpcList[0]!)); + + return this.createWeb3PublicProxy(BLOCKCHAIN_NAME.TRON, tronWeb3Public); + } + + private createSolanaWeb3PublicProxy(): SolanaWeb3Public { + const rpcProvider = this.rpcProvider[BLOCKCHAIN_NAME.SOLANA]!; + const solanaWeb3Public = new SolanaWeb3Public( + new Connection(rpcProvider.rpcList[0]!, 'confirmed') + ); + + return this.createWeb3PublicProxy(BLOCKCHAIN_NAME.SOLANA, solanaWeb3Public); + } + + private createWeb3PublicProxy( + blockchainName: Web3PublicSupportedBlockchain, + web3Public: T + ): T { + const rpcProvider = this.rpcProvider[blockchainName]!; + + return new Proxy(web3Public, { + get(target: T, prop: keyof Web3Public) { + if (prop === 'setProvider') { + return target[prop].bind(target); + } + + if (typeof target[prop] === 'function') { + return async function method(...params: unknown[]): Promise { + const curRpc = rpcProvider.rpcList[0]; + if (!curRpc) { + throw new RubicSdkError( + `There is no working rpc left for ${blockchainName}.` + ); + } + + const methodParams = cloneDeep(params); + const callMethod = () => (target[prop] as Function).call(target, ...params); + try { + const result = await pTimeout( + callMethod(), + Web3PublicService.mainRpcDefaultTimeout + ); + if (prop === 'healthCheck' && result === false) { + throw new HealthcheckError(); + } + return result; + } catch (e) { + const rpcString = typeof curRpc === 'string' ? curRpc : curRpc.fullHost; + if ( + e instanceof TimeoutError || + e instanceof HealthcheckError || + e.message?.toLowerCase().includes(rpcString.toLowerCase()) || + rpcErrors.some(error => e.message?.toLowerCase().includes(error)) + ) { + if (curRpc === rpcProvider.rpcList[0]) { + rpcProvider.rpcList.shift(); + if (!rpcProvider.rpcList.length) { + throw new RubicSdkError( + `There is no working rpc left for ${blockchainName}.` + ); + } + const nextRpc = rpcProvider.rpcList![0]!; + web3Public.setProvider(nextRpc); + console.debug( + `Rpc provider for ${blockchainName} is changed to ${nextRpc}.` + ); + } + + return method(...methodParams); + } + throw e; + } + }; + } + + return target[prop]; + } + }); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/constants/multicall-addresses.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/constants/multicall-addresses.ts new file mode 100644 index 0000000..29c4d20 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/constants/multicall-addresses.ts @@ -0,0 +1,68 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { + Web3PublicSupportedBlockchain, + web3PublicSupportedBlockchains +} from 'src/core/blockchain/web3-public-service/models/web3-public-storage'; + +const otherWeb3PublicSupportedBlockchains = Object.values(web3PublicSupportedBlockchains).reduce( + (acc, blockchain) => ({ ...acc, [blockchain]: '' }), + {} as Record +); + +export const MULTICALL_ADDRESSES: Record = { + ...otherWeb3PublicSupportedBlockchains, + [BLOCKCHAIN_NAME.ETHEREUM]: '0x5ba1e12693dc8f9c48aad8770482f4739beed696', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: '0x15dc8b5ed578AA7a019dd0139B330cfD625cA795', + [BLOCKCHAIN_NAME.POLYGON]: '0x176730799C812d70C6608F51aEa6C7e5cdA7eA50', + [BLOCKCHAIN_NAME.AVALANCHE]: '0xdDCbf776dF3dE60163066A5ddDF2277cB445E0F3', + [BLOCKCHAIN_NAME.MOONRIVER]: '0x270f2F35bED92B7A59eA5F08F6B3fd34c8D9D9b5', + [BLOCKCHAIN_NAME.FANTOM]: '0x22D4cF72C45F8198CfbF4B568dBdB5A85e8DC0B5', + [BLOCKCHAIN_NAME.HARMONY]: '0xdDCbf776dF3dE60163066A5ddDF2277cB445E0F3', + [BLOCKCHAIN_NAME.ARBITRUM]: '0x80C7DD17B01855a6D2347444a0FCC36136a314de', + [BLOCKCHAIN_NAME.AURORA]: '0xe0e3887b158F7F9c80c835a61ED809389BC08d1b', + [BLOCKCHAIN_NAME.TELOS]: '0x53dC7535028e2fcaCa0d847AD108b9240C0801b1', + [BLOCKCHAIN_NAME.OPTIMISM]: '0xeAa6877139d436Dc6d1f75F3aF15B74662617B2C', + [BLOCKCHAIN_NAME.CRONOS]: '0x5e954f5972EC6BFc7dECd75779F10d848230345F', + [BLOCKCHAIN_NAME.OKE_X_CHAIN]: '0xF4d73326C13a4Fc5FD7A064217e12780e9Bd62c3', + [BLOCKCHAIN_NAME.GNOSIS]: '0x67dA5f2FfaDDfF067AB9d5F025F8810634d84287', + [BLOCKCHAIN_NAME.FUSE]: '0x0769fd68dFb93167989C6f7254cd0D766Fb2841F', + [BLOCKCHAIN_NAME.MOONBEAM]: '0x6477204E12A7236b9619385ea453F370aD897bb2', + [BLOCKCHAIN_NAME.CELO]: '0x9aac9048fC8139667D6a2597B902865bfdc225d3', + [BLOCKCHAIN_NAME.BOBA]: '0x96a5Eac3fa7BB87c61881Dc093884C06719Bcd1E', + [BLOCKCHAIN_NAME.ASTAR_EVM]: '0xcA11bde05977b3631167028862bE2a173976CA11', + [BLOCKCHAIN_NAME.ETHEREUM_POW]: '0x5ba1e12693dc8f9c48aad8770482f4739beed696', + [BLOCKCHAIN_NAME.KAVA]: '0x45be772faE4a9F31401dfF4738E5DC7DD439aC0b', + [BLOCKCHAIN_NAME.TRON]: 'T9ziQU4EBteJzjzMzhHELdhgWFqwzS5Vki', + [BLOCKCHAIN_NAME.BITGERT]: '0x6BB10762aE02d0544a0E948B62EaeBD991100622', + [BLOCKCHAIN_NAME.OASIS]: '0xDA294FDE76F7369ed93D7C7A3FD2d5277C2003B5', + [BLOCKCHAIN_NAME.METIS]: '0xc39aBB6c4451089dE48Cffb013c39d3110530e5C', + [BLOCKCHAIN_NAME.DFK]: '0x5b24224dC16508DAD755756639E420817DD4c99E', + [BLOCKCHAIN_NAME.KLAYTN]: '0xd11dfc2ab34abd3e1abfba80b99aefbd6255c4b8', + [BLOCKCHAIN_NAME.VELAS]: '0x0747CFe82D3Bee998f634569FE2B0005dF9d8EDE', + [BLOCKCHAIN_NAME.SYSCOIN]: '0x0c50a45401fA051F5F38e98d0E323f08eFa3bb0b', + [BLOCKCHAIN_NAME.ZK_SYNC]: '0xDBA7ab3Ed22044417380E9358aAabCF85683f3c8', + [BLOCKCHAIN_NAME.PULSECHAIN]: '0x5ba1e12693dc8f9c48aad8770482f4739beed696', + // [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: '0xca11bde05977b3631167028862be2a173976ca11', + [BLOCKCHAIN_NAME.LINEA]: '0xcA11bde05977b3631167028862bE2a173976CA11', + [BLOCKCHAIN_NAME.BASE]: '0xcA11bde05977b3631167028862bE2a173976CA11', + [BLOCKCHAIN_NAME.MANTLE]: '0xcA11bde05977b3631167028862bE2a173976CA11', + [BLOCKCHAIN_NAME.SCROLL_SEPOLIA]: '0xcA11bde05977b3631167028862bE2a173976CA11', + [BLOCKCHAIN_NAME.ARTHERA]: '0x49a390a3dFd2d01389f799965F3af5961f87d228', + [BLOCKCHAIN_NAME.SEPOLIA]: '0x805488daa81c1b9e7c5ce3f1dcea28f21448ec6a', + [BLOCKCHAIN_NAME.GOERLI]: '0x5ba1e12693dc8f9c48aad8770482f4739beed696', + [BLOCKCHAIN_NAME.MANTA_PACIFIC]: '0xf43727c9BEb4C0aA3fEE1281A902e518f8586E54', + [BLOCKCHAIN_NAME.SCROLL]: '0xcA11bde05977b3631167028862bE2a173976CA11', + [BLOCKCHAIN_NAME.BERACHAIN]: '', + [BLOCKCHAIN_NAME.BLAST_TESTNET]: '', + [BLOCKCHAIN_NAME.ZETACHAIN]: '0xcA11bde05977b3631167028862bE2a173976CA11', + [BLOCKCHAIN_NAME.HOLESKY]: '0xcA11bde05977b3631167028862bE2a173976CA11', + [BLOCKCHAIN_NAME.BLAST]: '0xcA11bde05977b3631167028862bE2a173976CA11', + [BLOCKCHAIN_NAME.HORIZEN_EON]: '0x4ea6779581bDAcd376724A52070bE89FfB74eC39', + [BLOCKCHAIN_NAME.MERLIN]: '0x45CFd6FB7999328F189aaD2739Fba4Be6C45E5bf', + [BLOCKCHAIN_NAME.MODE]: '0xcA11bde05977b3631167028862bE2a173976CA11', + [BLOCKCHAIN_NAME.ZK_FAIR]: '0xcA11bde05977b3631167028862bE2a173976CA11', + [BLOCKCHAIN_NAME.ZK_LINK]: '0x7E06D0CD8D3fDDBB875345dF389d986f810A49F6', + [BLOCKCHAIN_NAME.XLAYER]: '0xcA11bde05977b3631167028862bE2a173976CA11', + [BLOCKCHAIN_NAME.TAIKO]: '0x74F689bb9f86BD24d370BE2a2B9C74446ce08843', + [BLOCKCHAIN_NAME.SEI]: '0xcA11bde05977b3631167028862bE2a173976CA11' +}; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/erc-20-token-abi.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/erc-20-token-abi.ts new file mode 100644 index 0000000..0ead1a5 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/erc-20-token-abi.ts @@ -0,0 +1,129 @@ +import { AbiItem } from 'web3-utils'; + +export const ERC20_TOKEN_ABI = [ + { + constant: true, + inputs: [], + name: 'name', + outputs: [ + { + name: '', + type: 'string' + } + ], + payable: false, + type: 'function' + }, + { + constant: false, + inputs: [ + { + name: '_spender', + type: 'address' + }, + { + name: '_value', + type: 'uint256' + } + ], + name: 'approve', + outputs: [ + { + name: 'success', + type: 'bool' + } + ], + payable: false, + type: 'function' + }, + { + constant: true, + inputs: [], + name: 'totalSupply', + outputs: [ + { + name: '', + type: 'uint256' + } + ], + payable: false, + type: 'function' + }, + { + constant: true, + inputs: [], + name: 'decimals', + outputs: [ + { + name: '', + type: 'uint256' + } + ], + payable: false, + type: 'function' + }, + { + constant: true, + inputs: [ + { + name: '_owner', + type: 'address' + } + ], + name: 'balanceOf', + outputs: [ + { + name: 'balance', + type: 'uint256' + } + ], + payable: false, + type: 'function' + }, + { + constant: true, + inputs: [], + name: 'symbol', + outputs: [ + { + name: '', + type: 'string' + } + ], + payable: false, + type: 'function' + }, + { + constant: true, + inputs: [ + { + name: '_owner', + type: 'address' + }, + { + name: '_spender', + type: 'address' + } + ], + name: 'allowance', + outputs: [ + { + name: 'remaining', + type: 'uint256' + } + ], + payable: false, + type: 'function' + }, + { + constant: false, + inputs: [ + { name: '_to', type: 'address' }, + { name: '_value', type: 'uint256' } + ], + name: 'transfer', + outputs: [], + payable: false, + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/evm-multicall-abi.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/evm-multicall-abi.ts new file mode 100644 index 0000000..a60c651 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/evm-multicall-abi.ts @@ -0,0 +1,32 @@ +import { AbiItem } from 'web3-utils'; + +export const EVM_MULTICALL_ABI = [ + { + inputs: [ + { internalType: 'bool', name: 'requireSuccess', type: 'bool' }, + { + components: [ + { internalType: 'address', name: 'target', type: 'address' }, + { internalType: 'bytes', name: 'callData', type: 'bytes' } + ], + internalType: 'struct Multicall2.Call[]', + name: 'calls', + type: 'tuple[]' + } + ], + name: 'tryAggregate', + outputs: [ + { + components: [ + { internalType: 'bool', name: 'success', type: 'bool' }, + { internalType: 'bytes', name: 'returnData', type: 'bytes' } + ], + internalType: 'struct Multicall2.Result[]', + name: 'returnData', + type: 'tuple[]' + } + ], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/uni-v3-permit2-abi.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/uni-v3-permit2-abi.ts new file mode 100644 index 0000000..9c009ab --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/uni-v3-permit2-abi.ts @@ -0,0 +1,71 @@ +import { AbiItem } from 'web3-utils'; + +export const UNI_V3_PERMIT_2_ABI = [ + { + inputs: [ + { + internalType: 'address', + name: 'owner', + type: 'address' + }, + { + internalType: 'address', + name: 'token', + type: 'address' + }, + { + internalType: 'address', + name: 'spender', + type: 'address' + } + ], + name: 'allowance', + outputs: [ + { + internalType: 'uint160', + name: 'amount', + type: 'uint160' + }, + { + internalType: 'uint48', + name: 'expiration', + type: 'uint48' + }, + { + internalType: 'uint48', + name: 'nonce', + type: 'uint48' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'token', + type: 'address' + }, + { + internalType: 'address', + name: 'spender', + type: 'address' + }, + { + internalType: 'uint160', + name: 'amount', + type: 'uint160' + }, + { + internalType: 'uint48', + name: 'expiration', + type: 'uint48' + } + ], + name: 'approve', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public.ts new file mode 100644 index 0000000..9c41c91 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public.ts @@ -0,0 +1,686 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError, TimeoutError } from 'src/common/errors'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { Cache } from 'src/common/utils/decorators'; +import pTimeout from 'src/common/utils/p-timeout'; +import { + HEALTHCHECK, + isBlockchainHealthcheckAvailable +} from 'src/core/blockchain/constants/healthcheck'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3PrimitiveType } from 'src/core/blockchain/models/web3-primitive-type'; +import { Web3Private } from 'src/core/blockchain/web3-private-service/web3-private/web3-private'; +import { ERC20_TOKEN_ABI } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/erc-20-token-abi'; +import { EVM_MULTICALL_ABI } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/evm-multicall-abi'; +import { BatchCall } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/batch-call'; +import { EvmCall } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/evm-call'; +import { EvmMulticallResponse } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/evm-multicall-response'; +import { RpcResponse } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/rpc-response'; +import { ContractMulticallResponse } from 'src/core/blockchain/web3-public-service/web3-public/models/contract-multicall-response'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { SupportedTokenField } from 'src/core/blockchain/web3-public-service/web3-public/models/supported-token-field'; +import { + TX_STATUS, + TxStatus +} from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; +import { Web3Public } from 'src/core/blockchain/web3-public-service/web3-public/web3-public'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { DefaultHttpClient } from 'src/core/http-client/default-http-client'; +import { HttpClient } from 'src/core/http-client/models/http-client'; +import Web3 from 'web3'; +import { BlockNumber, HttpProvider, provider as Provider } from 'web3-core'; +import { BlockTransactionString, TransactionReceipt } from 'web3-eth'; +import { EventData } from 'web3-eth-contract'; +import { AbiItem } from 'web3-utils'; + +import { UNI_V3_PERMIT_2_ABI } from './constants/uni-v3-permit2-abi'; +import { GasPrice } from './models/gas-price'; +import { + Permit2AllowanceContractResponse, + Permit2AllowanceData +} from './models/permit2-contract-types'; + +/** + * Class containing methods for calling contracts in order to obtain information from the blockchain. + * To send transaction or execute contract method use {@link Web3Private}. + */ +export class EvmWeb3Public extends Web3Public { + protected readonly tokenContractAbi = ERC20_TOKEN_ABI; + + public get web3Provider(): Web3 { + return this.web3; + } + + constructor( + private readonly web3: Web3, + blockchainName: EvmBlockchainName, + private httpClient?: HttpClient + ) { + super(blockchainName); + } + + public setProvider(provider: Provider): void { + this.web3.setProvider(provider); + } + + public async healthCheck(timeoutMs: number = 4000): Promise { + if (!isBlockchainHealthcheckAvailable(this.blockchainName)) { + return true; + } + const healthcheckData = HEALTHCHECK[this.blockchainName]; + + const contract = new this.web3.eth.Contract( + healthcheckData.contractAbi, + healthcheckData.contractAddress + ); + + try { + const result = await pTimeout( + contract.methods[healthcheckData.method]().call(), + timeoutMs + ); + return result === healthcheckData.expected; + } catch (e: unknown) { + if (e instanceof TimeoutError) { + console.debug( + `${this.blockchainName} node healthcheck timeout (${timeoutMs}ms) has occurred.` + ); + } else { + console.debug(`${this.blockchainName} node healthcheck fail: ${e}`); + } + return false; + } + } + + public async getBalance(userAddress: string, tokenAddress?: string): Promise { + const isToken = tokenAddress && !EvmWeb3Pure.isNativeAddress(tokenAddress); + const balance = isToken + ? await this.getTokenBalance(userAddress, tokenAddress) + : await this.web3.eth.getBalance(userAddress); + return new BigNumber(balance); + } + + public async getTokenBalance(userAddress: string, tokenAddress: string): Promise { + const contract = new this.web3.eth.Contract(this.tokenContractAbi, tokenAddress); + + const balance = await contract.methods.balanceOf(userAddress).call(); + return new BigNumber(balance); + } + + public async getTransactionCount(walletAddress: string): Promise { + return this.web3.eth.getTransactionCount(walletAddress); + } + + public async getAllowance( + tokenAddress: string, + ownerAddress: string, + spenderAddress: string + ): Promise { + try { + const contract = new this.web3.eth.Contract(this.tokenContractAbi, tokenAddress); + + const allowance = await contract.methods.allowance(ownerAddress, spenderAddress).call(); + return new BigNumber(allowance); + } catch (err) { + console.error(err); + return new BigNumber(0); + } + } + + public async getAllowanceAndExpirationOnPermit2( + tokenAddress: string, + walletAddress: string, + spenderAddress: string, + permit2Address: string + ): Promise { + try { + const contract = new this.web3.eth.Contract(UNI_V3_PERMIT_2_ABI, permit2Address); + const res = (await contract.methods['allowance']( + walletAddress, + tokenAddress, + spenderAddress + ).call()) as Permit2AllowanceContractResponse; + + return [new BigNumber(res.amount), res.expiration]; + } catch (err) { + console.error(err); + return [new BigNumber(0), '0']; + } + } + + public async multicallContractsMethods( + contractAbi: AbiItem[], + contractsData: { + contractAddress: string; + methodsData: MethodData[]; + }[] + ): Promise[][]> { + if (this.multicallAddress) { + try { + const calls: EvmCall[][] = contractsData.map(({ contractAddress, methodsData }) => { + const contract = new this.web3.eth.Contract(contractAbi, contractAddress); + return methodsData.map(({ methodName, methodArguments }) => ({ + callData: contract.methods[methodName](...methodArguments).encodeABI(), + target: contractAddress + })); + }); + + const outputs = await this.multicall(calls.flat()); + + let outputIndex = 0; + return contractsData.map(contractData => + contractData.methodsData.map(methodData => { + const methodOutputAbi = contractAbi.find( + funcSignature => funcSignature.name === methodData.methodName + )!.outputs!; + const output = outputs[outputIndex]; + if (!output) { + throw new RubicSdkError('Output has to be defined'); + } + + outputIndex++; + + return { + success: output.success, + output: + output.success && output.returnData.length > 2 + ? (this.web3.eth.abi.decodeParameters( + methodOutputAbi, + output.returnData + )[0] as Output) + : null + }; + }) + ); + } catch (err: unknown) { + if (this.allowMultipleRequests(err)) { + return this.multicallContractsMethodsByOne(contractAbi, contractsData); + } + + throw err; + } + } + + return this.multicallContractsMethodsByOne(contractAbi, contractsData); + } + + private allowMultipleRequests(err: unknown): boolean { + return ( + (err instanceof Error && err.message.includes('unsigned transaction')) || + this.blockchainName === BLOCKCHAIN_NAME.ZETACHAIN + ); + } + + /** + * Executes multiple calls in the single contract call. + * @param calls Multicall calls data list. + * @returns Result of calls execution. + */ + private async multicall(calls: EvmCall[]): Promise { + const contract = new this.web3.eth.Contract(EVM_MULTICALL_ABI, this.multicallAddress); + return contract.methods.tryAggregate(false, calls).call(); + } + + private multicallContractsMethodsByOne( + contractAbi: AbiItem[], + contractsData: { + contractAddress: string; + methodsData: MethodData[]; + }[] + ): Promise[][]> { + return Promise.all( + contractsData.map(contractData => { + const contract = new this.web3.eth.Contract( + contractAbi, + contractData.contractAddress + ); + return Promise.all( + contractData.methodsData.map(async methodData => { + try { + const output = (await contract.methods[methodData.methodName]( + ...methodData.methodArguments + ).call()) as Output; + return { + success: true, + output + }; + } catch { + return { + success: false, + output: null + }; + } + }) + ); + }) + ); + } + + public async callContractMethod( + contractAddress: string, + contractAbi: AbiItem[], + methodName: string, + methodArguments: unknown[] = [], + options: { + from?: string; + value?: string; + gasPrice?: string; + gas?: string; + } = {} + ): Promise { + const contract = new this.web3.eth.Contract(contractAbi, contractAddress); + const callableContract = contract.methods[methodName](...methodArguments); + return callableContract.call({ + ...(options.from && { from: options.from }), + ...(options.value && { value: options.value }), + ...(options.gasPrice && { value: options.gasPrice }), + ...(options.gas && { value: options.gas }) + }); + } + + /** + * Predicts the volume of gas required to execute the contract method. + * @param contractAbi Abi of smart-contract. + * @param contractAddress Address of smart-contract. + * @param methodName Method which execution gas limit is to be calculated. + * @param methodArguments Arguments of the contract method. + * @param fromAddress The address for which the gas calculation will be called. + * @param value The value transferred for the call “transaction” in wei. + * @returns Estimated gas limit. + */ + public async getEstimatedGas( + contractAbi: AbiItem[], + contractAddress: string, + methodName: string, + methodArguments: unknown[], + fromAddress: string, + value?: string | BigNumber + ): Promise { + const contract = new this.web3.eth.Contract(contractAbi, contractAddress); + + try { + const gasLimit = await contract.methods[methodName](...methodArguments).estimateGas({ + from: fromAddress, + ...(value && { value }) + }); + return new BigNumber(gasLimit); + } catch (err) { + console.debug(err); + return null; + } + } + + public async getEstimatedGasByData( + fromAddress: string, + toAddress: string, + options: { + from?: string; + value?: string; + gasPrice?: string; + gas?: string; + data: string; + } + ): Promise { + try { + const gasLimit = await this.web3.eth.estimateGas({ + from: fromAddress, + to: toAddress, + value: Web3Private.stringifyAmount(options.value || 0), + ...(options.gas && { gas: Web3Private.stringifyAmount(options.gas) }), + ...(options.data && { data: options.data }) + }); + return new BigNumber(gasLimit); + } catch (err) { + console.debug(err); + return null; + } + } + + /** + * Get estimated gas of several contract method executions via rpc batch request. + * @param fromAddress Sender address. + * @param callsData Transactions parameters. + * @returns List of contract execution estimated gases. + * If the execution of the method in the real blockchain would not be reverted, + * then the list item would be equal to the predicted gas limit. + * Else (if you have not enough balance, allowance ...) then the list item would be equal to null. + */ + public async batchEstimatedGas( + fromAddress: string, + callsData: BatchCall[] + ): Promise<(BigNumber | null)[]> { + try { + const rpcCallsData = callsData.map(callData => ({ + rpcMethod: 'eth_estimateGas', + params: { + from: fromAddress, + to: callData.to, + data: callData.data, + ...(callData.value && { + value: `0x${parseInt(callData.value).toString(16)}` + }) + } + })); + + const result = await this.rpcBatchRequest(rpcCallsData); + return result.map(value => (value ? new BigNumber(value) : null)); + } catch (e) { + console.error(e); + return callsData.map(() => null); + } + } + + /** + * Sends batch request to rpc provider directly. + * @see {@link https://playground.open-rpc.org/?schemaUrl=https://raw.githubusercontent.com/ethereum/eth1.0-apis/assembled-spec/openrpc.json&uiSchema%5BappBar%5D%5Bui:splitView%5D=false&uiSchema%5BappBar%5D%5Bui:input%5D=false&uiSchema%5BappBar%5D%5Bui:examplesDropdown%5D=false|EthereumJSON-RPC} + * @param rpcCallsData Rpc methods and parameters list. + * @returns Rpc batch request call result sorted in order of input parameters. + */ + private async rpcBatchRequest( + rpcCallsData: { + rpcMethod: string; + params: Object; + }[] + ): Promise<(T | null)[]> { + const seed = Date.now(); + const batch = rpcCallsData.map((callData, index) => ({ + id: seed + index, + jsonrpc: '2.0', + method: callData.rpcMethod, + params: [{ ...callData.params }] + })); + + const httpClient = await this.getHttpClient(); + + const response = await httpClient.post[]>( + (this.web3.currentProvider).host, + batch + ); + + return response.sort((a, b) => a.id - b.id).map(item => (item.error ? null : item.result)); + } + + /** + * Returns httpClient if it exists or imports the axios client. + */ + private async getHttpClient(): Promise { + if (!this.httpClient) { + this.httpClient = await DefaultHttpClient.getInstance(); + } + return this.httpClient; + } + + /** + * Gets mined transaction receipt. + * @param hash Transaction hash + */ + public async getTransactionReceipt(hash: string): Promise { + return this.web3.eth.getTransactionReceipt(hash); + } + + public async getTransactionStatus(hash: string): Promise { + const txReceipt = await this.getTransactionReceipt(hash); + + if (txReceipt === null) { + return TX_STATUS.PENDING; + } + if (txReceipt.status) { + return TX_STATUS.SUCCESS; + } + return TX_STATUS.FAIL; + } + + /** + * Calculates the average price per unit of gas according to web3. + * @returns Average gas price in wei. + */ + public async getGasPrice(): Promise { + return this.web3.eth.getGasPrice(); + } + + /** + * Estimates average maxPriorityFeePerGas for EIP-1559 transactions based on last 20 blocks. + * @see {@link https://docs.alchemy.com/docs/how-to-build-a-gas-fee-estimator-using-eip-1559} + * @returns Average maxPriorityFeePerGas in wei + */ + public async getMaxPriorityFeePerGas(): Promise { + const HISTORICAL_BLOCKS = 20; + + const feeHistory = await this.web3.eth.getFeeHistory(HISTORICAL_BLOCKS, 'pending', [50]); + const blocks = feeHistory.reward.map(x => x.map(reward => Number(reward))); + + const rewardSum = blocks + .map(x => x[0]) + .reduce((acc: number, v: number | undefined) => acc + (v || 0), 0); + + return Math.round(rewardSum / blocks.length); + } + + /** + * Calculates EIP-1559 specific gas details. + * @see {@link https://github.com/ethers-io/ethers.js/blob/master/packages/abstract-provider/src.ts/index.ts#L235} + * @returns block baseFee, average maxPriorityFeePerGas, and maxFeePerGas. + */ + public async getPriorityFeeGas(): Promise { + const block = await this.getBlock('latest'); + + let lastBaseFeePerGas = null; + let maxFeePerGas = null; + let maxPriorityFeePerGas = null; + + if (block && block.baseFeePerGas) { + try { + lastBaseFeePerGas = this.getBaseFee(block); + maxPriorityFeePerGas = await this.getMaxPriorityFeePerGas(); + maxFeePerGas = block.baseFeePerGas * 2 + maxPriorityFeePerGas; + } catch (err) { + console.debug(err); + } + } + + return { + baseFee: lastBaseFeePerGas?.toFixed(), + maxFeePerGas: maxFeePerGas?.toFixed(), + maxPriorityFeePerGas: maxPriorityFeePerGas?.toFixed() + }; + } + + /** + * Calculates base fee for a given block, based on EIP-1559 base fee formula + * @see {@link https://eips.ethereum.org/EIPS/eip-1559} + * @param block Block details + * @returns Base fee for a given block + */ + private getBaseFee(block: BlockTransactionString): number | null { + if (!block.baseFeePerGas) return null; + + const BASE_FEE_MAX_CHANGE_DENOMINATOR = 8; + + const parentGasUsed = block.gasUsed; + const parentGasTarget = block.gasLimit; + const parentBaseFeePerGas = block.baseFeePerGas; + + let lastBaseFeePerGas = null; + + if (parentGasUsed === parentGasTarget) { + lastBaseFeePerGas = block.baseFeePerGas; + } else if (parentGasUsed > parentGasTarget) { + const gasUsedDelta = parentGasUsed - parentGasTarget; + const baseFeePerGasDelta = Math.max( + (parentBaseFeePerGas * gasUsedDelta) / + parentGasTarget / + BASE_FEE_MAX_CHANGE_DENOMINATOR, + 1 + ); + lastBaseFeePerGas = parentBaseFeePerGas + baseFeePerGasDelta; + } else { + const gasUsedDelta = parentGasTarget - parentGasUsed; + const baseFeePerGasDelta = + (parentBaseFeePerGas * gasUsedDelta) / + parentGasTarget / + BASE_FEE_MAX_CHANGE_DENOMINATOR; + lastBaseFeePerGas = parentBaseFeePerGas - baseFeePerGasDelta; + } + + return lastBaseFeePerGas; + } + + /** + * Gets block by block id. + * @param [blockId] Block id: hash, number ... Default is 'latest'. + * @returns Block by blockId parameter. + */ + public getBlock(blockId: BlockNumber | string = 'latest'): Promise { + return this.web3.eth.getBlock(blockId); + } + + public async getBlockNumber(): Promise { + return this.web3.eth.getBlockNumber(); + } + + public async getPastEvents( + contractAddress: string, + contractAbi: AbiItem[], + eventName: string, + options: { + blocksAmount: number; + toBlock: number | 'latest'; + } + ): Promise { + const contract = new this.web3.eth.Contract(contractAbi, contractAddress); + const blockNumber = + options.toBlock === 'latest' ? await this.getBlockNumber() : options.toBlock; + return contract.getPastEvents(eventName, { + fromBlock: blockNumber - options.blocksAmount, + toBlock: blockNumber + }); + } + + /** + * Will call smart contract method in the EVM without sending any transaction. + * @param contractAddress Contract address. + * @param contractAbi Contract ABI. + * @param methodName Method name. + * @param methodArguments Method arguments. + * @param options Sender address and value. + * @returns Transaction receipt. + */ + public async staticCallContractMethod( + contractAddress: string, + contractAbi: AbiItem[], + methodName: string, + methodArguments: unknown[], + options: { from: string; value: string } + ): Promise { + const contract = new this.web3.eth.Contract(contractAbi, contractAddress); + + return new Promise((resolve, reject) => { + contract.methods[methodName](...methodArguments).call( + { + from: options?.from, + ...(options?.value && { value: options.value }) + }, + ( + error: { code: number; message: string; data: string }, + result: TransactionReceipt | PromiseLike + ) => { + if (result) { + resolve(result); + } + + if (error) { + reject(error); + } + } + ); + }); + } + + public async getTokensBalances( + userAddress: string, + tokensAddresses: string[] + ): Promise { + const indexOfNativeCoin = tokensAddresses.findIndex(EvmWeb3Pure.isNativeAddress); + const promises = []; + + if (indexOfNativeCoin !== -1) { + tokensAddresses.splice(indexOfNativeCoin, 1); + promises[1] = this.getBalance(userAddress); + } + + promises[0] = this.multicallContractsMethods( + this.tokenContractAbi, + tokensAddresses.map(tokenAddress => ({ + contractAddress: tokenAddress, + methodsData: [ + { + methodName: 'balanceOf', + methodArguments: [userAddress] + } + ] + })) + ); + + const results = await Promise.all( + promises as [Promise[][]>, Promise] + ); + const tokensBalances = results[0].map(tokenResults => { + const { success, output } = tokenResults[0]!; + return success ? new BigNumber(output!) : new BigNumber(0); + }); + + if (indexOfNativeCoin !== -1) { + tokensBalances.splice(indexOfNativeCoin, 0, results[1]); + } + + return tokensBalances; + } + + @Cache + public async callForTokensInfo( + tokenAddresses: string[] | ReadonlyArray, + tokenFields: SupportedTokenField[] = ['decimals', 'symbol', 'name'] + ): Promise>[]> { + const nativeTokenIndex = tokenAddresses.findIndex(address => + this.Web3Pure.isNativeAddress(address) + ); + const filteredTokenAddresses = tokenAddresses.filter( + (_, index) => index !== nativeTokenIndex + ); + const contractsData = filteredTokenAddresses.map(contractAddress => ({ + contractAddress, + methodsData: tokenFields.map(methodName => ({ + methodName, + methodArguments: [] + })) + })); + + const results = contractsData.length + ? await this.multicallContractsMethods<[string]>(this.tokenContractAbi, contractsData) + : []; + const tokens = results.map((tokenFieldsResults, tokenIndex) => { + const tokenAddress = tokenAddresses[tokenIndex]!; + return tokenFieldsResults.reduce((acc, field, fieldIndex) => { + if (!field.success) { + throw new RubicSdkError(`Cannot retrieve information about ${tokenAddress}`); + } + return { + ...acc, + [tokenFields[fieldIndex]!]: field.success ? field.output : undefined + }; + }, {}); + }); + + if (nativeTokenIndex === -1) { + return tokens; + } + + const blockchainNativeToken = nativeTokensList[this.blockchainName]; + const nativeToken = { + ...blockchainNativeToken, + decimals: blockchainNativeToken.decimals.toString() + }; + tokens.splice(nativeTokenIndex, 0, nativeToken); + return tokens; + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/batch-call.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/batch-call.ts new file mode 100644 index 0000000..dc21e7a --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/batch-call.ts @@ -0,0 +1,5 @@ +export interface BatchCall { + to: string; + data: string; + value?: string; +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/evm-call.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/evm-call.ts new file mode 100644 index 0000000..beeb82b --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/evm-call.ts @@ -0,0 +1,4 @@ +export interface EvmCall { + target: string; + callData: string; +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/evm-multicall-response.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/evm-multicall-response.ts new file mode 100644 index 0000000..92f4e4c --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/evm-multicall-response.ts @@ -0,0 +1,4 @@ +export interface EvmMulticallResponse { + success: boolean; + returnData: string; +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/gas-price.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/gas-price.ts new file mode 100644 index 0000000..ba6a2a4 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/gas-price.ts @@ -0,0 +1,26 @@ +import BigNumber from 'bignumber.js'; + +export type GasPrice = EIP1559Gas & SingleGasPrice; +export interface EIP1559Gas { + /** + * EIP-1559 Block base fee. + */ + baseFee?: string; + + /** + * EIP-1559 Transaction maximum fee. + */ + maxFeePerGas?: string; + + /** + * EIP-1559 Transaction miner's tip. + */ + maxPriorityFeePerGas?: string; +} +export interface SingleGasPrice { + gasPrice?: string; +} + +export type GasPriceBN = { + [P in keyof GasPrice]: BigNumber; +}; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/permit2-contract-types.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/permit2-contract-types.ts new file mode 100644 index 0000000..7925d4a --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/permit2-contract-types.ts @@ -0,0 +1,12 @@ +import BigNumber from 'bignumber.js'; + +export interface Permit2AllowanceContractResponse { + amount: string; + expiration: string; + nonce: string; +} + +type AllowanceBN = BigNumber; +type ExpirationMS = string; + +export type Permit2AllowanceData = [AllowanceBN, ExpirationMS]; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/rpc-response.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/rpc-response.ts new file mode 100644 index 0000000..9825cd8 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/rpc-response.ts @@ -0,0 +1,5 @@ +export interface RpcResponse { + id: number; + result: T; + error: unknown; +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/models/contract-multicall-response.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/models/contract-multicall-response.ts new file mode 100644 index 0000000..06de6f6 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/models/contract-multicall-response.ts @@ -0,0 +1,6 @@ +import { Web3PrimitiveType } from 'src/core/blockchain/models/web3-primitive-type'; + +export interface ContractMulticallResponse { + success: boolean; + output: Output | null; +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/models/method-data.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/models/method-data.ts new file mode 100644 index 0000000..6c248e7 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/models/method-data.ts @@ -0,0 +1,4 @@ +export interface MethodData { + methodName: string; + methodArguments: unknown[]; +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/models/supported-token-field.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/models/supported-token-field.ts new file mode 100644 index 0000000..2251178 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/models/supported-token-field.ts @@ -0,0 +1 @@ +export type SupportedTokenField = 'decimals' | 'symbol' | 'name' /* | 'totalSupply' */; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/models/tx-status.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/models/tx-status.ts new file mode 100644 index 0000000..ec384a2 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/models/tx-status.ts @@ -0,0 +1,11 @@ +export const TX_STATUS = { + PENDING: 'PENDING', + SUCCESS: 'SUCCESS', + FAIL: 'FAIL', + FALLBACK: 'FALLBACK', + REVERT: 'REVERT', + UNKNOWN: 'UNKNOWN', + READY_TO_CLAIM: 'READY_TO_CLAIM' +} as const; + +export type TxStatus = (typeof TX_STATUS)[keyof typeof TX_STATUS]; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/solana-web3-public/solana-web3-public.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/solana-web3-public/solana-web3-public.ts new file mode 100644 index 0000000..c94d808 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/solana-web3-public/solana-web3-public.ts @@ -0,0 +1,202 @@ +import { BlockhashWithExpiryBlockHeight, Connection, PublicKey } from '@solana/web3.js'; +import { Client as TokenSdk } from '@solflare-wallet/utl-sdk'; +import BigNumber from 'bignumber.js'; +import { catchError, firstValueFrom, from, map, of, timeout } from 'rxjs'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { Cache } from 'src/common/utils/decorators'; +import { NATIVE_SOLANA_MINT_ADDRESS } from 'src/core/blockchain/constants/solana/native-solana-mint-address'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { ReturnValue } from 'src/core/blockchain/models/solana-web3-types'; +import { Web3PrimitiveType } from 'src/core/blockchain/models/web3-primitive-type'; +import { ContractMulticallResponse } from 'src/core/blockchain/web3-public-service/web3-public/models/contract-multicall-response'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { SupportedTokenField } from 'src/core/blockchain/web3-public-service/web3-public/models/supported-token-field'; +import { + TX_STATUS, + TxStatus +} from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; +import { Web3Public } from 'src/core/blockchain/web3-public-service/web3-public/web3-public'; +import { SolanaWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/solana-web3-pure/solana-web3-pure'; +import { AbiItem } from 'web3-utils'; + +/** + * Class containing methods for calling contracts in order to obtain information from the blockchain. + * To send transaction or execute contract method use {@link Web3Private}. + */ +export class SolanaWeb3Public extends Web3Public { + constructor(private readonly connection: Connection) { + super(BLOCKCHAIN_NAME.SOLANA); + } + + public getBlockNumber(): Promise { + return this.connection.getBlockHeight('finalized'); + } + + public multicallContractsMethods( + _contractAbi: AbiItem[], + _contractsData: { + contractAddress: string; + methodsData: MethodData[]; + }[] + ): Promise[][]> { + throw new Error('Method multicall is not supported'); + } + + public async getTransactionStatus(hash: string): Promise { + try { + const transaction = await this.connection.getTransaction(hash, { + maxSupportedTransactionVersion: 1 + }); + if (transaction?.meta?.err) { + return TX_STATUS.FAIL; + } + if (transaction?.blockTime) { + return TX_STATUS.SUCCESS; + } + return TX_STATUS.PENDING; + } catch { + return TX_STATUS.PENDING; + } + } + + @Cache + public async callForTokensInfo( + tokenAddresses: string[] | ReadonlyArray, + tokenFields: SupportedTokenField[] = ['decimals', 'symbol', 'name'] + ): Promise>[]> { + const nativeTokenIndex = tokenAddresses.findIndex(address => + this.Web3Pure.isNativeAddress(address) + ); + const filteredTokenAddresses = tokenAddresses.filter( + (_, index) => index !== nativeTokenIndex + ); + + const mints = filteredTokenAddresses.map(address => new PublicKey(address)); + const tokenSdk = new TokenSdk(); + const tokensMint = await tokenSdk.fetchMints(mints); + + const tokens = tokensMint.map(token => { + const entries = tokenFields.map(field => [field, token?.[field]]); + return Object.fromEntries(entries); + }); + + if (nativeTokenIndex === -1) { + return tokens; + } + + const blockchainNativeToken = nativeTokensList[this.blockchainName]; + const nativeToken = { + ...blockchainNativeToken, + decimals: blockchainNativeToken.decimals.toString() + }; + tokens.splice(nativeTokenIndex, 0, nativeToken); + return tokens; + } + + public async getBalance(userAddress: string, tokenAddress: string): Promise { + const isToken = tokenAddress && !SolanaWeb3Pure.isNativeAddress(tokenAddress); + if (isToken) { + const balance = await this.getTokensBalances(userAddress, [tokenAddress]); + return balance?.[0] || new BigNumber(0); + } + const balance = await this.connection.getBalanceAndContext( + new PublicKey(userAddress), + 'confirmed' + ); + return new BigNumber(balance.value.toString()); + } + + public async getTokenBalance(address: string, tokenAddress: string): Promise { + const balance = await this.getTokensBalances(address, [tokenAddress]); + return balance?.[0] || new BigNumber(0); + } + + public async callContractMethod( + _contractAddress: string, + _contractAbi: AbiItem[], + _methodName: string, + _methodArguments: unknown[] = [], + _options: { + from?: string; + value?: string; + gasPrice?: string; + gas?: string; + } = {} + ): Promise { + throw new Error('Method call is not supported'); + } + + public healthCheck(timeoutMs: number = 4000): Promise { + const request = this.connection.getBalanceAndContext( + new PublicKey('DVLwQbEaw5txuduQwvfbNP3sXvjawHqaoMuGMKZx15bQ'), + 'confirmed' + ); + return firstValueFrom( + from(request).pipe( + timeout(timeoutMs), + map(result => Boolean(result)), + catchError((err: unknown) => { + if ((err as Error)?.name === 'TimeoutError') { + console.debug( + `Solana node healthcheck timeout (${timeoutMs}ms) has occurred.` + ); + } else { + console.debug(`Solana node healthcheck fail: ${err}`); + } + return of(false); + }) + ) + ); + } + + /** + * Gets balance of multiple tokens. + * @param address Wallet address. + * @param tokensAddresses Tokens addresses. + */ + public async getTokensBalances( + address: string, + tokensAddresses: string[] + ): Promise { + const resp = await ( + this.connection as Connection & { + _rpcRequest: (owner: string, data: unknown[]) => ReturnValue; + } + )._rpcRequest('getTokenAccountsByOwner', [ + address, + { programId: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA' }, + { encoding: 'jsonParsed' } + ]); + + const tokenInfo = new Map( + resp.result.value.map(el => { + const { mint, tokenAmount } = el.account.data.parsed.info; + return [mint, tokenAmount.amount]; + }) + ); + + const nativeSolBalance = await this.connection.getBalanceAndContext( + new PublicKey(address), + 'confirmed' + ); + return tokensAddresses.map(tokenAddress => { + if (tokenAddress === NATIVE_SOLANA_MINT_ADDRESS) { + return new BigNumber(nativeSolBalance.value.toString()); + } + const tokenWithBalance = tokenInfo.get(tokenAddress); + return new BigNumber(tokenWithBalance || NaN); + }); + } + + public async getAllowance(): Promise { + return new BigNumber(Infinity); + } + + public setProvider(_provider: unknown): void { + return; + } + + public async getRecentBlockhash(): Promise { + return this.connection.getLatestBlockhash(); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/constants/trc-20-contract-abi.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/constants/trc-20-contract-abi.ts new file mode 100644 index 0000000..d3361bf --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/constants/trc-20-contract-abi.ts @@ -0,0 +1,79 @@ +import { AbiItem } from 'web3-utils'; + +export const TRC20_CONTRACT_ABI = [ + { + outputs: [{ type: 'string' }], + name: 'symbol', + stateMutability: 'View', + type: 'Function' + }, + { + outputs: [{ type: 'uint256' }], + inputs: [ + { name: 'owner', type: 'address' }, + { name: 'spender', type: 'address' } + ], + name: 'allowance', + stateMutability: 'View', + type: 'Function' + }, + { + outputs: [{ type: 'bool' }], + inputs: [ + { name: 'spender', type: 'address' }, + { name: 'value', type: 'uint256' } + ], + name: 'approve', + stateMutability: 'Nonpayable', + type: 'Function' + }, + { + outputs: [{ type: 'uint256' }], + inputs: [{ name: 'account', type: 'address' }], + name: 'balanceOf', + stateMutability: 'View', + type: 'Function' + }, + { + outputs: [{ type: 'uint8' }], + name: 'decimals', + stateMutability: 'View', + type: 'Function' + }, + { + outputs: [{ type: 'string' }], + name: 'name', + stateMutability: 'View', + type: 'Function' + }, + { + outputs: [{ type: 'uint256' }], + name: 'totalSupply', + stateMutability: 'View', + type: 'Function' + }, + { + outputs: [{ type: 'bool' }], + inputs: [ + { name: 'to', type: 'address' }, + { name: 'value', type: 'uint256' } + ], + name: 'transfer', + stateMutability: 'Nonpayable', + type: 'Function' + }, + { + outputs: [{ type: 'bool' }], + inputs: [ + { name: 'from', type: 'address' }, + { name: 'to', type: 'address' }, + { + name: 'value', + type: 'uint256' + } + ], + name: 'transferFrom', + stateMutability: 'Nonpayable', + type: 'Function' + } +] as unknown as AbiItem[]; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/constants/tron-multicall-abi.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/constants/tron-multicall-abi.ts new file mode 100644 index 0000000..7c25e70 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/constants/tron-multicall-abi.ts @@ -0,0 +1,25 @@ +import { AbiItem } from 'web3-utils'; + +export const TRON_MULTICALL_ABI = [ + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'target', type: 'address' }, + { internalType: 'bytes', name: 'callData', type: 'bytes' } + ], + internalType: 'struct Multicall.Call[]', + name: 'calls', + type: 'tuple[]' + } + ], + name: 'aggregateViewCalls', + outputs: [ + { internalType: 'uint256', name: 'blockNumber', type: 'uint256' }, + { internalType: 'bytes[]', name: 'returnData', type: 'bytes[]' }, + { internalType: 'bool[]', name: 'results', type: 'bool[]' } + ], + stateMutability: 'view', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-block.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-block.ts new file mode 100644 index 0000000..de8ed07 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-block.ts @@ -0,0 +1,7 @@ +export interface TronBlock { + block_header: { + raw_data: { + number: number; + }; + }; +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-call.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-call.ts new file mode 100644 index 0000000..c045fe5 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-call.ts @@ -0,0 +1,5 @@ +/** + * First argument is `target`, that is contract address to execute on. + * Second argument is `data`, that is encoded contract method. + */ +export type TronCall = [string, string]; diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-multicall-response.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-multicall-response.ts new file mode 100644 index 0000000..3e72f88 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-multicall-response.ts @@ -0,0 +1,4 @@ +export interface TronMulticallResponse { + returnData: string[]; + results: boolean[]; +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-transaction-info.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-transaction-info.ts new file mode 100644 index 0000000..cf3aa63 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-transaction-info.ts @@ -0,0 +1,10 @@ +export interface TronTransactionInfo { + blockNumber: number; + blockTimeStamp: number; + contract_address: string; + fee: number; + id: string; + receipt: { energy_fee: number; energy_usage_total: number; net_fee: number; result: string }; + resMessage: string; + result: string; +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-web-provider.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-web-provider.ts new file mode 100644 index 0000000..800919e --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-web-provider.ts @@ -0,0 +1,4 @@ +export interface TronWebProvider { + fullHost: string; + headers?: Record; +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/tron-web3-public.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/tron-web3-public.ts new file mode 100644 index 0000000..40f44bb --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/tron-web3-public/tron-web3-public.ts @@ -0,0 +1,323 @@ +import BigNumber from 'bignumber.js'; +import { BigNumber as EthersBigNumber } from 'ethers'; +import { RubicSdkError, TimeoutError } from 'src/common/errors'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { Cache } from 'src/common/utils/decorators'; +import pTimeout from 'src/common/utils/p-timeout'; +import { + HEALTHCHECK, + isBlockchainHealthcheckAvailable +} from 'src/core/blockchain/constants/healthcheck'; +import { TronWeb } from 'src/core/blockchain/constants/tron/tron-web'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { Web3PrimitiveType } from 'src/core/blockchain/models/web3-primitive-type'; +import { ContractMulticallResponse } from 'src/core/blockchain/web3-public-service/web3-public/models/contract-multicall-response'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { SupportedTokenField } from 'src/core/blockchain/web3-public-service/web3-public/models/supported-token-field'; +import { + TX_STATUS, + TxStatus +} from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; +import { TRC20_CONTRACT_ABI } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/constants/trc-20-contract-abi'; +import { TRON_MULTICALL_ABI } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/constants/tron-multicall-abi'; +import { TronBlock } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-block'; +import { TronCall } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-call'; +import { TronMulticallResponse } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-multicall-response'; +import { TronTransactionInfo } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-transaction-info'; +import { TronWebProvider } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-web-provider'; +import { Web3Public } from 'src/core/blockchain/web3-public-service/web3-public/web3-public'; +import { TronWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/tron-web3-pure'; +import { AbiItem } from 'web3-utils'; + +export class TronWeb3Public extends Web3Public { + protected readonly tokenContractAbi = TRC20_CONTRACT_ABI; + + constructor(private readonly tronWeb: typeof TronWeb) { + super(BLOCKCHAIN_NAME.TRON); + } + + public setProvider(provider: TronWebProvider): void { + this.tronWeb.setProvider(provider); + } + + public async convertTronAddressToHex(address: string): Promise { + return this.tronWeb.address.toHex(address); + } + + public async healthCheck(timeoutMs: number = 4000): Promise { + if (!isBlockchainHealthcheckAvailable(this.blockchainName)) { + return true; + } + const healthcheckData = HEALTHCHECK[this.blockchainName]; + + this.tronWeb.setAddress(healthcheckData.contractAddress); + const contract = await this.tronWeb.contract( + healthcheckData.contractAbi, + healthcheckData.contractAddress + ); + + try { + const result = await pTimeout(contract[healthcheckData.method]().call(), timeoutMs); + return result === healthcheckData.expected; + } catch (e: unknown) { + if (e instanceof TimeoutError) { + console.debug( + `${this.blockchainName} node healthcheck timeout (${timeoutMs}ms) has occurred.` + ); + } else { + console.debug(`${this.blockchainName} node healthcheck fail: ${e}`); + } + return false; + } + } + + public async getBalance(userAddress: string, tokenAddress?: string): Promise { + let balance; + if (tokenAddress && !TronWeb3Pure.isNativeAddress(tokenAddress)) { + balance = await this.getTokenBalance(userAddress, tokenAddress); + } else { + this.tronWeb.setAddress(userAddress); + balance = await this.tronWeb.trx.getBalance(userAddress); + } + return new BigNumber(balance); + } + + public async getTokenBalance(userAddress: string, tokenAddress: string): Promise { + this.tronWeb.setAddress(userAddress); + const contract = await this.tronWeb.contract(this.tokenContractAbi, tokenAddress); + const balance: EthersBigNumber = await contract.balanceOf(userAddress).call(); + return new BigNumber(balance?.toString()); + } + + public async getAllowance( + tokenAddress: string, + ownerAddress: string, + spenderAddress: string + ): Promise { + const contract = await this.tronWeb.contract(this.tokenContractAbi, tokenAddress); + + const allowance: EthersBigNumber = await contract + .allowance(ownerAddress, spenderAddress) + .call(); + return new BigNumber(allowance?.toString()); + } + + public async multicallContractsMethods( + contractAbi: AbiItem[], + contractsData: { + contractAddress: string; + methodsData: MethodData[]; + }[] + ): Promise[][]> { + const calls: TronCall[][] = contractsData.map(({ contractAddress, methodsData }) => { + return methodsData.map(({ methodName, methodArguments }) => [ + contractAddress, + TronWeb3Pure.encodeFunctionCall(contractAbi, methodName, methodArguments) + ]); + }); + + try { + const outputs = await this.multicall(calls.flat()); + + let outputIndex = 0; + return contractsData.map(contractData => + contractData.methodsData.map(methodData => { + const success = outputs.results[outputIndex]!; + const returnData = outputs.returnData[outputIndex]!; + outputIndex++; + + const methodOutputAbi = contractAbi.find( + funcSignature => funcSignature.name === methodData.methodName + )!.outputs!; + + return { + success, + output: success + ? (TronWeb3Pure.decodeMethodOutput( + methodOutputAbi, + returnData + ) as Output) + : null + }; + }) + ); + } catch { + return this.multicallContractsMethodsByOne(contractAbi, contractsData); + } + } + + /** + * Executes multiple calls in the single contract call. + * @param calls Multicall calls data list. + * @returns Result of calls execution. + */ + private async multicall(calls: TronCall[]): Promise { + this.tronWeb.setAddress(this.multicallAddress); + const contract = await this.tronWeb.contract(TRON_MULTICALL_ABI, this.multicallAddress); + return contract.aggregateViewCalls(calls).call(); + } + + public async callContractMethod( + contractAddress: string, + contractAbi: AbiItem[], + methodName: string, + methodArguments: unknown[] = [] + ): Promise { + this.tronWeb.setAddress(contractAddress); + const contract = await this.tronWeb.contract(contractAbi, contractAddress); + + const response = await contract[methodName](...methodArguments).call(); + return TronWeb3Pure.flattenParameterToPrimitive(response) as T; + } + + /** + * Gets mined transaction info. + * @param hash Transaction hash. + */ + public async getTransactionInfo(hash: string): Promise { + return this.tronWeb.trx.getTransactionInfo(hash); + } + + public async getTransactionStatus(hash: string): Promise { + const txReceipt = await this.getTransactionInfo(hash); + + if (txReceipt?.receipt) { + if (txReceipt.result === 'FAILED') { + return TX_STATUS.FAIL; + } + return TX_STATUS.SUCCESS; + } + return TX_STATUS.PENDING; + } + + public async getBlock(): Promise { + return this.tronWeb.trx.getCurrentBlock(); + } + + public async getBlockNumber(): Promise { + return (await this.getBlock()).block_header.raw_data.number; + } + + private multicallContractsMethodsByOne( + contractAbi: AbiItem[], + contractsData: { + contractAddress: string; + methodsData: MethodData[]; + }[] + ): Promise[][]> { + return Promise.all( + contractsData.map(contractData => { + return Promise.all( + contractData.methodsData.map(async methodData => { + try { + const output = (await this.callContractMethod( + contractData.contractAddress, + contractAbi, + methodData.methodName, + methodData.methodArguments + )) as Output; + return { + success: true, + output + }; + } catch { + return { + success: false, + output: null + }; + } + }) + ); + }) + ); + } + + // @TODO Refactoring + public async getTokensBalances( + userAddress: string, + tokensAddresses: string[] + ): Promise { + const indexOfNativeCoin = tokensAddresses.findIndex(TronWeb3Pure.isNativeAddress); + const promises = []; + + if (indexOfNativeCoin !== -1) { + tokensAddresses.splice(indexOfNativeCoin, 1); + promises[1] = this.getBalance(userAddress); + } + + promises[0] = this.multicallContractsMethods( + this.tokenContractAbi, + tokensAddresses.map(tokenAddress => ({ + contractAddress: tokenAddress, + methodsData: [ + { + methodName: 'balanceOf', + methodArguments: [userAddress] + } + ] + })) + ); + + const results = await Promise.all( + promises as [Promise[][]>, Promise] + ); + const tokensBalances = results[0].map(tokenResults => { + const { success, output } = tokenResults[0]!; + return success ? new BigNumber(output!) : new BigNumber(0); + }); + + if (indexOfNativeCoin !== -1) { + tokensBalances.splice(indexOfNativeCoin, 0, results[1]); + } + + return tokensBalances; + } + + @Cache + public async callForTokensInfo( + tokenAddresses: string[] | ReadonlyArray, + tokenFields: SupportedTokenField[] = ['decimals', 'symbol', 'name'] + ): Promise>[]> { + const nativeTokenIndex = tokenAddresses.findIndex(address => + this.Web3Pure.isNativeAddress(address) + ); + const filteredTokenAddresses = tokenAddresses.filter( + (_, index) => index !== nativeTokenIndex + ); + const contractsData = filteredTokenAddresses.map(contractAddress => ({ + contractAddress, + methodsData: tokenFields.map(methodName => ({ + methodName, + methodArguments: [] + })) + })); + + const results = contractsData.length + ? await this.multicallContractsMethods<[string]>(this.tokenContractAbi, contractsData) + : []; + const tokens = results.map((tokenFieldsResults, tokenIndex) => { + const tokenAddress = tokenAddresses[tokenIndex]!; + return tokenFieldsResults.reduce((acc, field, fieldIndex) => { + if (!field.success) { + throw new RubicSdkError(`Cannot retrieve information about ${tokenAddress}`); + } + return { + ...acc, + [tokenFields[fieldIndex]!]: field.success ? field.output : undefined + }; + }, {}); + }); + + if (nativeTokenIndex === -1) { + return tokens; + } + + const blockchainNativeToken = nativeTokensList[this.blockchainName]; + const nativeToken = { + ...blockchainNativeToken, + decimals: blockchainNativeToken.decimals.toString() + }; + tokens.splice(nativeTokenIndex, 0, nativeToken); + return tokens; + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/web3-public.ts b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/web3-public.ts new file mode 100644 index 0000000..443d591 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-public-service/web3-public/web3-public.ts @@ -0,0 +1,198 @@ +import BigNumber from 'bignumber.js'; +import { InsufficientFundsError, RubicSdkError } from 'src/common/errors'; +import { Token } from 'src/common/tokens'; +import { Cache } from 'src/common/utils/decorators'; +import { Web3PrimitiveType } from 'src/core/blockchain/models/web3-primitive-type'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { Web3PublicSupportedBlockchain } from 'src/core/blockchain/web3-public-service/models/web3-public-storage'; +import { MULTICALL_ADDRESSES } from 'src/core/blockchain/web3-public-service/web3-public/constants/multicall-addresses'; +import { ContractMulticallResponse } from 'src/core/blockchain/web3-public-service/web3-public/models/contract-multicall-response'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { SupportedTokenField } from 'src/core/blockchain/web3-public-service/web3-public/models/supported-token-field'; +import { TxStatus } from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { AbiItem } from 'web3-utils'; + +/** + * Class containing methods for calling contracts in order to obtain information from the blockchain. + * To send transaction or execute contract method use {@link Web3Private}. + */ +export abstract class Web3Public { + protected readonly multicallAddress = MULTICALL_ADDRESSES[this.blockchainName]; + + protected readonly Web3Pure = Web3Pure[BlockchainsInfo.getChainType(this.blockchainName)]; + + protected constructor(protected readonly blockchainName: Web3PublicSupportedBlockchain) {} + + /** + * Sets new provider to web3 instance. + * @param provider New web3 provider, e.g. rpc link. + */ + public abstract setProvider(provider: unknown): void; + + /** + * Health-check current rpc node. + * @param timeoutMs Acceptable node response timeout. + * @returns Null if healthcheck is not defined for current blockchain, else node health status. + */ + public abstract healthCheck(timeoutMs: number): Promise; + + /** + * Gets account native or token balance in wei. + * @param userAddress Wallet address, whose balance you want to find out. + * @param tokenAddress Address of the smart-contract corresponding to the token, + */ + public abstract getBalance(userAddress: string, tokenAddress?: string): Promise; + + /** + * Gets token's balance in wei. + * @param tokenAddress Address of the smart-contract corresponding to the token. + * @param userAddress Wallet address, whose balance you want to find out. + */ + public abstract getTokenBalance(userAddress: string, tokenAddress: string): Promise; + + public abstract getTokensBalances( + userAddress: string, + tokensAddresses: string[] + ): Promise; + + /** + * Checks that user has enough balance. + * @param userAddress Wallet address, which contains tokens. + * @param token Token to check balance of. + * @param requiredAmount Required user balance in Eth units. + */ + public async checkBalance( + token: Token, + requiredAmount: BigNumber, + userAddress: string + ): Promise { + const balanceWei = await this.getBalance(userAddress, token.address); + const balance = Web3Pure.fromWei(balanceWei, token.decimals); + if (balance.lt(requiredAmount)) { + throw new InsufficientFundsError(token, balance, requiredAmount); + } + } + + /** + * Gets token info by address. + * @param tokenAddress Address of token. + * @param tokenFields Token's fields to get. + */ + @Cache + public async callForTokenInfo( + tokenAddress: string, + tokenFields: SupportedTokenField[] = ['decimals', 'symbol', 'name'] + ): Promise>> { + return (await this.callForTokensInfo([tokenAddress], tokenFields))[0]!; + } + + /** + * Gets tokens info by addresses. + * @param tokenAddresses Addresses of tokens. + * @param tokenFields Token's fields to get. + */ + public abstract callForTokensInfo( + tokenAddresses: string[] | ReadonlyArray, + tokenFields?: SupportedTokenField[] + ): Promise>[]>; + + /** + * Calls allowance method in token contract. + * @param tokenAddress Address of the smart-contract corresponding to the token. + * @param spenderAddress Wallet or contract address, allowed to spend. + * @param ownerAddress Wallet address to spend from. + * @returns Token's amount, allowed to be spent. + */ + public abstract getAllowance( + tokenAddress: string, + ownerAddress: string, + spenderAddress: string + ): Promise; + + /** + * Uses multicall to make several calls of one method in one contract. + * @param contractAddress Target contract address. + * @param contractAbi Target contract abi. + * @param methodName Method name. + * @param methodCallsArguments Method parameters array, for each method call. + */ + public async multicallContractMethod( + contractAddress: string, + contractAbi: AbiItem[], + methodName: string, + methodCallsArguments: unknown[][] + ): Promise[]> { + return this.multicallContractMethods( + contractAddress, + contractAbi, + methodCallsArguments.map(methodArguments => ({ + methodName, + methodArguments + })) + ); + } + + /** + * Uses multicall to make several methods calls in one contract. + * @param contractAddress Target contract address. + * @param contractAbi Target contract abi. + * @param methodsData Methods data, containing methods' names and arguments. + */ + public async multicallContractMethods( + contractAddress: string, + contractAbi: AbiItem[], + methodsData: MethodData[] + ): Promise[]> { + const results = await this.multicallContractsMethods(contractAbi, [ + { + contractAddress, + methodsData + } + ]); + if (!results?.[0]) { + throw new RubicSdkError('Cant perform multicall or request data is empty'); + } + return results[0]; + } + + /** + * Uses multicall to make many methods calls in several contracts. + * @param contractAbi Target contract abi. + * @param contractsData Contract addresses and methods data, containing methods' names and arguments. + */ + public abstract multicallContractsMethods( + contractAbi: AbiItem[], + contractsData: { + contractAddress: string; + methodsData: MethodData[]; + }[] + ): Promise[][]>; + + /** + * Calls pure method of smart-contract and returns its output value. + * @param contractAddress Address of smart-contract which method is to be executed. + * @param contractAbi Abi of smart-contract which method is to be executed. + * @param methodName Called method name. + * @param methodArguments Method arguments. + * @param options Transaction options. + */ + public abstract callContractMethod( + contractAddress: string, + contractAbi: AbiItem[], + methodName: string, + methodArguments?: unknown[], + options?: object + ): Promise; + + /** + * Returns transaction status, based on transaction receipt. + */ + public abstract getTransactionStatus(hash: string): Promise; + + /** + * Gets last block number. + * @returns Block number. + */ + public abstract getBlockNumber(): Promise; +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/models/web3-pure-container.ts b/SDK-mstr/src/core/blockchain/web3-pure/models/web3-pure-container.ts new file mode 100644 index 0000000..921e495 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/models/web3-pure-container.ts @@ -0,0 +1,4 @@ +import { ChainType } from 'src/core/blockchain/models/chain-type'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; + +export type Web3PureContainer = Record; diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/bitcoin-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/bitcoin-web3-pure.ts new file mode 100644 index 0000000..f9a8ec3 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/bitcoin-web3-pure.ts @@ -0,0 +1,25 @@ +import { Network, validate } from 'bitcoin-address-validation'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; + +@staticImplements() +export class BitcoinWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return BitcoinWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, BitcoinWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === BitcoinWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return validate(address, Network.mainnet); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/eos-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/eos-web3-pure.ts new file mode 100644 index 0000000..829ce21 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/eos-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class EosWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return EosWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, EosWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === EosWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.EOS, + /^[1-5a-z]{12}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure.ts new file mode 100644 index 0000000..cb15261 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure.ts @@ -0,0 +1,139 @@ +import { ethers } from 'ethers'; +import { FunctionFragment, Result } from 'ethers/lib/utils'; +import { RubicSdkError } from 'src/common/errors'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { TransactionGasParams } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-params'; +import Web3 from 'web3'; +import { AbiItem, fromAscii, isAddress, randomHex, toChecksumAddress } from 'web3-utils'; + +export type DecodeResult = Result & T; + +@staticImplements() +export class EvmWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + private static web3Eth = new Web3().eth; + + public static get nativeTokenAddress(): string { + return '0x0000000000000000000000000000000000000000'; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, EvmWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === EvmWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isAddress(address); + } + + public static encodeParameters(types: string[], params: unknown[]): string { + return EvmWeb3Pure.web3Eth.abi.encodeParameters(types, params); + } + + /** + * Converts address to bytes32 format. + * @param address Address to convert. + */ + public static addressToBytes32(address: string): string { + if (address.slice(0, 2) !== '0x' || address.length !== 42) { + console.error('Wrong address format'); + throw new RubicSdkError('Wrong address format'); + } + + return `0x${address.slice(2).padStart(64, '0')}`; + } + + /** + * Converts ascii address to bytes32 format. + * @param address Address to convert. + */ + public static asciiToBytes32(address: string): string { + const bytes = fromAscii(address); + return `0x${bytes.slice(2).padStart(64, '0')}`; + } + + /** + * Generate random HEX strings from a given byte size. + * @param size byte size. + */ + public static randomHex(size: number): string { + return randomHex(size); + } + + /** + * Returns transaction config with encoded data. + */ + public static encodeMethodCall( + contractAddress: string, + contractAbi: AbiItem[], + method: string, + parameters: unknown[] = [], + value?: string, + options: TransactionGasParams = {} + ): EvmEncodeConfig { + const contract = new this.web3Eth.Contract(contractAbi); + const data = contract.methods[method](...parameters).encodeABI(); + return { + to: contractAddress, + data, + value: value || '0', + gas: options.gas, + gasPrice: options.gasPrice, + maxFeePerGas: options.maxFeePerGas, + maxPriorityFeePerGas: options.maxPriorityFeePerGas + }; + } + + /** + * Encodes a function call using its JSON interface object and given parameters. + * @param contractAbi The JSON interface object of a function. + * @param methodName Method name to encode. + * @param methodArguments Parameters to encode. + * @returns An ABI encoded function call. Means function signature + parameters. + */ + public static encodeFunctionCall( + contractAbi: AbiItem[], + methodName: string, + methodArguments: unknown[] + ): string { + const methodSignature = contractAbi.find(abiItem => abiItem.name === methodName); + if (methodSignature === undefined) { + throw Error('No such method in abi'); + } + return this.web3Eth.abi.encodeFunctionCall(methodSignature, methodArguments as string[]); + } + + /** + * Converts address to checksum format. + * @param address Address to convert. + */ + public static toChecksumAddress(address: string): string { + return toChecksumAddress(address); + } + + /** + * Decodes data by ABI. + * @param functionName Function name in ABI. + * @param functionArguments Array of function's inputs. + * @param data Data (hex string). + * @returns Decoded data. + */ + public static decodeData( + functionName: string, + functionArguments: Array<[type: string, name: string]>, + data: string + ): T { + const argumentsString = functionArguments.map(arg => arg.join(' ')).join(', '); + const abiString = `function ${functionName}(${argumentsString})`; + const abi = new ethers.utils.Interface([abiString]); + const abiFunctionKey = Object.keys(abi.functions)[0] as string; + return abi.decodeFunctionData(abi.functions[abiFunctionKey] as FunctionFragment, data) as T; + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config.ts new file mode 100644 index 0000000..552cee7 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config.ts @@ -0,0 +1,9 @@ +export interface EvmEncodeConfig { + to: string; + data: string; + value: string; + gas?: string; + gasPrice?: string; + maxFeePerGas?: string; + maxPriorityFeePerGas?: string; +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/filecoin-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/filecoin-web3-pure.ts new file mode 100644 index 0000000..a93c42e --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/filecoin-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class FilecoinWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return FilecoinWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, FilecoinWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === FilecoinWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.FILECOIN, + /[a-z0-9]{41}$|[a-z0-9]{86}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/icp-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/icp-web3-pure.ts new file mode 100644 index 0000000..81d1dcf --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/icp-web3-pure.ts @@ -0,0 +1,41 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; + +const CRC32 = require('crc-32'); + +@staticImplements() +export class IcpWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return IcpWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, IcpWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === IcpWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + if (address?.length !== 64) { + return false; + } + const buffer = this.base16Decode(address); + const hash = CRC32.buf(buffer.slice(4)); + return hash === ((buffer[0]! << 24) | (buffer[1]! << 16) | (buffer[2]! << 8) | buffer[3]!); + } + + private static base16Decode(str: string): number[] { + const buffer = []; + for (let i = 0; i < str.length / 2; ++i) { + const hi = parseInt(str.substring(i * 2, i * 2 + 1), 16); + const lo = parseInt(str.substring(i * 2 + 1, i * 2 + 2), 16); + buffer.push((hi << 4) | lo); + } + return buffer; + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/algorand-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/algorand-web3-pure.ts new file mode 100644 index 0000000..8d79f8f --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/algorand-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class AlgorandWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return AlgorandWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, AlgorandWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === AlgorandWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.ALGORAND, + /^[A-Z0-9]{58}/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/aptos-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/aptos-web3-pure.ts new file mode 100644 index 0000000..45432c5 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/aptos-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class AptosWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return AptosWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, AptosWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === AptosWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.APTOS, + /^(0x)[0-9A-Za-z]{64}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/astar-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/astar-web3-pure.ts new file mode 100644 index 0000000..6a89725 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/astar-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class AstarWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return AstarWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, AstarWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === AstarWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.ASTAR, + /^[0-9a-z-A-Z]{44,50}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/cardano-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/cardano-web3-pure.ts new file mode 100644 index 0000000..5fd31da --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/cardano-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class CardanoWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return CardanoWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, CardanoWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === CardanoWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.CARDANO, + /^([1-9A-HJ-NP-Za-km-z]{59,104})|([0-9A-Za-z]{58,104})$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/casper-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/casper-web3-pure.ts new file mode 100644 index 0000000..1556b5a --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/casper-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class CasperWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return CasperWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, CasperWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === CasperWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.CASPER, + /^[A-Za-z0-9]{66,68}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/cosmos-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/cosmos-web3-pure.ts new file mode 100644 index 0000000..68f7d3b --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/cosmos-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class CosmosWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return CosmosWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, CosmosWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === CosmosWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.COSMOS, + /^(cosmos1)[0-9a-z]{38}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/dash-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/dash-web3-pure.ts new file mode 100644 index 0000000..45b2c98 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/dash-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class DashWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return DashWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, DashWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === DashWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.DASH, + /^[X|7][0-9A-Za-z]{33}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/dogecoin-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/dogecoin-web3-pure.ts new file mode 100644 index 0000000..53f5ae1 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/dogecoin-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class DogecoinWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return DogecoinWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, DogecoinWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === DogecoinWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.DOGECOIN, + /^[X|7][0-9A-Za-z]{33}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/flow-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/flow-web3-pure.ts new file mode 100644 index 0000000..f528ee0 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/flow-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class FlowWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return FlowWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, FlowWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === FlowWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.FLOW, + /^(0x)[0-9A-Fa-f]{16}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/hedear-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/hedear-web3-pure.ts new file mode 100644 index 0000000..26ab345 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/hedear-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class HederaWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return HederaWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, HederaWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === HederaWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.HEDERA, + /^0\.0\.\d{1,7}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/iota-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/iota-web3-pure.ts new file mode 100644 index 0000000..980c9e5 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/iota-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class IotaWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return IotaWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, IotaWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === IotaWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.IOTA, + /^(iota)[0-9a-z]{60}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/kadena-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/kadena-web3-pure.ts new file mode 100644 index 0000000..1a7d358 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/kadena-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class KadenaWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return KadenaWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, KadenaWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === KadenaWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.KADENA, + /^k:[0-9a-zA-Z]{64}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/kava-cosmos-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/kava-cosmos-web3-pure.ts new file mode 100644 index 0000000..bbb947d --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/kava-cosmos-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class KavaCosmosWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return KavaCosmosWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, KavaCosmosWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === KavaCosmosWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.KAVA_COSMOS, + /^(kava1)[0-9a-z]{38}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/kusama-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/kusama-web3-pure.ts new file mode 100644 index 0000000..f9c4e38 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/kusama-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class KusamaWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return KusamaWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, KusamaWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === KusamaWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.KUSAMA, + /^[C-Z][a-km-zA-HJ-NP-Z1-9]{46}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/litecoin-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/litecoin-web3-pure.ts new file mode 100644 index 0000000..00527f6 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/litecoin-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class LitecoinWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return LitecoinWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, LitecoinWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === LitecoinWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.LITECOIN, + /^(L|M|3)[A-Za-z0-9]{33}$|^(ltc1)[0-9A-Za-z]{39}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/mina-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/mina-web3-pure.ts new file mode 100644 index 0000000..a981142 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/mina-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class MinaWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return MinaWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, MinaWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === MinaWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.MINA_PROTOCOL, + /^(B62)[A-Za-z0-9]{52}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/monero-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/monero-web3-pure.ts new file mode 100644 index 0000000..0e6ff22 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/monero-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class MoneroWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return MoneroWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, MoneroWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === MoneroWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.MONERO, + /^[48][a-zA-Z|\d]{94}([a-zA-Z|\d]{11})?$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/near-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/near-web3-pure.ts new file mode 100644 index 0000000..1a84c28 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/near-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class NearWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return 'near'; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, NearWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === NearWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.NEAR, + /(^[a-z0-9_-]{2,64}\.near$)|(^[0-9a-f]{64}$)/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/neo-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/neo-web3-pure.ts new file mode 100644 index 0000000..9ea3f00 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/neo-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class NeoWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return NeoWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, NeoWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === NeoWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.NEO, + /^(A)[A-Za-z0-9]{33}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/osmosis-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/osmosis-web3-pure.ts new file mode 100644 index 0000000..a4badf2 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/osmosis-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class OsmosisWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return OsmosisWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, OsmosisWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === OsmosisWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.OSMOSIS, + /^(osmo1)[0-9a-z]{38}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/polkadot-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/polkadot-web3-pure.ts new file mode 100644 index 0000000..435f7df --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/polkadot-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class PolkadotWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return PolkadotWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, PolkadotWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === PolkadotWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.POLKADOT, + /^1[0-9a-z-A-Z]{44,49}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/ripple-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/ripple-web3-pure.ts new file mode 100644 index 0000000..9a59255 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/ripple-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class RippleWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return RippleWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, RippleWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === RippleWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.RIPPLE, + /^r[1-9A-HJ-NP-Za-km-z]{25,34}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/secret-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/secret-web3-pure.ts new file mode 100644 index 0000000..102f0ff --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/secret-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class SecretWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return SecretWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, SecretWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === SecretWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.SECRET, + /^(secret1)[0-9a-z]{38}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/sia-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/sia-web3-pure.ts new file mode 100644 index 0000000..d9cdc15 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/sia-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class SiaWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return SiaWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, SiaWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === SiaWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.SIA, + /^[A-Za-z0-9]{76}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/stellar-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/stellar-web3-pure.ts new file mode 100644 index 0000000..4d9f8d4 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/stellar-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class StellarWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return StellarWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, StellarWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === StellarWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.STELLAR, + /^G[A-D]{1}[A-Z2-7]{54}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/tezos-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/tezos-web3-pure.ts new file mode 100644 index 0000000..2ac9758 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/tezos-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class TezosWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return TezosWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, TezosWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === TezosWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.TEZOS, + /^tz1[a-zA-Z0-9]{33}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/ton-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/ton-web3-pure.ts new file mode 100644 index 0000000..f7557e6 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/ton-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class TonWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return TonWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, TonWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === TonWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.TON, + /^(EQ|UQ)[0-9a-zA-Z-_!]{46}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/waves-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/waves-web3-pure.ts new file mode 100644 index 0000000..d471e11 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/waves-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class WavesWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return WavesWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, WavesWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === WavesWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.WAVES, + /^(3P)[0-9A-Za-z]{33}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/wax-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/wax-web3-pure.ts new file mode 100644 index 0000000..49beac7 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/wax-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class WaxWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return WaxWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, WaxWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === WaxWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.WAX, + /^[a-z1-5.]{1,12}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/zilliqa-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/zilliqa-web3-pure.ts new file mode 100644 index 0000000..9808f0b --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/zilliqa-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class ZilliqaWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return ZilliqaWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, ZilliqaWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === ZilliqaWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.ZILLIQA, + /^zil1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/ontology-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/ontology-web3-pure.ts new file mode 100644 index 0000000..f0e592c --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/ontology-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class OntologyWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return OntologyWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, OntologyWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === OntologyWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.ONTOLOGY, + /^(A)[A-Za-z0-9]{33}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/solana-web3-pure/solana-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/solana-web3-pure/solana-web3-pure.ts new file mode 100644 index 0000000..62c19a7 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/solana-web3-pure/solana-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class SolanaWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return 'So11111111111111111111111111111111111111111'; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, SolanaWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === SolanaWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.SOLANA, + /^[1-9A-HJ-NP-Za-km-z]{32,44}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-parameters.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-parameters.ts new file mode 100644 index 0000000..5c07a78 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-parameters.ts @@ -0,0 +1 @@ +export type TronParameters = { type: string; value: string | TronParameters }[]; diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-transaction-config.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-transaction-config.ts new file mode 100644 index 0000000..7b75e38 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-transaction-config.ts @@ -0,0 +1,6 @@ +export interface TronTransactionConfig { + to: string; + data: string; + callValue?: string; + feeLimit?: number; +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/tron-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/tron-web3-pure.ts new file mode 100644 index 0000000..0adffbc --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/tron-web3-pure.ts @@ -0,0 +1,178 @@ +import { BigNumber as EthersBigNumber } from 'ethers'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { InfiniteArray } from 'src/common/utils/types'; +import { TronWeb } from 'src/core/blockchain/constants/tron/tron-web'; +import { + TronWeb3PrimitiveType, + Web3PrimitiveType +} from 'src/core/blockchain/models/web3-primitive-type'; +import { TronParameters } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-parameters'; +import { TronTransactionConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-transaction-config'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { AbiInput, AbiItem, AbiOutput } from 'web3-utils'; + +@staticImplements() +export class TronWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return '0x0000000000000000000000000000000000000000'; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, TronWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === TronWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return TronWeb.isAddress(address); + } + + public static addressToHex(address: string): string { + return TronWeb.address.toHex(address).replace(/^41/, '0x'); + } + + /** + * Returns transaction config with encoded data. + */ + public static encodeMethodCall( + contractAddress: string, + contractAbi: AbiItem[], + methodName: string, + methodArguments: unknown[] = [], + callValue?: string, + feeLimit?: number + ): TronTransactionConfig { + const data = this.encodeFunctionCall(contractAbi, methodName, methodArguments); + return { + to: contractAddress, + data, + callValue, + feeLimit + }; + } + + /** + * Encodes a function call using its JSON interface object and given parameters. + * @param contractAbi The JSON interface object of a function. + * @param methodName Method name to encode. + * @param methodArguments Parameters to encode. + * @returns An ABI encoded function call. Means function signature + parameters. + */ + public static encodeFunctionCall( + contractAbi: AbiItem[], + methodName: string, + methodArguments: unknown[] + ): string { + const methodSignature = contractAbi.find(abiItem => abiItem.name === methodName); + if (methodSignature === undefined) { + throw Error('No such method in abi'); + } + + const encodedMethodSignature = TronWeb.sha3( + `${methodSignature.name!}(${this.flattenTypesToString(methodSignature.inputs!).join( + ',' + )})` + ).slice(0, 10); + const encodedParameters = TronWeb.utils.abi.encodeParams( + this.flattenTypesToArray(methodSignature.inputs!), + methodArguments + ); + + return encodedMethodSignature + encodedParameters.slice(2); + } + + public static encodeMethodSignature( + methodSignature: string, + parameters: TronParameters + ): string { + const encodedMethodSignature = TronWeb.sha3(methodSignature).slice(0, 10); + const flattenedParameters = this.flattenParameters(parameters); + const encodedParameters = TronWeb.utils.abi.encodeParams( + flattenedParameters[0], + flattenedParameters[1] + ); + + return encodedMethodSignature + encodedParameters.slice(2); + } + + /** + * Decodes method result using its JSON interface object and given parameters. + * @param outputAbi The JSON interface object of an output of function. + * @param response Bytes code returned after method call. + * @returns Parsed method output. + */ + public static decodeMethodOutput(outputAbi: AbiOutput[], response: string): Web3PrimitiveType { + const decodedParam: TronWeb3PrimitiveType = TronWeb.utils.abi.decodeParams( + [], + this.flattenTypesToArray(outputAbi), + response + )[0]; + return this.flattenParameterToPrimitive(decodedParam); + } + + private static flattenTypesToString(abiInputs: (AbiInput | AbiOutput)[]): string[] { + return ( + abiInputs?.map(abiInput => { + if (abiInput.type === 'tuple') { + const flattenedComponents = this.flattenTypesToString(abiInput.components!); + return `(${flattenedComponents.join(',')})`; + } + return abiInput.type; + }) || [] + ); + } + + private static flattenTypesToArray(abiInputs: (AbiInput | AbiOutput)[]): InfiniteArray { + return ( + abiInputs?.map(abiInput => { + if (abiInput.type === 'tuple') { + return this.flattenTypesToArray(abiInput.components!); + } + return abiInput.type; + }) || [] + ); + } + + private static flattenParameters( + parameters: TronParameters + ): [InfiniteArray, InfiniteArray] { + const types: InfiniteArray = []; + const values: InfiniteArray = []; + parameters.forEach(parameter => { + if (parameter.type === 'tuple') { + const flattenedParameters = this.flattenParameters( + parameter.value as TronParameters + ); + types.push(flattenedParameters[0]); + values.push(flattenedParameters[1]); + } else { + types.push(parameter.type); + values.push(parameter.value as string); + } + }); + return [types, values]; + } + + public static flattenParameterToPrimitive(parameter: TronWeb3PrimitiveType): Web3PrimitiveType { + if (typeof parameter === 'number' || parameter instanceof EthersBigNumber) { + return parameter.toString(); + } + if (typeof parameter === 'string' || typeof parameter === 'boolean') { + return parameter; + } + return Object.keys(parameter).reduce((acc, paramKey) => { + const parameterField = (parameter as { [key: string]: TronWeb3PrimitiveType })[ + paramKey + ] as TronWeb3PrimitiveType; + return { + ...acc, + [paramKey]: this.flattenParameterToPrimitive(parameterField) + }; + }, {}); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure.ts new file mode 100644 index 0000000..adcc653 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure.ts @@ -0,0 +1,22 @@ +export interface TypedWeb3Pure { + /** + * Gets address of native coin. + */ + get nativeTokenAddress(): string; + + /** + * Checks if address is native address. + * @param address Address to check. + */ + isNativeAddress(address: string): boolean; + + EMPTY_ADDRESS: string; + + isEmptyAddress(address: string): boolean; + + /** + * Checks if a given address is a valid Ethereum address. + * @param address The address to check validity of. + */ + isAddressCorrect(address: string): Promise; +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/xdc-web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/xdc-web3-pure.ts new file mode 100644 index 0000000..9749893 --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/typed-web3-pure/xdc-web3-pure.ts @@ -0,0 +1,30 @@ +import { compareAddresses } from 'src/common/utils/blockchain'; +import { staticImplements } from 'src/common/utils/decorators'; +import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator'; +import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure'; +import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; + +@staticImplements() +export class XdcWeb3Pure { + public static readonly EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + + public static get nativeTokenAddress(): string { + return XdcWeb3Pure.EMPTY_ADDRESS; + } + + public static isNativeAddress(address: string): boolean { + return compareAddresses(address, XdcWeb3Pure.nativeTokenAddress); + } + + public static isEmptyAddress(address?: string): boolean { + return address === XdcWeb3Pure.EMPTY_ADDRESS; + } + + public static async isAddressCorrect(address: string): Promise { + return isChangenowReceiverAddressCorrect( + address, + changenowApiBlockchain.XDC, + /^xdc[a-fA-F\d]{40}$/ + ); + } +} diff --git a/SDK-mstr/src/core/blockchain/web3-pure/web3-pure.ts b/SDK-mstr/src/core/blockchain/web3-pure/web3-pure.ts new file mode 100644 index 0000000..23f209c --- /dev/null +++ b/SDK-mstr/src/core/blockchain/web3-pure/web3-pure.ts @@ -0,0 +1,242 @@ +import BigNumber from 'bignumber.js'; +import { staticImplements } from 'src/common/utils/decorators'; +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { Web3PureContainer } from 'src/core/blockchain/web3-pure/models/web3-pure-container'; +import { BitcoinWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/bitcoin-web3-pure'; +import { EosWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/eos-web3-pure'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { FilecoinWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/filecoin-web3-pure'; +import { IcpWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/icp-web3-pure'; +import { AlgorandWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/algorand-web3-pure'; +import { AptosWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/aptos-web3-pure'; +import { AstarWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/astar-web3-pure'; +import { CardanoWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/cardano-web3-pure'; +import { CasperWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/casper-web3-pure'; +import { CosmosWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/cosmos-web3-pure'; +import { DashWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/dash-web3-pure'; +import { DogecoinWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/dogecoin-web3-pure'; +import { FlowWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/flow-web3-pure'; +import { HederaWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/hedear-web3-pure'; +import { IotaWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/iota-web3-pure'; +import { KadenaWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/kadena-web3-pure'; +import { KavaCosmosWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/kava-cosmos-web3-pure'; +import { KusamaWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/kusama-web3-pure'; +import { LitecoinWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/litecoin-web3-pure'; +import { MinaWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/mina-web3-pure'; +import { MoneroWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/monero-web3-pure'; +import { NearWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/near-web3-pure'; +import { NeoWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/neo-web3-pure'; +import { OsmosisWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/osmosis-web3-pure'; +import { PolkadotWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/polkadot-web3-pure'; +import { RippleWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/ripple-web3-pure'; +import { SecretWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/secret-web3-pure'; +import { SiaWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/sia-web3-pure'; +import { StellarWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/stellar-web3-pure'; +import { TezosWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/tezos-web3-pure'; +import { TonWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/ton-web3-pure'; +import { WavesWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/waves-web3-pure'; +import { WaxWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/wax-web3-pure'; +import { ZilliqaWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/non-evm-web3-pure/zilliqa-web3-pure'; +import { OntologyWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/ontology-web3-pure'; +import { SolanaWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/solana-web3-pure/solana-web3-pure'; +import { TronWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/tron-web3-pure'; +import { XdcWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/xdc-web3-pure'; + +/** + * Contains common methods, connected with web3, e.g. wei conversion, encoding data, etc. + */ +@staticImplements() +export class Web3Pure { + public static [CHAIN_TYPE.EVM] = EvmWeb3Pure; + + public static [CHAIN_TYPE.TRON] = TronWeb3Pure; + + public static [CHAIN_TYPE.BITCOIN] = BitcoinWeb3Pure; + + public static [CHAIN_TYPE.ICP] = IcpWeb3Pure; + + public static [CHAIN_TYPE.RIPPLE] = RippleWeb3Pure; + + public static [CHAIN_TYPE.CARDANO] = CardanoWeb3Pure; + + public static [CHAIN_TYPE.SOLANA] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.DOGECOIN] = DogecoinWeb3Pure; + + public static [CHAIN_TYPE.POLKADOT] = PolkadotWeb3Pure; + + public static [CHAIN_TYPE.LITECOIN] = LitecoinWeb3Pure; + + public static [CHAIN_TYPE.MONERO] = MoneroWeb3Pure; + + public static [CHAIN_TYPE.NEAR] = NearWeb3Pure; + + public static [CHAIN_TYPE.ALGORAND] = AlgorandWeb3Pure; + + public static [CHAIN_TYPE.TEZOS] = TezosWeb3Pure; + + public static [CHAIN_TYPE.DASH] = DashWeb3Pure; + + public static [CHAIN_TYPE.ZILLIQA] = ZilliqaWeb3Pure; + + public static [CHAIN_TYPE.KAVA_COSMOS] = KavaCosmosWeb3Pure; + + public static [CHAIN_TYPE.APTOS] = AptosWeb3Pure; + + public static [CHAIN_TYPE.ASTAR] = AstarWeb3Pure; + + public static [CHAIN_TYPE.COSMOS] = CosmosWeb3Pure; + + public static [CHAIN_TYPE.FLOW] = FlowWeb3Pure; + + public static [CHAIN_TYPE.HEDERA] = HederaWeb3Pure; + + public static [CHAIN_TYPE.BITCOIN_DIAMOND] = BitcoinWeb3Pure; + + public static [CHAIN_TYPE.BITCOIN_GOLD] = BitcoinWeb3Pure; + + public static [CHAIN_TYPE.BSV] = BitcoinWeb3Pure; + + public static [CHAIN_TYPE.IOTA] = IotaWeb3Pure; + + public static [CHAIN_TYPE.KADENA] = KadenaWeb3Pure; + + public static [CHAIN_TYPE.KUSAMA] = KusamaWeb3Pure; + + public static [CHAIN_TYPE.MINA_PROTOCOL] = MinaWeb3Pure; + + public static [CHAIN_TYPE.NEO] = NeoWeb3Pure; + + public static [CHAIN_TYPE.OSMOSIS] = OsmosisWeb3Pure; + + public static [CHAIN_TYPE.SIA] = SiaWeb3Pure; + + public static [CHAIN_TYPE.SECRET] = SecretWeb3Pure; + + public static [CHAIN_TYPE.TON] = TonWeb3Pure; + + public static [CHAIN_TYPE.WAVES] = WavesWeb3Pure; + + public static [CHAIN_TYPE.WAX] = WaxWeb3Pure; + + public static [CHAIN_TYPE.STELLAR] = StellarWeb3Pure; + + public static [CHAIN_TYPE.XDC] = XdcWeb3Pure; + + public static [CHAIN_TYPE.ONTOLOGY] = OntologyWeb3Pure; + + public static [CHAIN_TYPE.EOS] = EosWeb3Pure; + + public static [CHAIN_TYPE.FILECOIN] = FilecoinWeb3Pure; + + public static [CHAIN_TYPE.CASPER] = CasperWeb3Pure; + + // @TODO create web3pure file before release + + public static [CHAIN_TYPE.AION] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.ARDOR] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.ARK] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.BAND_PROTOCOL] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.DECRED] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.DIGI_BYTE] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.DIVI] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.MULTIVERS_X] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.FIO_PROTOCOL] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.FIRO] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.HELIUM] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.ICON] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.IOST] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.KOMODO] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.LISK] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.TERRA] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.TERRA_CLASSIC] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.NANO] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.PIVX] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.POLYX] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.QTUM] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.THOR_CHAIN] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.RAVENCOIN] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.STEEM] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.STRATIS] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.STACKS] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.SOLAR] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.VE_CHAIN] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.DX_CHAIN] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.E_CASH] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.NEM] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.VERGE] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.SYMBOL] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.ZCASH] = SolanaWeb3Pure; + + public static [CHAIN_TYPE.HORIZEN] = SolanaWeb3Pure; + + /** + * Increases the gas limit value by the specified percentage and rounds to the nearest integer. + * @param gasLimit Gas limit value to increase. + * @param multiplier The multiplier by which the gas limit will be increased. + */ + public static calculateGasMargin( + gasLimit: BigNumber | string | number | null | undefined, + multiplier: number + ): BigNumber { + return new BigNumber(gasLimit || '0').multipliedBy(multiplier).dp(0); + } + + /** + * Converts amount from Ether to Wei units. + * @param amount Amount to convert. + * @param decimals Token decimals. + * @param roundingMode BigNumberRoundingMode. + */ + public static toWei( + amount: BigNumber | string | number, + decimals = 18, + roundingMode?: BigNumber.RoundingMode + ): string { + return new BigNumber(amount || 0) + .times(new BigNumber(10).pow(decimals)) + .toFixed(0, roundingMode); + } + + /** + * Converts amount from Wei to Ether units. + * @param amountInWei Amount to convert. + * @param decimals Token decimals. + */ + public static fromWei(amountInWei: BigNumber | string | number, decimals = 18): BigNumber { + return new BigNumber(amountInWei).div(new BigNumber(10).pow(decimals)); + } +} diff --git a/SDK-mstr/src/core/coingecko-api/coingecko-api.ts b/SDK-mstr/src/core/coingecko-api/coingecko-api.ts new file mode 100644 index 0000000..3405773 --- /dev/null +++ b/SDK-mstr/src/core/coingecko-api/coingecko-api.ts @@ -0,0 +1,57 @@ +import BigNumber from 'bignumber.js'; +import { Cache } from 'src/common/utils/decorators'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { HttpClient } from 'src/core/http-client/models/http-client'; + +import { TO_BACKEND_BLOCKCHAINS } from '../blockchain/models/backend-blockchains'; + +interface TokenPriceFromBackend { + network: string; + address: string; + usd_price: number | null; +} + +/** + * Works with coingecko api to get tokens prices in usd. + */ +export class CoingeckoApi { + constructor(private readonly httpClient: HttpClient) {} + + @Cache({ + maxAge: 1000 * 60 * 5 + }) + private async getTokenPriceFromBackend( + blockchain: BlockchainName, + tokenAddress: string + ): Promise { + try { + const backendBlockchain = TO_BACKEND_BLOCKCHAINS[blockchain]; + const result = await this.httpClient.get( + `https://tokens.rubic.exchange/api/v1/tokens/price/${backendBlockchain}/${tokenAddress}` + ); + + return result; + } catch (error) { + console.debug(error); + + return { + network: blockchain, + address: tokenAddress, + usd_price: null + }; + } + } + + /** + * Gets price of common token or native coin in usd from coingecko. + * @param token Token to get price for. + */ + public async getTokenPrice(token: { + address: string; + blockchain: BlockchainName; + }): Promise { + const response = await this.getTokenPriceFromBackend(token.blockchain, token.address); + + return new BigNumber(response?.usd_price || NaN); + } +} diff --git a/SDK-mstr/src/core/gas-price-api/constants/eip1559-compatible-blockchains.ts b/SDK-mstr/src/core/gas-price-api/constants/eip1559-compatible-blockchains.ts new file mode 100644 index 0000000..a72c385 --- /dev/null +++ b/SDK-mstr/src/core/gas-price-api/constants/eip1559-compatible-blockchains.ts @@ -0,0 +1,15 @@ +import { BLOCKCHAIN_NAME, BlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +export const EIP1559CompatibleBlockchains: Record = { + ...Object.values(BLOCKCHAIN_NAME).reduce( + (acc, blockchain) => ({ ...acc, [blockchain]: false }), + {} as Record + ), + [BLOCKCHAIN_NAME.ETHEREUM]: true, + [BLOCKCHAIN_NAME.POLYGON]: true, + [BLOCKCHAIN_NAME.AVALANCHE]: true, + [BLOCKCHAIN_NAME.FANTOM]: true, + [BLOCKCHAIN_NAME.ARBITRUM]: true, + [BLOCKCHAIN_NAME.ZK_SYNC]: true + // [BLOCKCHAIN_NAME.OPTIMISM]: true, - Will be compatible on 3rd of June +}; diff --git a/SDK-mstr/src/core/gas-price-api/gas-price-api.ts b/SDK-mstr/src/core/gas-price-api/gas-price-api.ts new file mode 100644 index 0000000..f32df2e --- /dev/null +++ b/SDK-mstr/src/core/gas-price-api/gas-price-api.ts @@ -0,0 +1,83 @@ +import { Cache } from 'src/common/utils/decorators'; +import pTimeout from 'src/common/utils/p-timeout'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { HttpClient } from 'src/core/http-client/models/http-client'; +import { Injector } from 'src/core/injector/injector'; + +import { GasPrice } from '../blockchain/web3-public-service/web3-public/evm-web3-public/models/gas-price'; +import { EIP1559CompatibleBlockchains } from './constants/eip1559-compatible-blockchains'; +import { OneInchGasResponse } from './models/1inch-gas-response'; + +/** + * Uses different api or web3 to retrieve current gas price. + */ +export class GasPriceApi { + /** + * Gas price request interval in seconds. + */ + private static readonly requestInterval = 15_000; + + constructor(private readonly httpClient: HttpClient) {} + + /** + * Gets gas price in Wei for selected blockchain. + * @param blockchain Blockchain to get gas price from. + */ + public getGasPrice(blockchain: EvmBlockchainName): Promise { + if (blockchain === BLOCKCHAIN_NAME.ETHEREUM) { + return this.fetchEthGas(); + } + return this.fetchGas(blockchain); + } + + /** + * Gets gas price in Eth units for selected blockchain. + * @param blockchain Blockchain to get gas price from. + */ + public async getGasPriceInEthUnits(blockchain: EvmBlockchainName): Promise { + return await this.getGasPrice(blockchain); + } + + /** + * Gets Ethereum gas price from different APIs, sorted by priority. + * @returns Average gas price in Wei. + */ + @Cache({ + maxAge: GasPriceApi.requestInterval + }) + private async fetchEthGas(): Promise { + const requestTimeout = 3000; + + try { + const response: OneInchGasResponse = await pTimeout( + this.httpClient.get('https://gas-price-api.1inch.io/v1.2/1'), + requestTimeout + ); + return { + baseFee: response.baseFee, + maxFeePerGas: response.high.maxFeePerGas, + maxPriorityFeePerGas: response.high.maxPriorityFee + }; + } catch (_err) {} + + const web3Public = Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.ETHEREUM); + return web3Public.getPriorityFeeGas(); + } + + /** + * Gets gas price from web3. + * @returns Average gas price in Wei. + */ + @Cache({ + maxAge: GasPriceApi.requestInterval + }) + private async fetchGas(blockchain: EvmBlockchainName): Promise { + const web3Public = Injector.web3PublicService.getWeb3Public(blockchain); + if (EIP1559CompatibleBlockchains[blockchain]) { + return await web3Public.getPriorityFeeGas(); + } + return { + gasPrice: await web3Public.getGasPrice() + }; + } +} diff --git a/SDK-mstr/src/core/gas-price-api/models/1inch-gas-response.ts b/SDK-mstr/src/core/gas-price-api/models/1inch-gas-response.ts new file mode 100644 index 0000000..dfe918e --- /dev/null +++ b/SDK-mstr/src/core/gas-price-api/models/1inch-gas-response.ts @@ -0,0 +1,4 @@ +export interface OneInchGasResponse { + baseFee: string; + high: { maxPriorityFee: string; maxFeePerGas: string }; +} diff --git a/SDK-mstr/src/core/http-client/default-http-client.ts b/SDK-mstr/src/core/http-client/default-http-client.ts new file mode 100644 index 0000000..af954ff --- /dev/null +++ b/SDK-mstr/src/core/http-client/default-http-client.ts @@ -0,0 +1,29 @@ +import { AxiosInstance } from 'axios'; +import { Cache } from 'src/common/utils/decorators'; + +export class DefaultHttpClient { + @Cache + public static async getInstance(): Promise { + const axios = (await import('axios')) as unknown as AxiosInstance; + DefaultHttpClient.addBodyInterceptor(axios); + DefaultHttpClient.addCacheDisablingInterceptor(axios); + return axios; + } + + private static addBodyInterceptor(axios: AxiosInstance): void { + axios.interceptors.response.use( + response => response.data, + error => Promise.reject(error) + ); + } + + private static addCacheDisablingInterceptor(axios: AxiosInstance): void { + axios.interceptors.request.use( + request => { + request.params = { ...request.params, timestamp: Date.now() }; + return request; + }, + error => Promise.reject(error) + ); + } +} diff --git a/SDK-mstr/src/core/http-client/models/http-client.ts b/SDK-mstr/src/core/http-client/models/http-client.ts new file mode 100644 index 0000000..0044a0d --- /dev/null +++ b/SDK-mstr/src/core/http-client/models/http-client.ts @@ -0,0 +1,29 @@ +/** + * Http client, used to get and send http requests. + */ +export interface HttpClient { + post( + url: string, + body: Object, + options?: { + headers?: { + [header: string]: string; + }; + } + ): Promise; + get( + url: string, + options?: { + headers?: { + [header: string]: string; + }; + params?: { + [param: string]: + | string + | number + | boolean + | ReadonlyArray; + }; + } + ): Promise; +} diff --git a/SDK-mstr/src/core/injector/injector.ts b/SDK-mstr/src/core/injector/injector.ts new file mode 100644 index 0000000..e291c14 --- /dev/null +++ b/SDK-mstr/src/core/injector/injector.ts @@ -0,0 +1,52 @@ +import { Web3PrivateService } from 'src/core/blockchain/web3-private-service/web3-private-service'; +import { Web3PublicService } from 'src/core/blockchain/web3-public-service/web3-public-service'; +import { CoingeckoApi } from 'src/core/coingecko-api/coingecko-api'; +import { GasPriceApi } from 'src/core/gas-price-api/gas-price-api'; +import { HttpClient } from 'src/core/http-client/models/http-client'; + +export class Injector { + private static injector: Injector; + + public static get web3PublicService(): Web3PublicService { + return Injector.injector.web3PublicService; + } + + public static get web3PrivateService(): Web3PrivateService { + return Injector.injector.web3PrivateService; + } + + public static get httpClient(): HttpClient { + return Injector.injector.httpClient; + } + + public static get coingeckoApi(): CoingeckoApi { + return Injector.injector.coingeckoApi; + } + + public static get gasPriceApi(): GasPriceApi { + return Injector.injector.gasPriceApi; + } + + public static createInjector( + web3PublicService: Web3PublicService, + web3PrivateService: Web3PrivateService, + httpClient: HttpClient + ): void { + // eslint-disable-next-line no-new + new Injector(web3PublicService, web3PrivateService, httpClient); + } + + private readonly coingeckoApi: CoingeckoApi; + + private readonly gasPriceApi: GasPriceApi; + + private constructor( + private readonly web3PublicService: Web3PublicService, + private readonly web3PrivateService: Web3PrivateService, + private readonly httpClient: HttpClient + ) { + this.coingeckoApi = new CoingeckoApi(httpClient); + this.gasPriceApi = new GasPriceApi(httpClient); + Injector.injector = this; + } +} diff --git a/SDK-mstr/src/core/sdk/models/configuration.ts b/SDK-mstr/src/core/sdk/models/configuration.ts new file mode 100644 index 0000000..28c3876 --- /dev/null +++ b/SDK-mstr/src/core/sdk/models/configuration.ts @@ -0,0 +1,34 @@ +import { HttpClient } from 'src/core/http-client/models/http-client'; +import { ProviderAddress } from 'src/core/sdk/models/provider-address'; +import { RpcProviders } from 'src/core/sdk/models/rpc-provider'; +import { WalletProvider } from 'src/core/sdk/models/wallet-provider'; + +/** + * Main sdk configuration. + */ +export interface Configuration { + /** + * Rpc data to connect to blockchains you will use. + * You have to pass rpcProvider for each blockchain you will use with sdk. + */ + readonly rpcProviders: RpcProviders; + + /** + * Required to use `swap`, `approve` and other methods which sends transactions. + * But you can calculate and encode trades without `walletProvider`. + * Pass it when user connects wallet. Please note that `address` and `chainId` must + * match account address and selected chain id in a user's wallet. + */ + readonly walletProvider?: WalletProvider; + + /** + * You can pass your own http client (e.g. HttpClient in Angular) if you have it, + * to not duplicate http clients and decrease bundle size. + */ + readonly httpClient?: HttpClient; + + /** + * Integrator wallet address. + */ + readonly providerAddress?: Partial; +} diff --git a/SDK-mstr/src/core/sdk/models/provider-address.ts b/SDK-mstr/src/core/sdk/models/provider-address.ts new file mode 100644 index 0000000..899f5a7 --- /dev/null +++ b/SDK-mstr/src/core/sdk/models/provider-address.ts @@ -0,0 +1,11 @@ +import { WalletProvider } from 'src/core/sdk/models/wallet-provider'; + +export type ProviderAddress = Partial< + Record< + keyof WalletProvider, + { + crossChain?: string; + onChain?: string; + } + > +>; diff --git a/SDK-mstr/src/core/sdk/models/rpc-provider.ts b/SDK-mstr/src/core/sdk/models/rpc-provider.ts new file mode 100644 index 0000000..e79782e --- /dev/null +++ b/SDK-mstr/src/core/sdk/models/rpc-provider.ts @@ -0,0 +1,22 @@ +import { + EvmBlockchainName, + SolanaBlockchainName, + TronBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { TronWebProvider } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/models/tron-web-provider'; + +/** + * Stores information about rpc in certain blockchain. + */ +export interface RpcProvider { + /** + * Contains rpc links in order of prioritization. + */ + readonly rpcList: T[]; +} + +export type RpcProviders = Partial< + Record> & + Record> & + Record> +>; diff --git a/SDK-mstr/src/core/sdk/models/solana-web3.ts b/SDK-mstr/src/core/sdk/models/solana-web3.ts new file mode 100644 index 0000000..6618f02 --- /dev/null +++ b/SDK-mstr/src/core/sdk/models/solana-web3.ts @@ -0,0 +1,27 @@ +import { + Connection, + Transaction, + TransactionSignature, + VersionedTransaction +} from '@solana/web3.js'; + +export interface SolanaWeb3 { + publicKey?: { toBytes(): Uint8Array }; + isConnected: boolean; + signTransaction(transaction: Transaction): Promise; + signAllTransactions(transactions: Transaction[]): Promise; + signMessage(message: Uint8Array, encoding: string): Promise<{ signature: Uint8Array }>; + sendTransaction( + transaction: Transaction, + connection?: Connection, + options?: {} + ): Promise; + request(args: { method: string; params: { message: string } }): Promise; + connect(): Promise; + disconnect(): Promise; + signAndSendTransaction( + transaction: Transaction | VersionedTransaction + ): Promise<{ signature: string }>; + on: (event: string, callback: () => void) => unknown; + off: (event: string, callback: () => void) => unknown; +} diff --git a/SDK-mstr/src/core/sdk/models/wallet-provider.ts b/SDK-mstr/src/core/sdk/models/wallet-provider.ts new file mode 100644 index 0000000..88e6bf4 --- /dev/null +++ b/SDK-mstr/src/core/sdk/models/wallet-provider.ts @@ -0,0 +1,33 @@ +import { Any } from 'src/common/utils/types'; +import { TronWeb } from 'src/core/blockchain/constants/tron/tron-web'; +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { SolanaWeb3 } from 'src/core/sdk/models/solana-web3'; +import Web3 from 'web3'; +import { provider } from 'web3-core'; + +export interface WalletProviderCore { + /** + * Core provider. + */ + readonly core: T; + + /** + * User wallet address. + */ + readonly address: string; +} + +export type EvmWalletProviderCore = WalletProviderCore; +export type TronWalletProviderCore = WalletProviderCore; +export type SolanaWalletProviderCore = WalletProviderCore; + +/** + * Stores wallet core and information about current user, used to make `send` transactions. + */ +interface IWalletProvider { + readonly [CHAIN_TYPE.EVM]?: EvmWalletProviderCore; + readonly [CHAIN_TYPE.TRON]?: TronWalletProviderCore; + readonly [CHAIN_TYPE.SOLANA]?: SolanaWalletProviderCore; +} + +export type WalletProvider = Partial; diff --git a/SDK-mstr/src/core/sdk/sdk.ts b/SDK-mstr/src/core/sdk/sdk.ts new file mode 100644 index 0000000..319b9a5 --- /dev/null +++ b/SDK-mstr/src/core/sdk/sdk.ts @@ -0,0 +1,155 @@ +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { Web3PrivateService } from 'src/core/blockchain/web3-private-service/web3-private-service'; +import { Web3PublicService } from 'src/core/blockchain/web3-public-service/web3-public-service'; +import { TronWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/tron-web3-pure'; +import { CoingeckoApi } from 'src/core/coingecko-api/coingecko-api'; +import { GasPriceApi } from 'src/core/gas-price-api/gas-price-api'; +import { DefaultHttpClient } from 'src/core/http-client/default-http-client'; +import { HttpClient } from 'src/core/http-client/models/http-client'; +import { Injector } from 'src/core/injector/injector'; +import { Configuration } from 'src/core/sdk/models/configuration'; +import { ProviderAddress } from 'src/core/sdk/models/provider-address'; +import { WalletProvider, WalletProviderCore } from 'src/core/sdk/models/wallet-provider'; +import { CrossChainManager } from 'src/features/cross-chain/calculation-manager/cross-chain-manager'; +import { CrossChainStatusManager } from 'src/features/cross-chain/status-manager/cross-chain-status-manager'; +import { CrossChainSymbiosisManager } from 'src/features/cross-chain/symbiosis-manager/cross-chain-symbiosis-manager'; +import { DeflationTokenManager } from 'src/features/deflation-token-manager/deflation-token-manager'; +import { OnChainManager } from 'src/features/on-chain/calculation-manager/on-chain-manager'; +import { OnChainStatusManager } from 'src/features/on-chain/status-manager/on-chain-status-manager'; + +/** + * Base class to work with sdk. + */ +export class SDK { + /** + * On-chain manager object. Use it to calculate and create on-chain trades. + */ + public readonly onChainManager: OnChainManager; + + /** + * Cross-chain trades manager object. Use it to calculate and create cross-chain trades. + */ + public readonly crossChainManager: CrossChainManager; + + /** + * On-chain status manager object. Use it for special providers, which requires more than one trade. + */ + public readonly onChainStatusManager: OnChainStatusManager; + + /** + * Cross-chain status manager object. Use it to get trade statuses on source and target network. + */ + public readonly crossChainStatusManager: CrossChainStatusManager; + + /** + * Cross-chain symbiosis manager object. Use it to get pending trades in symbiosis and revert them. + */ + public readonly crossChainSymbiosisManager: CrossChainSymbiosisManager; + + /** + * Deflation token manager object. Use it to check specific token for fees or deflation. + */ + public readonly deflationTokenManager: DeflationTokenManager; + + /** + * Can be used to get `Web3Public` instance by blockchain name to get public information from blockchain. + */ + public get web3PublicService(): Web3PublicService { + return Injector.web3PublicService; + } + + /** + * Can be used to send transactions and execute smart contracts methods. + */ + public get web3PrivateService(): Web3PrivateService { + return Injector.web3PrivateService; + } + + /** + * Use it to get gas price information. + */ + public get gasPriceApi(): GasPriceApi { + return Injector.gasPriceApi; + } + + /** + * Use it to get coingecko price information. + */ + public get coingeckoApi(): CoingeckoApi { + return Injector.coingeckoApi; + } + + /** + * Creates new sdk instance. Changes dependencies of all sdk entities according + * to new configuration (even for entities created with other previous sdk instances). + */ + public static async createSDK(configuration: Configuration): Promise { + const [web3PublicService, web3PrivateService, httpClient] = await Promise.all([ + SDK.createWeb3PublicService(configuration), + SDK.createWeb3PrivateService(configuration), + SDK.createHttpClient(configuration) + ]); + Injector.createInjector(web3PublicService, web3PrivateService, httpClient); + + const { providerAddress } = configuration; + return new SDK({ + [CHAIN_TYPE.EVM]: providerAddress?.[CHAIN_TYPE.EVM] || undefined, + [CHAIN_TYPE.TRON]: providerAddress?.[CHAIN_TYPE.TRON] || { + crossChain: TronWeb3Pure.EMPTY_ADDRESS, + onChain: TronWeb3Pure.EMPTY_ADDRESS + } + }); + } + + private static createWeb3PrivateService(configuration: Configuration): Web3PrivateService { + return new Web3PrivateService(configuration.walletProvider || {}); + } + + private static createWeb3PublicService(configuration: Configuration): Web3PublicService { + return new Web3PublicService(configuration.rpcProviders); + } + + private static async createHttpClient(configuration: Configuration): Promise { + if (!configuration.httpClient) { + return DefaultHttpClient.getInstance(); + } + return configuration.httpClient; + } + + private constructor(providerAddress: ProviderAddress) { + this.onChainManager = new OnChainManager(providerAddress); + this.crossChainManager = new CrossChainManager(providerAddress); + this.deflationTokenManager = new DeflationTokenManager(); + this.onChainStatusManager = new OnChainStatusManager(); + this.crossChainStatusManager = new CrossChainStatusManager(); + this.crossChainSymbiosisManager = new CrossChainSymbiosisManager(); + } + + /** + * Updates sdk configuration and sdk entities dependencies. + */ + public async updateConfiguration(configuration: Configuration): Promise { + const [web3PublicService, web3PrivateService, httpClient] = await Promise.all([ + SDK.createWeb3PublicService(configuration), + SDK.createWeb3PrivateService(configuration), + SDK.createHttpClient(configuration) + ]); + + Injector.createInjector(web3PublicService, web3PrivateService, httpClient); + } + + public updateWalletProvider(walletProvider: WalletProvider): void { + Injector.web3PrivateService.updateWeb3PrivateStorage(walletProvider); + } + + public updateWalletProviderCore( + chainType: keyof WalletProvider, + walletProviderCore: WalletProviderCore + ): void { + Injector.web3PrivateService.updateWeb3Private(chainType, walletProviderCore); + } + + public updateWalletAddress(chainType: keyof WalletProvider, address: string): void { + Injector.web3PrivateService.updateWeb3PrivateAddress(chainType, address); + } +} diff --git a/SDK-mstr/src/features/common/constants/fake-wallet-address.ts b/SDK-mstr/src/features/common/constants/fake-wallet-address.ts new file mode 100644 index 0000000..6e8cab8 --- /dev/null +++ b/SDK-mstr/src/features/common/constants/fake-wallet-address.ts @@ -0,0 +1,2 @@ +/* used for passing fromAddress in swap/quote requests with disabled wallet */ +export const FAKE_WALLET_ADDRESS = '0xe388Ed184958062a2ea29B7fD049ca21244AE02e'; diff --git a/SDK-mstr/src/features/common/constants/proxy-supported-blockchain.ts b/SDK-mstr/src/features/common/constants/proxy-supported-blockchain.ts new file mode 100644 index 0000000..436501b --- /dev/null +++ b/SDK-mstr/src/features/common/constants/proxy-supported-blockchain.ts @@ -0,0 +1,52 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const proxySupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.POLYGON_ZKEVM, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.MOONRIVER, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.TELOS, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.CRONOS, + BLOCKCHAIN_NAME.ZK_SYNC, + BLOCKCHAIN_NAME.PULSECHAIN, + BLOCKCHAIN_NAME.AURORA, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.MANTLE, + BLOCKCHAIN_NAME.SCROLL, + BLOCKCHAIN_NAME.MANTA_PACIFIC, + BLOCKCHAIN_NAME.METIS, + BLOCKCHAIN_NAME.BLAST, + BLOCKCHAIN_NAME.KROMA, + BLOCKCHAIN_NAME.HORIZEN_EON, + BLOCKCHAIN_NAME.MERLIN, + BLOCKCHAIN_NAME.ROOTSTOCK, + BLOCKCHAIN_NAME.MODE, + BLOCKCHAIN_NAME.ZK_FAIR, + BLOCKCHAIN_NAME.ZETACHAIN, + BLOCKCHAIN_NAME.ZK_LINK, + BLOCKCHAIN_NAME.XLAYER, + BLOCKCHAIN_NAME.TAIKO, + BLOCKCHAIN_NAME.SEI + // BLOCKCHAIN_NAME.OKE_X_CHAIN, + // BLOCKCHAIN_NAME.GNOSIS, + // BLOCKCHAIN_NAME.FUSE, + // BLOCKCHAIN_NAME.MOONBEAM, + // BLOCKCHAIN_NAME.CELO, + // BLOCKCHAIN_NAME.BOBA, + // BLOCKCHAIN_NAME.KAVA, + // BLOCKCHAIN_NAME.BITGERT, + // BLOCKCHAIN_NAME.KLAYTN, + // BLOCKCHAIN_NAME.SYSCOIN, + // BLOCKCHAIN_NAME.VELAS, + // BLOCKCHAIN_NAME.OASIS +] as const; + +export const ON_CHAIN_PROXY_DISABLED_CHAINS = [] as const; + +export type ProxySupportedBlockchain = (typeof proxySupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/common/constants/wl-contract-abi.ts b/SDK-mstr/src/features/common/constants/wl-contract-abi.ts new file mode 100644 index 0000000..67937e2 --- /dev/null +++ b/SDK-mstr/src/features/common/constants/wl-contract-abi.ts @@ -0,0 +1,46 @@ +import { AbiItem } from 'web3-utils'; + +export const wlContractAbi: AbiItem[] = [ + { + inputs: [], + name: 'getAvailableAnyRouters', + outputs: [{ internalType: 'address[]', name: '', type: 'address[]' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'getAvailableCrossChains', + outputs: [{ internalType: 'address[]', name: '', type: 'address[]' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'getAvailableDEXs', + outputs: [{ internalType: 'address[]', name: '', type: 'address[]' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'address', name: '_anyRouter', type: 'address' }], + name: 'isWhitelistedAnyRouter', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'address', name: '_crossChain', type: 'address' }], + name: 'isWhitelistedCrossChain', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'address', name: '_dex', type: 'address' }], + name: 'isWhitelistedDEX', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/common/constants/wl-contract-address.ts b/SDK-mstr/src/features/common/constants/wl-contract-address.ts new file mode 100644 index 0000000..a148956 --- /dev/null +++ b/SDK-mstr/src/features/common/constants/wl-contract-address.ts @@ -0,0 +1,10 @@ +import { EVM_BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +export const wlContractAddress: Record = Object.values( + EVM_BLOCKCHAIN_NAME +).reduce((acc, blockchain) => { + return { + ...acc, + [blockchain]: '' + }; +}, {} as Record); diff --git a/SDK-mstr/src/features/common/models/contract-params.ts b/SDK-mstr/src/features/common/models/contract-params.ts new file mode 100644 index 0000000..416e5c9 --- /dev/null +++ b/SDK-mstr/src/features/common/models/contract-params.ts @@ -0,0 +1,28 @@ +import { AbiItem } from 'web3-utils'; + +export interface ContractParams { + /** + * Contract address in source network. + */ + contractAddress: string; + + /** + * Contract abi in source network. + */ + contractAbi: AbiItem[]; + + /** + * Method's name to call in contract. + */ + methodName: string; + + /** + * Method's arguments to call method with. + */ + methodArguments: unknown[]; + + /** + * Value in Wei to send with transaction. + */ + value: string; +} diff --git a/SDK-mstr/src/features/common/models/encode-transaction-options.ts b/SDK-mstr/src/features/common/models/encode-transaction-options.ts new file mode 100644 index 0000000..d6a012d --- /dev/null +++ b/SDK-mstr/src/features/common/models/encode-transaction-options.ts @@ -0,0 +1,42 @@ +import { + EIP1559Gas, + SingleGasPrice +} from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/gas-price'; + +/** + * Stores options for transaction in `encode` function. + */ +export interface EncodeTransactionOptions { + /** + * User wallet address to send swap transaction. + */ + fromAddress: string; + + receiverAddress?: string; + + /** + * Transaction gas price options. + */ + gasPriceOptions?: EIP1559Gas | SingleGasPrice; + + /** + * Evm-transaction gas limit. + */ + gasLimit?: string; + + /** + * Uniquely for Uniswap v2, defines which method to use - regular or supporting fee. + */ + supportFee?: boolean; + + /** + * Tron-transaction fee limit. + */ + feeLimit?: number; + + referrer?: string; + + skipAmountCheck?: boolean; + + useCacheData?: boolean; +} diff --git a/SDK-mstr/src/features/common/models/non-evm-chain-address-correct-response.ts b/SDK-mstr/src/features/common/models/non-evm-chain-address-correct-response.ts new file mode 100644 index 0000000..82ea283 --- /dev/null +++ b/SDK-mstr/src/features/common/models/non-evm-chain-address-correct-response.ts @@ -0,0 +1,5 @@ +export interface nonEvmChainAddressCorrectResponse { + isActivated: null; + result: boolean; + message: null | string; +} diff --git a/SDK-mstr/src/features/common/models/swap-transaction-options.ts b/SDK-mstr/src/features/common/models/swap-transaction-options.ts new file mode 100644 index 0000000..3506c38 --- /dev/null +++ b/SDK-mstr/src/features/common/models/swap-transaction-options.ts @@ -0,0 +1,61 @@ +import { + EIP1559Gas, + SingleGasPrice +} from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/gas-price'; + +export interface SwapTransactionOptions { + /** + * Callback to be called, when user confirm swap transaction. + * @param hash Transaction hash. + */ + onConfirm?: (hash: string) => void; + + /** + * Callback to be called, when user confirm approve transaction. + * @param hash Transaction hash. + */ + onApprove?: (hash: string | null) => void; + + /** + * Tokens receiver address. + */ + receiverAddress?: string; + + /** + * Transaction gas price options. + */ + gasPriceOptions?: EIP1559Gas | SingleGasPrice; + + /** + * Evm-transaction gas limit. + */ + gasLimit?: string; + + /** + * Approve evm-transaction gas limit. + * Will be used for approve transaction, if it is called before swap. + */ + approveGasLimit?: string; + + /** + * Tron-transaction fee limit. + */ + feeLimit?: number; + + /** + * Approve tron-transaction fee limit. + * Will be used for approve transaction, if it is called before swap. + */ + approveFeeLimit?: number; + + testMode?: boolean; + + useCacheData?: boolean; + + referrer?: string; + + /** + * Use in case of eip-155 + */ + useEip155?: boolean; +} diff --git a/SDK-mstr/src/features/common/providers/bridgers/constants/bridgers-native-address.ts b/SDK-mstr/src/features/common/providers/bridgers/constants/bridgers-native-address.ts new file mode 100644 index 0000000..7617093 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/bridgers/constants/bridgers-native-address.ts @@ -0,0 +1 @@ +export const bridgersNativeAddress = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'; diff --git a/SDK-mstr/src/features/common/providers/bridgers/constants/to-bridgers-blockchain.ts b/SDK-mstr/src/features/common/providers/bridgers/constants/to-bridgers-blockchain.ts new file mode 100644 index 0000000..3b3c84f --- /dev/null +++ b/SDK-mstr/src/features/common/providers/bridgers/constants/to-bridgers-blockchain.ts @@ -0,0 +1,10 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { BridgersCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/constants/bridgers-cross-chain-supported-blockchain'; + +export const toBridgersBlockchain: Record = { + [BLOCKCHAIN_NAME.ETHEREUM]: 'ETH', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: 'BSC', + [BLOCKCHAIN_NAME.POLYGON]: 'POLYGON', + [BLOCKCHAIN_NAME.FANTOM]: 'FANTOM', + [BLOCKCHAIN_NAME.TRON]: 'TRON' +}; diff --git a/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-contract-addresses.ts b/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-contract-addresses.ts new file mode 100644 index 0000000..87eb9d4 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-contract-addresses.ts @@ -0,0 +1,10 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { BridgersCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/constants/bridgers-cross-chain-supported-blockchain'; + +export const bridgersContractAddresses: Record = { + [BLOCKCHAIN_NAME.ETHEREUM]: '0x92e929d8b2c8430bcaf4cd87654789578bb2b786', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: '0x1ed5685f345b2fa564ea4a670de1fde39e484751', + [BLOCKCHAIN_NAME.POLYGON]: '0x242Ea2A8C4a3377A738ed8a0d8cC0Fe8B4D6C36E', + [BLOCKCHAIN_NAME.FANTOM]: '0x8f957ed3f969d7b6e5d6df81e61a5ff45f594dd1', + [BLOCKCHAIN_NAME.TRON]: 'TEorZTZ5MHx8SrvsYs1R3Ds5WvY1pVoMSA' +}; diff --git a/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-get-trans-data-by-id-api.ts b/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-get-trans-data-by-id-api.ts new file mode 100644 index 0000000..4ff857d --- /dev/null +++ b/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-get-trans-data-by-id-api.ts @@ -0,0 +1,10 @@ +export interface BridgersGetTransDataByIdRequest { + orderId: string; +} + +export interface BridgersGetTransDataByIdResponse { + data: { + status: string; + toHash: string; + }; +} diff --git a/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-quote-api.ts b/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-quote-api.ts new file mode 100644 index 0000000..582d970 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-quote-api.ts @@ -0,0 +1,23 @@ +export interface BridgersQuoteRequest { + fromTokenAddress: string; + toTokenAddress: string; + fromTokenAmount: string; + fromTokenChain: string; + toTokenChain: string; +} + +export interface BridgersQuoteResponse { + resMsg: string; + resCode: number; + data: { + txData: { + toTokenAmount: number; + amountOutMin: string; + fee: number; + chainFee: string; + depositMin: string; + depositMax: string; + contractAddress: string; + }; + }; +} diff --git a/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-source-flag.ts b/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-source-flag.ts new file mode 100644 index 0000000..8e0100c --- /dev/null +++ b/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-source-flag.ts @@ -0,0 +1,5 @@ +/** + * Use `rubic` if swap is sent through rubic-proxy contract. + * Use `rubic_widget` if swap is sent directly through bridgers contract. + */ +export type BridgersSourceFlag = 'rubic' | 'rubic_widget'; diff --git a/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-swap-api.ts b/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-swap-api.ts new file mode 100644 index 0000000..e6c6465 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-swap-api.ts @@ -0,0 +1,24 @@ +import { BridgersSourceFlag } from 'src/features/common/providers/bridgers/models/bridgers-source-flag'; +import { EvmBridgersTransactionData } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/evm-bridgers-trade/models/evm-bridgers-transaction-data'; +import { TronBridgersTransactionData } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/tron-bridgers-trade/models/tron-bridgers-transaction-data'; + +export interface BridgersSwapRequest { + fromTokenAddress: string; + toTokenAddress: string; + fromAddress: string; + toAddress: string; + fromTokenChain: string; + toTokenChain: string; + fromTokenAmount: string; + amountOutMin: string; + equipmentNo: string; + sourceFlag: BridgersSourceFlag; +} + +export interface BridgersSwapResponse< + T extends EvmBridgersTransactionData | TronBridgersTransactionData +> { + data: { + txData: T; + }; +} diff --git a/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-update-data-and-status-api.ts b/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-update-data-and-status-api.ts new file mode 100644 index 0000000..1e45386 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/bridgers/models/bridgers-update-data-and-status-api.ts @@ -0,0 +1,22 @@ +import { BridgersSourceFlag } from 'src/features/common/providers/bridgers/models/bridgers-source-flag'; + +export interface BridgersUpdateDataAndStatusRequest { + hash: string; + fromTokenChain: string; + sourceFlag: BridgersSourceFlag; + fromTokenAddress?: string; + toTokenAddress?: string; + fromAddress?: string; + toAddress?: string; + toTokenChain?: string; + fromTokenAmount?: string; + amountOutMin?: string; + fromCoinCode?: string; + toCoinCode?: string; +} + +export interface BridgersUpdateDataAndStatusResponse { + data: { + orderId?: string; + }; +} diff --git a/SDK-mstr/src/features/common/providers/changenow/constants/changenow-api-key.ts b/SDK-mstr/src/features/common/providers/changenow/constants/changenow-api-key.ts new file mode 100644 index 0000000..2984a4a --- /dev/null +++ b/SDK-mstr/src/features/common/providers/changenow/constants/changenow-api-key.ts @@ -0,0 +1 @@ +export const changenowApiKey = '7e7de5c280d2fa7f2b11a2f1b4e00c5113bc389f4b7394a4c7ae3804b9854c83'; diff --git a/SDK-mstr/src/features/common/providers/dln/dln-api-service.ts b/SDK-mstr/src/features/common/providers/dln/dln-api-service.ts new file mode 100644 index 0000000..e31680a --- /dev/null +++ b/SDK-mstr/src/features/common/providers/dln/dln-api-service.ts @@ -0,0 +1,70 @@ +import { Injector } from 'src/core/injector/injector'; +import { EstimationRequest } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/estimation-request'; +import { TransactionRequest } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-request'; +import { + DeBridgeFilteredListApiResponse, + DeBridgeOrderApiResponse, + DeBridgeOrderApiStatusResponse +} from 'src/features/cross-chain/status-manager/models/statuses-api'; +import { DlnOnChainEstimateRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-estimate-request'; +import { DlnOnChainEstimateResponse } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-estimate-response'; +import { DlnOnChainSwapRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-request'; +import { DlnOnChainSwapResponse } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-response'; + +export class DlnApiService { + public static apiEndpoint = 'https://api.dln.trade/v1.0'; + + public static fetchCrossChainQuote(requestParams: EstimationRequest): Promise { + return Injector.httpClient.get(`${DlnApiService.apiEndpoint}/dln/order/quote`, { + params: requestParams as unknown as {} + }); + } + + public static fetchCrossChainSwapData(requestParams: TransactionRequest): Promise { + return Injector.httpClient.get(`${DlnApiService.apiEndpoint}/dln/order/create-tx`, { + params: requestParams as unknown as {} + }); + } + + public static fetchOnChainQuote( + requestParams: DlnOnChainEstimateRequest + ): Promise { + return Injector.httpClient.get( + `${DlnApiService.apiEndpoint}/chain/estimation`, + { + params: requestParams as unknown as {} + } + ); + } + + public static fetchOnChainSwapData( + requestParams: DlnOnChainSwapRequest + ): Promise> { + return Injector.httpClient.get>( + `${DlnApiService.apiEndpoint}/chain/transaction`, + { + params: requestParams as unknown as {} + } + ); + } + + public static fetchCrossChainEventMetaData(orderId: string): Promise { + return Injector.httpClient.get( + `https://stats-api.dln.trade/api/Orders/${orderId}` + ); + } + + public static fetchCrossChainStatus(orderId: string): Promise { + return Injector.httpClient.get( + `${DlnApiService.apiEndpoint}/dln/order/${orderId}/status` + ); + } + + public static fetchCrossChainOrdersByHash( + sourceTransactionHash: string + ): Promise { + return Injector.httpClient.get( + `${DlnApiService.apiEndpoint}/dln/tx/${sourceTransactionHash}/order-ids` + ); + } +} diff --git a/SDK-mstr/src/features/common/providers/dln/dln-utils.ts b/SDK-mstr/src/features/common/providers/dln/dln-utils.ts new file mode 100644 index 0000000..40a49f6 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/dln/dln-utils.ts @@ -0,0 +1,24 @@ +import { Token } from 'src/common/tokens'; +import { BLOCKCHAIN_NAME, BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; + +export class DlnUtils { + public static getSupportedAddress(token: Token): string { + if (token.blockchain === BLOCKCHAIN_NAME.SOLANA && token.isNative) { + return '11111111111111111111111111111111'; + } + return token.address; + } + + public static getFakeReceiver(blockchain: BlockchainName): string { + const type = BlockchainsInfo.getChainType(blockchain); + if (type === CHAIN_TYPE.EVM) { + return '0xe388Ed184958062a2ea29B7fD049ca21244AE02e'; + } + if (type === CHAIN_TYPE.SOLANA) { + return 'HZgssrdZjBdypDux7tHWWDZ7hF7hhwUXN445t85GaoQT'; + } + throw new Error('Chain type is not supported'); + } +} diff --git a/SDK-mstr/src/features/common/providers/dln/models/dln-estimation.ts b/SDK-mstr/src/features/common/providers/dln/models/dln-estimation.ts new file mode 100644 index 0000000..cfb499c --- /dev/null +++ b/SDK-mstr/src/features/common/providers/dln/models/dln-estimation.ts @@ -0,0 +1,18 @@ +interface DlnBaseToken { + address: string; + name: string; + symbol: string; + decimals: number; +} + +export interface DlnTokenAmount extends DlnBaseToken { + amount: string; +} + +export interface DlnTokenMinAmount extends DlnTokenAmount { + minAmount: string; +} + +export interface DlnMaxTheoreticalAmountToken extends DlnTokenMinAmount { + maxTheoreticalAmount: string; +} diff --git a/SDK-mstr/src/features/common/providers/rango/constants/rango-api-common.ts b/SDK-mstr/src/features/common/providers/rango/constants/rango-api-common.ts new file mode 100644 index 0000000..9f563d0 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/rango/constants/rango-api-common.ts @@ -0,0 +1,3 @@ +export const RANGO_API_KEY = 'a24ca428-a18e-4e84-b57f-edb3e2a5bf13'; + +export const RANGO_API_ENDPOINT = 'https://api.rango.exchange/basic'; diff --git a/SDK-mstr/src/features/common/providers/rango/constants/rango-contract-address.ts b/SDK-mstr/src/features/common/providers/rango/constants/rango-contract-address.ts new file mode 100644 index 0000000..9a3704b --- /dev/null +++ b/SDK-mstr/src/features/common/providers/rango/constants/rango-contract-address.ts @@ -0,0 +1,83 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { rubicProxyContractAddress } from '../../../../cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { UniversalContract } from '../../../../cross-chain/calculation-manager/providers/common/models/universal-contract'; +import { RangoSupportedBlockchain } from '../models/rango-supported-blockchains'; + +export const rangoContractAddresses: Record = { + [BLOCKCHAIN_NAME.ETHEREUM]: { + providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + }, + [BLOCKCHAIN_NAME.POLYGON]: { + providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + }, + [BLOCKCHAIN_NAME.OPTIMISM]: { + providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + }, + [BLOCKCHAIN_NAME.ARBITRUM]: { + providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + }, + [BLOCKCHAIN_NAME.AVALANCHE]: { + providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + }, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: { + providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + }, + [BLOCKCHAIN_NAME.CRONOS]: { + providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + }, + [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: { + providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + }, + [BLOCKCHAIN_NAME.AURORA]: { + providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + }, + [BLOCKCHAIN_NAME.LINEA]: { + providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + }, + [BLOCKCHAIN_NAME.METIS]: { + providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + }, + [BLOCKCHAIN_NAME.ZK_SYNC]: { + providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + }, + [BLOCKCHAIN_NAME.BASE]: { + providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + } + // [BLOCKCHAIN_NAME.BLAST]: { + // providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + // providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + // rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + // } + // [BLOCKCHAIN_NAME.SCROLL]: { + // providerGateway: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + // providerRouter: '0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d', + // rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + // } +}; diff --git a/SDK-mstr/src/features/common/providers/rango/models/rango-api-best-route-types.ts b/SDK-mstr/src/features/common/providers/rango/models/rango-api-best-route-types.ts new file mode 100644 index 0000000..23784cc --- /dev/null +++ b/SDK-mstr/src/features/common/providers/rango/models/rango-api-best-route-types.ts @@ -0,0 +1,66 @@ +import { + RangoAmountRestrictionType, + RangoExpenseType, + RangoResponseToken, + RangoRoutingResultType, + RangoSwapperType +} from './rango-api-common-types'; +import { RangoTradeType } from './rango-api-trade-types'; + +export interface RangoBestRouteRequestOptions { + slippageTolerance: number; + swapperGroups?: RangoTradeType[]; + swappersGroupsExclude?: boolean; +} + +export interface RangoBestRouteResponse { + requestId: string; + resultType: RangoRoutingResultType; + route: RangoBestRouteSimulationResult | null; + error: string | null; +} + +export interface RangoBestRouteSimulationResult { + from: RangoResponseToken; + to: RangoResponseToken; + outputAmount: string; + outputAmountMin: string; + outputAmountUsd: number | null; + swapper: RangoSwapperMeta; + path: RangoQuotePath[] | null; + fee: RangoSwapFee[]; + feeUsd: number | null; + amountRestriction: RangoAmountRestriction | null; + estimatedTimeInSeconds: number; +} + +interface RangoSwapperMeta { + id: string; + title: string; + logo: string; + swapperGroup: string; + types: RangoSwapperType[]; +} + +export interface RangoQuotePath { + from: RangoResponseToken; + to: RangoResponseToken; + swapper: RangoSwapperMeta; + swapperType: RangoSwapperType; + expectedOutput: string; + inputAmount: string; + estimatedTimeInSeconds: number; +} + +export interface RangoSwapFee { + name: string; + token: RangoResponseToken; + expenseType: RangoExpenseType; + amount: string; +} + +interface RangoAmountRestriction { + min: string | null; + max: string | null; + type: RangoAmountRestrictionType; +} diff --git a/SDK-mstr/src/features/common/providers/rango/models/rango-api-blockchain-names.ts b/SDK-mstr/src/features/common/providers/rango/models/rango-api-blockchain-names.ts new file mode 100644 index 0000000..d079cef --- /dev/null +++ b/SDK-mstr/src/features/common/providers/rango/models/rango-api-blockchain-names.ts @@ -0,0 +1,23 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { RangoSupportedBlockchain } from './rango-supported-blockchains'; + +export const rangoApiBlockchainNames: Record = { + [BLOCKCHAIN_NAME.ETHEREUM]: 'ETH', + [BLOCKCHAIN_NAME.POLYGON]: 'POLYGON', + [BLOCKCHAIN_NAME.OPTIMISM]: 'OPTIMISM', + [BLOCKCHAIN_NAME.ARBITRUM]: 'ARBITRUM', + [BLOCKCHAIN_NAME.AVALANCHE]: 'AVAX_CCHAIN', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: 'BSC', + [BLOCKCHAIN_NAME.CRONOS]: 'CRONOS', + [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: 'POLYGONZK', + [BLOCKCHAIN_NAME.AURORA]: 'AURORA', + [BLOCKCHAIN_NAME.BASE]: 'BASE', + [BLOCKCHAIN_NAME.METIS]: 'METIS', + [BLOCKCHAIN_NAME.ZK_SYNC]: 'ZKSYNC', + [BLOCKCHAIN_NAME.LINEA]: 'LINEA' + // [BLOCKCHAIN_NAME.BLAST]: 'BLAST', + // [BLOCKCHAIN_NAME.SCROLL]: 'SCROLL' +}; + +export type RangoBlockchainName = keyof typeof rangoApiBlockchainNames; diff --git a/SDK-mstr/src/features/common/providers/rango/models/rango-api-common-types.ts b/SDK-mstr/src/features/common/providers/rango/models/rango-api-common-types.ts new file mode 100644 index 0000000..f398a3a --- /dev/null +++ b/SDK-mstr/src/features/common/providers/rango/models/rango-api-common-types.ts @@ -0,0 +1,36 @@ +import { RangoBlockchainName } from './rango-api-blockchain-names'; + +export interface RangoResponseToken { + blockchain: RangoBlockchainName; + chainId: string | null; + address: string | null; + symbol: string; + name: string; + decimals: number; + image: string; + blockchainImage: string; + usdPrice: number | null; + isPopular: boolean; +} + +export const RangoRoutingResult = { + OK: 'OK', + HIGH_IMPACT: 'HIGH_IMPACT', + NO_ROUTE: 'NO_ROUTE', + INPUT_LIMIT_ISSUE: 'INPUT_LIMIT_ISSUE' +} as const; + +export interface HttpClientParams { + [param: string]: string | number | boolean | readonly (string | number | boolean)[]; +} + +export type RangoSwapperType = 'BRIDGE' | 'DEX' | 'AGGREGATOR'; + +export type RangoExpenseType = + | 'FROM_SOURCE_WALLET' + | 'DECREASE_FROM_OUTPUT' + | 'FROM_DESTINATION_WALLET'; + +export type RangoAmountRestrictionType = 'INCLUSIVE' | 'EXCLUSIVE'; + +export type RangoRoutingResultType = (typeof RangoRoutingResult)[keyof typeof RangoRoutingResult]; diff --git a/SDK-mstr/src/features/common/providers/rango/models/rango-api-status-types.ts b/SDK-mstr/src/features/common/providers/rango/models/rango-api-status-types.ts new file mode 100644 index 0000000..9bb862c --- /dev/null +++ b/SDK-mstr/src/features/common/providers/rango/models/rango-api-status-types.ts @@ -0,0 +1,43 @@ +import { RangoResponseToken } from './rango-api-common-types'; + +export const RANGO_SWAP_STATUS = { + SUCCESS: 'success', + FAILED: 'failed', + RUNNING: 'running' +} as const; + +export type RangoSwapStatus = (typeof RANGO_SWAP_STATUS)[keyof typeof RANGO_SWAP_STATUS]; + +export interface RangoTxStatusResponse { + status: RangoSwapStatus | null; + error: string | null; + output: RangoStatusOutput | null; + explorerUrl: RangoSwapExplorerUrl[] | null; + bridgeData: RangoBridgeData | null; +} + +interface RangoStatusOutput { + amount: string; + receivedToken: RangoResponseToken; + type: 'REVERTED_TO_INPUT' | 'MIDDLE_ASSET_IN_SRC' | 'MIDDLE_ASSET_IN_DEST' | 'DESIRED_OUTPUT'; +} + +interface RangoSwapExplorerUrl { + description: string | null; + url: string; +} + +interface RangoBridgeData { + srcChainId: number; + srcTxHash: string | null; + srcToken: string | null; + srcTokenAmt: string; + srcTokenDecimals: number; + srcTokenPrice: string | null; + destChainId: number; + destTxHash: string | null; + destToken: string | null; + destTokenAmt: string | null; + destTokenDecimals: number; + destTokenPrice: string | null; +} diff --git a/SDK-mstr/src/features/common/providers/rango/models/rango-api-swap-types.ts b/SDK-mstr/src/features/common/providers/rango/models/rango-api-swap-types.ts new file mode 100644 index 0000000..2ac5eae --- /dev/null +++ b/SDK-mstr/src/features/common/providers/rango/models/rango-api-swap-types.ts @@ -0,0 +1,61 @@ +import { RangoBestRouteRequestOptions, RangoBestRouteResponse } from './rango-api-best-route-types'; + +export interface RangoSwapRequestOptions extends RangoBestRouteRequestOptions { + receiverAddress?: string; + fromAddress?: string; +} + +export interface RangoSwapTransactionResponse extends RangoBestRouteResponse { + tx: RangoTransaction | null; +} + +export interface RangoTransaction { + type: 'EVM'; + blockChain: RangoEvmBlockchainMeta; + from: string | null; + approveTo: string | null; + approveData: string | null; + txTo: string; + txData: string | null; + value: string | null; + gasLimit: string | null; + gasPrice: string | null; + maxPriorityFeePerGas: string | null; + maxFeePerGas: string | null; +} + +interface RangoEvmBlockchainMeta { + type: 'EVM'; + name: string; + shortName: string; + displayName: string; + defaultDecimals: number; + feeAssets: RangoFeeAsset[]; + addressPatterns: string[]; + logo: string; + color: string; + sort: number; + enabled: boolean; + chainId: string | null; + info: RangoEvmChainInfo; +} + +interface RangoFeeAsset { + blockchain: string; + address: string | null; + symbol: string; +} + +interface RangoEvmChainInfo { + infoType: 'EvmMetaInfo'; + chainName: string; + nativeCurrency: { + name: string; + symbol: string; + decimals: number; + }; + rpcUrls: string[]; + blockExplorerUrls: string[]; + addressUrl: string; + transactionUrl: string; +} diff --git a/SDK-mstr/src/features/common/providers/rango/models/rango-api-symbol-names.ts b/SDK-mstr/src/features/common/providers/rango/models/rango-api-symbol-names.ts new file mode 100644 index 0000000..bf07ef5 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/rango/models/rango-api-symbol-names.ts @@ -0,0 +1,23 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { RangoSupportedBlockchain } from './rango-supported-blockchains'; + +export const rangoApiSymbols: Record = { + [BLOCKCHAIN_NAME.ETHEREUM]: 'ETH', + [BLOCKCHAIN_NAME.POLYGON]: 'MATIC', + [BLOCKCHAIN_NAME.OPTIMISM]: 'ETH', + [BLOCKCHAIN_NAME.ARBITRUM]: 'ETH', + [BLOCKCHAIN_NAME.AVALANCHE]: 'AVAX', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: 'BNB', + [BLOCKCHAIN_NAME.CRONOS]: 'CRO', + [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: 'ETH', + [BLOCKCHAIN_NAME.AURORA]: 'ETH', + [BLOCKCHAIN_NAME.BASE]: 'ETH', + [BLOCKCHAIN_NAME.ZK_SYNC]: 'ETH', + [BLOCKCHAIN_NAME.LINEA]: 'ETH', + [BLOCKCHAIN_NAME.METIS]: 'METIS' + // [BLOCKCHAIN_NAME.BLAST]: 'ETH', + // [BLOCKCHAIN_NAME.SCROLL]: 'ETH' +}; + +export type RangoSymbols = keyof typeof rangoApiSymbols; diff --git a/SDK-mstr/src/features/common/providers/rango/models/rango-api-trade-types.ts b/SDK-mstr/src/features/common/providers/rango/models/rango-api-trade-types.ts new file mode 100644 index 0000000..6a5ef00 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/rango/models/rango-api-trade-types.ts @@ -0,0 +1,99 @@ +import { + BRIDGE_TYPE, + BridgeType +} from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +const RANGO_TO_RUBIC_ON_CHAIN_PROVIDERS = { + '10KSwap': ON_CHAIN_TRADE_TYPE['10K_SWAP'], + 'Pangolin Swap': ON_CHAIN_TRADE_TYPE.PANGOLIN, + 'Sushi Swap': ON_CHAIN_TRADE_TYPE.SUSHI_SWAP, + Osmosis: ON_CHAIN_TRADE_TYPE.OSMOSIS_SWAP, + UniSwapV2: ON_CHAIN_TRADE_TYPE.UNISWAP_V2, + 'VVS Finance': ON_CHAIN_TRADE_TYPE.VVS_FINANCE, + 'MM Finance': ON_CHAIN_TRADE_TYPE.MM_FINANCE, + 'Crona Swap': ON_CHAIN_TRADE_TYPE.CRONA_SWAP, + 'Oolong Swap': ON_CHAIN_TRADE_TYPE.OOLONG_SWAP, + 'Trisolaris Swap': ON_CHAIN_TRADE_TYPE.TRISOLARIS, + 'Mojito Swap': ON_CHAIN_TRADE_TYPE.MOJITO_SWAP, + Netswap: ON_CHAIN_TRADE_TYPE.NET_SWAP, + 'Voltage Swap': ON_CHAIN_TRADE_TYPE.VOLTAGE_SWAP, + PancakeV2: ON_CHAIN_TRADE_TYPE.PANCAKE_SWAP, + PancakeV3: ON_CHAIN_TRADE_TYPE.PANCAKE_SWAP_V3, + UniSwapV3: ON_CHAIN_TRADE_TYPE.UNI_SWAP_V3, + KyberSwapV3: ON_CHAIN_TRADE_TYPE.KYBER_SWAP, + Jupiter: ON_CHAIN_TRADE_TYPE.JUPITER, + 'Open Ocean': ON_CHAIN_TRADE_TYPE.OPEN_OCEAN, + CurveFi: ON_CHAIN_TRADE_TYPE.CURVE, + 'Quick Swap': ON_CHAIN_TRADE_TYPE.QUICK_SWAP, + 'XY Finance': ON_CHAIN_TRADE_TYPE.XY_DEX, + Solarbeam: ON_CHAIN_TRADE_TYPE.SOLAR_BEAM, + 'Aurora Swap': ON_CHAIN_TRADE_TYPE.AURORA_SWAP, + 'Stella Swap': ON_CHAIN_TRADE_TYPE.STELLA_SWAP, + '1Inch': ON_CHAIN_TRADE_TYPE.ONE_INCH, + 'Beam Swap': ON_CHAIN_TRADE_TYPE.BEAM_SWAP, + ParaSwap: ON_CHAIN_TRADE_TYPE.PARA_SWAP, + 'Synapse Swapper': ON_CHAIN_TRADE_TYPE.SYNAPSE, + 'Fin Kujira': ON_CHAIN_TRADE_TYPE.FINKUJIRA, + 'Solana Wrapper': ON_CHAIN_TRADE_TYPE.SOLANA, + Avnu: ON_CHAIN_TRADE_TYPE.AVNU, + EchoDEX: ON_CHAIN_TRADE_TYPE.ECHO_DEX, + SpaceFi: ON_CHAIN_TRADE_TYPE.SPACEFI_SWAP, + 'Wynd Dex': ON_CHAIN_TRADE_TYPE.WYND, + 'Sun Swap': ON_CHAIN_TRADE_TYPE.SUN_SWAP, + MDex: ON_CHAIN_TRADE_TYPE.MDEX, + 'Okc Swap': ON_CHAIN_TRADE_TYPE.OKC_SWAP, + 'Cherry Swap': ON_CHAIN_TRADE_TYPE.CHERRY_SWAP + // If you need to disable manually some dex or bridge in code and this dex/bridge not included in config - you can get + // full list of rango dexes here https://api.rango.exchange/basic/meta/swappers?apiKey=c6381a79-2817-4602-83bf-6a641a409e32 +} as const; + +export const RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS = Object.entries( + RANGO_TO_RUBIC_ON_CHAIN_PROVIDERS +).reduce( + (acc, [key, value]) => ({ ...acc, [value]: key }), + {} as Record +); + +const RANGO_TO_RUBIC_CROSS_CHAIN_PROVIDERS = { + Across: BRIDGE_TYPE.ACROSS, + Voyager: BRIDGE_TYPE.VOYAGER, + CBridge: BRIDGE_TYPE.CBRIDGE, + 'Rainbow Bridge': BRIDGE_TYPE.RAINBOW, + 'Synapse Bridge': BRIDGE_TYPE.SYNAPSE, + 'Optimism Bridge': BRIDGE_TYPE.OPTIMISM_GATEWAY, + Orbiter: BRIDGE_TYPE.ORBITER_BRIDGE, + 'Maya Protocol': BRIDGE_TYPE.MAYA_PROTOCOL, + 'XY Finance': BRIDGE_TYPE.XY, + ThorChain: BRIDGE_TYPE.THORCHAIN, + 'Arbitrum Bridge': BRIDGE_TYPE.ARBITRUM_BRIDGE, + AllBridge: BRIDGE_TYPE.ALLBRIDGE, + Hyphen: BRIDGE_TYPE.HYPHEN, + Circle: BRIDGE_TYPE.CIRCLE_CELER_BRIDGE, + IBC: BRIDGE_TYPE.IBC, + Stargate: BRIDGE_TYPE.STARGATE, + Satellite: BRIDGE_TYPE.SATELLITE, + Symbiosis: BRIDGE_TYPE.SYMBIOSIS, + Osmosis: BRIDGE_TYPE.OSMOSIS_BRIDGE + // Full list rango bridges here https://api.rango.exchange/basic/meta/swappers?apiKey=c6381a79-2817-4602-83bf-6a641a409e32 +} as const; + +export const RANGO_TO_RUBIC_PROVIDERS = { + ...RANGO_TO_RUBIC_ON_CHAIN_PROVIDERS, + ...RANGO_TO_RUBIC_CROSS_CHAIN_PROVIDERS +} as const; + +export const RUBIC_TO_RANGO_PROVIDERS = Object.entries(RANGO_TO_RUBIC_PROVIDERS).reduce( + (acc, [key, value]) => ({ ...acc, [value]: key }), + {} as Record +); + +export type RubicOnChainTradeTypeForRango = + (typeof RANGO_TO_RUBIC_ON_CHAIN_PROVIDERS)[keyof typeof RANGO_TO_RUBIC_ON_CHAIN_PROVIDERS]; + +export type RangoOnChainTradeType = keyof typeof RANGO_TO_RUBIC_ON_CHAIN_PROVIDERS; + +export type RangoTradeType = keyof typeof RANGO_TO_RUBIC_PROVIDERS; diff --git a/SDK-mstr/src/features/common/providers/rango/models/rango-parser-types.ts b/SDK-mstr/src/features/common/providers/rango/models/rango-parser-types.ts new file mode 100644 index 0000000..b9c0f45 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/rango/models/rango-parser-types.ts @@ -0,0 +1,42 @@ +export interface RangoSwapQueryParams { + apiKey: string; + from: string; + to: string; + amount: string; + slippage: number; + fromAddress: string; + toAddress: string; + /** + * List of all accepted swappers (e.g. providers), an empty list means no filter is required + */ + swapperGroups?: string; + /** + * Indicates include/exclude mode for the swappers param + */ + swappersGroupsExclude?: boolean; + /* true means sending tx only via rango-contracts */ + contractCall?: boolean; +} + +export interface RangoBestRouteQueryParams { + apiKey: string; + from: string; + to: string; + amount: string; + slippage?: number; + swapperGroups?: string; + swappersGroupsExclude?: boolean; + /* true means sending tx only via rango-contracts */ + contractCall?: boolean; +} + +/** + * @property {string} apiKey + * @property {string} requestId Random UUID returned in swap/quote methodes in response + * @property {string} srcTxHash In Rango-api used as `txId` queryParam in getTxStatus request + */ +export interface RangoTxStatusQueryParams { + apiKey: string; + requestId: string; + txId: string; +} diff --git a/SDK-mstr/src/features/common/providers/rango/models/rango-supported-blockchains.ts b/SDK-mstr/src/features/common/providers/rango/models/rango-supported-blockchains.ts new file mode 100644 index 0000000..862d620 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/rango/models/rango-supported-blockchains.ts @@ -0,0 +1,21 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const rangoSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.CRONOS, + BLOCKCHAIN_NAME.POLYGON_ZKEVM, + BLOCKCHAIN_NAME.AURORA, + BLOCKCHAIN_NAME.METIS, + BLOCKCHAIN_NAME.ZK_SYNC, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.LINEA + // BLOCKCHAIN_NAME.BLAST + // BLOCKCHAIN_NAME.SCROLL +] as const; + +export type RangoSupportedBlockchain = (typeof rangoSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/common/providers/rango/services/rango-parser.ts b/SDK-mstr/src/features/common/providers/rango/services/rango-parser.ts new file mode 100644 index 0000000..0b05413 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/rango/services/rango-parser.ts @@ -0,0 +1,90 @@ +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { FAKE_WALLET_ADDRESS } from 'src/features/common/constants/fake-wallet-address'; + +import { RANGO_API_KEY } from '../constants/rango-api-common'; +import { RangoBestRouteRequestOptions } from '../models/rango-api-best-route-types'; +import { RangoSwapRequestOptions } from '../models/rango-api-swap-types'; +import { + RangoBestRouteQueryParams, + RangoSwapQueryParams, + RangoTxStatusQueryParams +} from '../models/rango-parser-types'; +import { RangoUtils } from '../utils/rango-utils'; + +export class RangoCommonParser { + /** + * @description Transform parameters to required view for rango-api + */ + public static async getBestRouteQueryParams( + from: PriceTokenAmount, + toToken: PriceToken, + options: RangoBestRouteRequestOptions + ): Promise { + const fromParam = await RangoUtils.getFromToQueryParam(from); + const toParam = await RangoUtils.getFromToQueryParam(toToken); + + const amountParam = Web3Pure.toWei(from.tokenAmount, from.decimals); + + const apiKey = RANGO_API_KEY; + + const swapperGroups = options.swapperGroups?.join(','); + + return { + apiKey, + from: fromParam, + to: toParam, + amount: amountParam, + ...(options.slippageTolerance && { slippage: options.slippageTolerance * 100 }), + ...(options.swapperGroups?.length && { swapperGroups }), + swappersGroupsExclude: options?.swappersGroupsExclude ?? true, + contractCall: true + }; + } + + public static async getSwapQueryParams( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options: RangoSwapRequestOptions + ): Promise { + const amount = Web3Pure.toWei(fromToken.tokenAmount, fromToken.decimals); + + const from = await RangoUtils.getFromToQueryParam(fromToken); + const to = await RangoUtils.getFromToQueryParam(toToken); + + const walletAddress = Injector.web3PrivateService.getWeb3PrivateByBlockchain( + fromToken.blockchain + ).address; + const fromAddress = options.fromAddress || walletAddress; + const toAddress = options?.receiverAddress || walletAddress || FAKE_WALLET_ADDRESS; + + const slippage = options.slippageTolerance * 100; + const apiKey = RANGO_API_KEY; + + const swapperGroups = options.swapperGroups?.join(','); + + return { + apiKey, + amount, + from, + to, + fromAddress, + slippage, + toAddress, + ...(options.swapperGroups?.length && { swapperGroups }), + swappersGroupsExclude: options?.swappersGroupsExclude ?? true, + contractCall: true + }; + } + + public static getTxStatusQueryParams( + srcTxHash: string, + requestId: string + ): RangoTxStatusQueryParams { + const apiKey = RANGO_API_KEY; + + return { apiKey, requestId, txId: srcTxHash }; + } +} diff --git a/SDK-mstr/src/features/common/providers/rango/utils/rango-utils.ts b/SDK-mstr/src/features/common/providers/rango/utils/rango-utils.ts new file mode 100644 index 0000000..b4cbcc9 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/rango/utils/rango-utils.ts @@ -0,0 +1,70 @@ +import { PriceToken, Token } from 'src/common/tokens'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { + TX_STATUS, + TxStatus +} from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { BridgeType } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +import { rangoApiBlockchainNames, RangoBlockchainName } from '../models/rango-api-blockchain-names'; +import { RANGO_SWAP_STATUS, RangoSwapStatus } from '../models/rango-api-status-types'; +import { rangoApiSymbols } from '../models/rango-api-symbol-names'; +import { RANGO_TO_RUBIC_PROVIDERS, RangoTradeType } from '../models/rango-api-trade-types'; +import { RangoSupportedBlockchain } from '../models/rango-supported-blockchains'; + +export class RangoUtils { + /** + * @returns Query-param string in format `chainName.symbol--address`, chainName's compatible with rango-api + */ + public static async getFromToQueryParam(token: PriceToken): Promise { + const rangoBlockchainName = + rangoApiBlockchainNames[token.blockchain as RangoSupportedBlockchain]; + + const symbol = token.isNative + ? rangoApiSymbols[token.blockchain as RangoSupportedBlockchain] + : (await Token.createToken({ address: token.address, blockchain: token.blockchain })) + .symbol; + + if (token.isNative && token.blockchain === BLOCKCHAIN_NAME.METIS) { + return `${rangoBlockchainName}.${symbol}--0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000`; + } + + const param = token.isNative + ? `${rangoBlockchainName}.${symbol}` + : `${rangoBlockchainName}.${symbol}--${token.address}`; + + return param; + } + + public static convertStatusForRubic(rangoStatus: RangoSwapStatus): TxStatus { + if (rangoStatus === RANGO_SWAP_STATUS.SUCCESS) { + return TX_STATUS.SUCCESS; + } + if (rangoStatus === RANGO_SWAP_STATUS.RUNNING) { + return TX_STATUS.PENDING; + } + return TX_STATUS.FAIL; + } + + public static getRubicBlockchainByRangoBlockchain( + rangoBlockchainName: RangoBlockchainName + ): BlockchainName { + const blockchainName = Object.entries(rangoApiBlockchainNames).find( + ([_, value]) => value === rangoBlockchainName + )![0] as BlockchainName; + + return blockchainName; + } + + public static getTradeTypeForRubic( + rangoTradeType: RangoTradeType + ): BridgeType | OnChainTradeType { + return RANGO_TO_RUBIC_PROVIDERS[rangoTradeType] || CROSS_CHAIN_TRADE_TYPE.RANGO; + } +} diff --git a/SDK-mstr/src/features/common/providers/symbiosis/constants/symbiosis-api-common.ts b/SDK-mstr/src/features/common/providers/symbiosis/constants/symbiosis-api-common.ts new file mode 100644 index 0000000..9c7731c --- /dev/null +++ b/SDK-mstr/src/features/common/providers/symbiosis/constants/symbiosis-api-common.ts @@ -0,0 +1 @@ +export const SYMBIOSIS_API_ENDPOINT = 'https://api-v2.symbiosis.finance/crosschain'; diff --git a/SDK-mstr/src/features/common/providers/symbiosis/models/symbiosis-api-common-types.ts b/SDK-mstr/src/features/common/providers/symbiosis/models/symbiosis-api-common-types.ts new file mode 100644 index 0000000..e1091d4 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/symbiosis/models/symbiosis-api-common-types.ts @@ -0,0 +1,14 @@ +export interface SymbiosisErrorResponse { + code: number; + message: string; +} + +export interface SymbiosisTokenInfo { + address: string; + chainId: number; + decimals: number; +} + +export interface SymbiosisTokenInfoWithAmount extends SymbiosisTokenInfo { + amount: string; +} diff --git a/SDK-mstr/src/features/common/providers/symbiosis/models/symbiosis-api-parser-types.ts b/SDK-mstr/src/features/common/providers/symbiosis/models/symbiosis-api-parser-types.ts new file mode 100644 index 0000000..d6fe18e --- /dev/null +++ b/SDK-mstr/src/features/common/providers/symbiosis/models/symbiosis-api-parser-types.ts @@ -0,0 +1,5 @@ +export interface SymbiosisSwapRequestOptions { + slippage: number; + receiverAddress?: string; + fromAddress?: string; +} diff --git a/SDK-mstr/src/features/common/providers/symbiosis/models/symbiosis-api-swap-types.ts b/SDK-mstr/src/features/common/providers/symbiosis/models/symbiosis-api-swap-types.ts new file mode 100644 index 0000000..d743b98 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/symbiosis/models/symbiosis-api-swap-types.ts @@ -0,0 +1,47 @@ +import { SymbiosisTokenInfo, SymbiosisTokenInfoWithAmount } from './symbiosis-api-common-types'; + +export interface SymbiosisSwapRequestBody { + tokenAmountIn: SymbiosisTokenInfoWithAmount; + tokenOut: SymbiosisTokenInfo; + + /* 1% = 100 etc. */ + slippage: number; + + /* senderAddress */ + from?: string; + + /* receiverAddress */ + to?: string; +} + +export interface SymbiosisSwapResponse { + kind: SymbiosisSwapResponseKind; + fee: SymbiosisSwapResponseFee; + priceImpact: string; + tokenAmountOut: SymbiosisTokenInfoWithAmount; + tokenAmountOutMin: SymbiosisTokenInfoWithAmount; + amountInUsd: SymbiosisTokenInfoWithAmount; + tx: SymbiosisSwapResponseTx; + approveTo: string; + route: SymbiosisTokenInfo[]; + outTradeType: string; + type: 'tron' | 'evm'; +} + +interface SymbiosisSwapResponseTx { + chainId: number; + data: string; + to: string; + value: string; +} + +interface SymbiosisSwapResponseFee { + address: string; + chainId: number; + decimals: number; + symbol: string; + icon: string; + amount: string; +} + +type SymbiosisSwapResponseKind = 'onchain-swap' | 'crosschain-swap' | 'wrap' | 'unwrap' | 'bridge'; diff --git a/SDK-mstr/src/features/common/providers/symbiosis/services/symbiosis-api-service.ts b/SDK-mstr/src/features/common/providers/symbiosis/services/symbiosis-api-service.ts new file mode 100644 index 0000000..2d62506 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/symbiosis/services/symbiosis-api-service.ts @@ -0,0 +1,64 @@ +import { RubicSdkError } from 'src/common/errors'; +import { Cache } from 'src/common/utils/decorators'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Injector } from 'src/core/injector/injector'; +import { SymbiosisSwappingParams } from 'src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-swapping-params'; +import { SymbiosisTradeData } from 'src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-trade-data'; + +import { SYMBIOSIS_API_ENDPOINT } from '../constants/symbiosis-api-common'; +import { SymbiosisErrorResponse } from '../models/symbiosis-api-common-types'; +import { + SymbiosisSwapRequestBody, + SymbiosisSwapResponse +} from '../models/symbiosis-api-swap-types'; + +export class SymbiosisApiService { + /** + * New method for all kind of swaps + */ + @Cache({ + maxAge: 15_000 + }) + public static async getOnChainSwapTx( + body: SymbiosisSwapRequestBody + ): Promise { + const res = await Injector.httpClient.post( + `${SYMBIOSIS_API_ENDPOINT}/v1/swap`, + body + ); + + if ('code' in res && 'message' in res) { + throw new RubicSdkError(res.message); + } + + return res; + } + + /** + * @description Old method only for cross-chain swaps + * @param params Swap request body + */ + @Cache({ + maxAge: 15_000 + }) + public static async getCrossChainSwapTx( + params: SymbiosisSwappingParams + ): Promise { + const url = + params.tokenOut.chainId === blockchainId[BLOCKCHAIN_NAME.BITCOIN] + ? `${SYMBIOSIS_API_ENDPOINT}/v1/swap` + : `${SYMBIOSIS_API_ENDPOINT}/v1/swapping/exact_in?partnerId=rubic`; + + const res = await Injector.httpClient.post( + url, + params + ); + + if ('code' in res && 'message' in res) { + throw new RubicSdkError(res.message); + } + + return res; + } +} diff --git a/SDK-mstr/src/features/common/providers/symbiosis/services/symbiosis-parser.ts b/SDK-mstr/src/features/common/providers/symbiosis/services/symbiosis-parser.ts new file mode 100644 index 0000000..bf1e479 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/symbiosis/services/symbiosis-parser.ts @@ -0,0 +1,51 @@ +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Web3Private } from 'src/core/blockchain/web3-private-service/web3-private/web3-private'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; + +import { + SymbiosisTokenInfo, + SymbiosisTokenInfoWithAmount +} from '../models/symbiosis-api-common-types'; +import { SymbiosisSwapRequestOptions } from '../models/symbiosis-api-parser-types'; +import { SymbiosisSwapRequestBody } from '../models/symbiosis-api-swap-types'; + +export class SymbiosisParser { + public static getSwapRequestBody( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options: SymbiosisSwapRequestOptions + ): SymbiosisSwapRequestBody { + const walletAddress = ( + Injector.web3PrivateService.getWeb3PrivateByBlockchain( + fromToken.blockchain + ) as Web3Private + ).address as string; + + const toAddress = options.receiverAddress ?? walletAddress; + const fromAddress = options.fromAddress ?? walletAddress; + const slippage = options.slippage * 10000; + + const tokenAmountIn = { + address: fromToken.address, + decimals: fromToken.decimals, + chainId: blockchainId[fromToken.blockchain], + amount: Web3Pure.toWei(fromToken.tokenAmount, fromToken.decimals) + } as SymbiosisTokenInfoWithAmount; + + const tokenOut = { + address: toToken.address, + decimals: toToken.decimals, + chainId: blockchainId[toToken.blockchain] + } as SymbiosisTokenInfo; + + return { + from: fromAddress, + to: toAddress, + slippage, + tokenAmountIn, + tokenOut + }; + } +} diff --git a/SDK-mstr/src/features/common/providers/xy/constants/xy-api-params.ts b/SDK-mstr/src/features/common/providers/xy/constants/xy-api-params.ts new file mode 100644 index 0000000..4760d8d --- /dev/null +++ b/SDK-mstr/src/features/common/providers/xy/constants/xy-api-params.ts @@ -0,0 +1,3 @@ +export const XY_API_ENDPOINT = 'https://aggregator-api.xy.finance/v1'; +export const XY_NATIVE_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'; +export const XY_AFFILIATE_ADDRESS = '0xcb022eba97b53f74e0901618252682f0728cd192'; diff --git a/SDK-mstr/src/features/common/providers/xy/constants/xy-error-code.ts b/SDK-mstr/src/features/common/providers/xy/constants/xy-error-code.ts new file mode 100644 index 0000000..8b97a1b --- /dev/null +++ b/SDK-mstr/src/features/common/providers/xy/constants/xy-error-code.ts @@ -0,0 +1,19 @@ +export const XY_ERROR_CODE = { + 10000: '10000', + 10001: '10001', + 20001: '20001', + 20003: '20003', + 20004: '20004', + 20005: '20005', + 20006: '20006', + 20007: '20007', + 20008: '20008', + 30001: '30001', + 30002: '30002', + 30003: '30003', + 30004: '30004', + 30005: '30005', + 30006: '30006' +} as const; + +export type XyErrorCode = (typeof XY_ERROR_CODE)[keyof typeof XY_ERROR_CODE]; diff --git a/SDK-mstr/src/features/common/providers/xy/models/xy-build-tx-request.ts b/SDK-mstr/src/features/common/providers/xy/models/xy-build-tx-request.ts new file mode 100644 index 0000000..bc38567 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/xy/models/xy-build-tx-request.ts @@ -0,0 +1,18 @@ +import { XyQuoteRequest } from 'src/features/common/providers/xy/models/xy-quote-request'; + +export interface XyBuildTxRequest extends XyQuoteRequest { + /** + * Destination chain quote token receiver. + */ + receiver: string; + + /** + * Source chain bridge token address. + */ + srcBridgeTokenAddress?: string; + + /** + * Destination chain bridge token address. + */ + dstBridgeTokenAddress?: string; +} diff --git a/SDK-mstr/src/features/common/providers/xy/models/xy-build-tx-response.ts b/SDK-mstr/src/features/common/providers/xy/models/xy-build-tx-response.ts new file mode 100644 index 0000000..d13e467 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/xy/models/xy-build-tx-response.ts @@ -0,0 +1,12 @@ +import { XyQuoteErrorResponse } from 'src/features/common/providers/xy/models/xy-quote-error-response'; +import { XyRoute } from 'src/features/common/providers/xy/models/xy-quote-success-response'; + +export interface XyBuildTxResponse extends XyQuoteErrorResponse { + success: boolean; + route: XyRoute; + tx: { + to: string; + data: string; + value: string; + }; +} diff --git a/SDK-mstr/src/features/common/providers/xy/models/xy-quote-error-response.ts b/SDK-mstr/src/features/common/providers/xy/models/xy-quote-error-response.ts new file mode 100644 index 0000000..a0002e0 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/xy/models/xy-quote-error-response.ts @@ -0,0 +1,7 @@ +import { XyErrorCode } from 'src/features/common/providers/xy/constants/xy-error-code'; + +export interface XyQuoteErrorResponse { + success: boolean; + errorCode: XyErrorCode; + errorMsg: string; +} diff --git a/SDK-mstr/src/features/common/providers/xy/models/xy-quote-request.ts b/SDK-mstr/src/features/common/providers/xy/models/xy-quote-request.ts new file mode 100644 index 0000000..93d1ad3 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/xy/models/xy-quote-request.ts @@ -0,0 +1,13 @@ +export interface XyQuoteRequest { + srcChainId: number; + srcQuoteTokenAddress: string; + srcQuoteTokenAmount: string; + dstChainId: number; + dstQuoteTokenAddress: string; + slippage: number; + affiliate?: string; + commissionRate?: number; + srcSwapProvider?: string; + bridgeProvider?: string; + dstSwapProvider?: string; +} diff --git a/SDK-mstr/src/features/common/providers/xy/models/xy-quote-response.ts b/SDK-mstr/src/features/common/providers/xy/models/xy-quote-response.ts new file mode 100644 index 0000000..2d655fb --- /dev/null +++ b/SDK-mstr/src/features/common/providers/xy/models/xy-quote-response.ts @@ -0,0 +1,4 @@ +import { XyQuoteErrorResponse } from 'src/features/common/providers/xy/models/xy-quote-error-response'; +import { XyQuoteSuccessResponse } from 'src/features/common/providers/xy/models/xy-quote-success-response'; + +export interface XyQuoteResponse extends XyQuoteSuccessResponse, XyQuoteErrorResponse {} diff --git a/SDK-mstr/src/features/common/providers/xy/models/xy-quote-success-response.ts b/SDK-mstr/src/features/common/providers/xy/models/xy-quote-success-response.ts new file mode 100644 index 0000000..3acb6a2 --- /dev/null +++ b/SDK-mstr/src/features/common/providers/xy/models/xy-quote-success-response.ts @@ -0,0 +1,66 @@ +export interface XyQuoteSuccessResponse { + success: boolean; + routes: XyRoute[]; +} + +interface QuoteToken { + chainId: number; // 324 + address: string; // '0x...' + decimals: number; // 18 + symbol: string; // 'ETH' +} + +interface QuoteBridgeDescription { + provider: string; // 'yBridge' + srcChainId: number; // 324 + srcBridgeTokenAddress: string; // '0x...' + dstChainId: number; // 59144 + dstBridgeTokenAddress: string; // '0x...' + srcBridgeTokenAmount: string; // '9990000000000000' + dstBridgeTokenAmount: string; // '9190000000000000' + bridgeContractAddress: string; // '0x...' + bridgeFeeAmount: string; // '800000000000000' + bridgeFeeToken: QuoteToken; + srcBridgeToken: QuoteToken; + dstBridgeToken: QuoteToken; +} + +interface QuoteSwapDescription { + chainId: string; // '59144' + provider: string; // 'OpenOcean V3 DexAggregator' + srcTokenAddress: string; // '0x...' + dstTokenAddress: string; // '0x...' + srcTokenAmount: string; // '997500000' + dstTokenAmount: string; // '609954227563476606' + dexNames: string[]; // ['Horizon'] +} + +export interface XyQuote { + srcSwapDescription: QuoteSwapDescription; + bridgeDescription: QuoteBridgeDescription; + dstSwapDescription: QuoteSwapDescription; + dstQuoteTokenAmount: string; // '9190000000000000' +} + +export interface XyRoute extends XyQuote { + srcChainId: number; // 324 + srcQuoteTokenAddress: string; // '0x...' + srcQuoteTokenAmount: string; // '10000000000000000' + dstChainId: number; // 59144 + dstQuoteTokenAddress: string; // '0x...' + slippage: number; // 1 + minReceiveAmount: string; // '9098100000000000' + affiliateFeeAmount: string; // '10000000000000' + withholdingFeeAmount: string; // '0' + routeType: string; // 'xy_router_cross_chain' + tags: []; + contractAddress: string; // '0x...' + withholdingFeeToken: QuoteToken; + srcQuoteToken: QuoteToken; + dstQuoteToken: QuoteToken; + srcQuoteTokenUsdValue: string; // '16.3805' + dstQuoteTokenUsdValue: string; // '15.0536795' + transactionCounts: number; // 2 + estimatedGas: string; // '400000' + estimatedTransferTime: number; // 180 +} diff --git a/SDK-mstr/src/features/common/providers/xy/utils/xy-utils.ts b/SDK-mstr/src/features/common/providers/xy/utils/xy-utils.ts new file mode 100644 index 0000000..0a9d6fa --- /dev/null +++ b/SDK-mstr/src/features/common/providers/xy/utils/xy-utils.ts @@ -0,0 +1,14 @@ +import { RubicSdkError } from 'src/common/errors'; +import { SdkSwapErrorOnProviderSide } from 'src/common/errors/swap/swap-error-on-provider-side'; +import { + XY_ERROR_CODE, + XyErrorCode +} from 'src/features/common/providers/xy/constants/xy-error-code'; + +export function xyAnalyzeStatusCode(errorCode: XyErrorCode, errorMessage: string): void { + if (XY_ERROR_CODE[errorCode] && errorMessage !== '') { + throw new RubicSdkError(errorMessage); + } + + throw new SdkSwapErrorOnProviderSide(); +} diff --git a/SDK-mstr/src/features/common/status-manager/models/tx-status-data.ts b/SDK-mstr/src/features/common/status-manager/models/tx-status-data.ts new file mode 100644 index 0000000..effb7db --- /dev/null +++ b/SDK-mstr/src/features/common/status-manager/models/tx-status-data.ts @@ -0,0 +1,9 @@ +import { TxStatus } from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; + +export interface TxStatusData { + status: TxStatus; + hash: string | null; + extraInfo?: { + mesonSwapId: string; + }; +} diff --git a/SDK-mstr/src/features/common/status-manager/utils/get-bridgers-trade-status.ts b/SDK-mstr/src/features/common/status-manager/utils/get-bridgers-trade-status.ts new file mode 100644 index 0000000..7c54836 --- /dev/null +++ b/SDK-mstr/src/features/common/status-manager/utils/get-bridgers-trade-status.ts @@ -0,0 +1,75 @@ +import { TX_STATUS } from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; +import { Injector } from 'src/core/injector/injector'; +import { toBridgersBlockchain } from 'src/features/common/providers/bridgers/constants/to-bridgers-blockchain'; +import { + BridgersGetTransDataByIdRequest, + BridgersGetTransDataByIdResponse +} from 'src/features/common/providers/bridgers/models/bridgers-get-trans-data-by-id-api'; +import { + BridgersUpdateDataAndStatusRequest, + BridgersUpdateDataAndStatusResponse +} from 'src/features/common/providers/bridgers/models/bridgers-update-data-and-status-api'; +import { TxStatusData } from 'src/features/common/status-manager/models/tx-status-data'; +import { BridgersCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/constants/bridgers-cross-chain-supported-blockchain'; + +export async function getBridgersTradeStatus( + srcTxHash: string, + fromBlockchain: BridgersCrossChainSupportedBlockchain, + sourceFlag: 'rubic' | 'rubic_widget' +): Promise { + try { + const updateDataAndStatusRequest: BridgersUpdateDataAndStatusRequest = { + hash: srcTxHash, + fromTokenChain: toBridgersBlockchain[fromBlockchain], + sourceFlag + }; + const updateDataAndStatusResponse = + await Injector.httpClient.post( + 'https://sswap.swft.pro/api/exchangeRecord/updateDataAndStatus', + updateDataAndStatusRequest + ); + const orderId = updateDataAndStatusResponse.data?.orderId; + if (!orderId) { + return { + status: TX_STATUS.PENDING, + hash: null + }; + } + + const getTransDataByIdRequest: BridgersGetTransDataByIdRequest = { + orderId + }; + const getTransDataByIdResponse = + await Injector.httpClient.post( + 'https://sswap.swft.pro/api/exchangeRecord/getTransDataById', + getTransDataByIdRequest + ); + const transactionData = getTransDataByIdResponse.data; + if (!transactionData?.status) { + return { + status: TX_STATUS.PENDING, + hash: null + }; + } + + if (transactionData.status === 'receive_complete') { + return { + status: TX_STATUS.SUCCESS, + hash: transactionData.toHash + }; + } + if (transactionData.status.includes('error') || transactionData.status.includes('fail')) { + return { + status: TX_STATUS.FAIL, + hash: null + }; + } + } catch (err) { + console.debug('[ERROR_getBridgersTradeStatus]', err); + } + + return { + status: TX_STATUS.PENDING, + hash: null + }; +} diff --git a/SDK-mstr/src/features/common/status-manager/utils/get-src-tx-status.ts b/SDK-mstr/src/features/common/status-manager/utils/get-src-tx-status.ts new file mode 100644 index 0000000..b7e0af5 --- /dev/null +++ b/SDK-mstr/src/features/common/status-manager/utils/get-src-tx-status.ts @@ -0,0 +1,30 @@ +import { waitFor } from 'src/common/utils/waitFor'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { Web3PublicSupportedBlockchain } from 'src/core/blockchain/web3-public-service/models/web3-public-storage'; +import { + TX_STATUS, + TxStatus +} from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; +import { Injector } from 'src/core/injector/injector'; + +/** + * Get cross-chain trade's source transaction status via receipt. + * @returns Cross-chain transaction status. + */ +export async function getSrcTxStatus( + fromBlockchain: Web3PublicSupportedBlockchain, + srcTxHash: string +): Promise { + try { + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const status = await web3Public.getTransactionStatus(srcTxHash); + if (status === TX_STATUS.FAIL && fromBlockchain === BLOCKCHAIN_NAME.ZK_SYNC) { + const zkSyncAwarenessTime = 4000; + await waitFor(zkSyncAwarenessTime); + return web3Public.getTransactionStatus(srcTxHash); + } + return status; + } catch { + return TX_STATUS.PENDING; + } +} diff --git a/SDK-mstr/src/features/common/utils/check-address.ts b/SDK-mstr/src/features/common/utils/check-address.ts new file mode 100644 index 0000000..4cdb39a --- /dev/null +++ b/SDK-mstr/src/features/common/utils/check-address.ts @@ -0,0 +1,35 @@ +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { nonEvmChainAddressCorrectResponse } from 'src/features/common/models/non-evm-chain-address-correct-response'; +import { + CROSS_CHAIN_TRADE_TYPE, + CrossChainTradeType +} from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { + changenowApiBlockchain, + ChangenowCrossChainSupportedBlockchain +} from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +export async function isAddressCorrect( + address: string, + toBlockchain: BlockchainName, + crossChainType?: CrossChainTradeType | OnChainTradeType +): Promise { + try { + if (crossChainType === CROSS_CHAIN_TRADE_TYPE.CHANGENOW) { + const chain = + changenowApiBlockchain[toBlockchain as ChangenowCrossChainSupportedBlockchain]; + const response = await Injector.httpClient.get( + `https://api.changenow.io/v2/validate/address?currency=${chain.toLowerCase()}&address=${address}` + ); + return response.result; + } + const blockchainProvider = Web3Pure[BlockchainsInfo.getChainType(toBlockchain)]; + return blockchainProvider.isAddressCorrect(address); + } catch { + return true; + } +} diff --git a/SDK-mstr/src/features/common/utils/check-unsupported-receiver-address.ts b/SDK-mstr/src/features/common/utils/check-unsupported-receiver-address.ts new file mode 100644 index 0000000..cd981f1 --- /dev/null +++ b/SDK-mstr/src/features/common/utils/check-unsupported-receiver-address.ts @@ -0,0 +1,11 @@ +import { UnsupportedReceiverAddressError } from 'src/common/errors'; +import { compareAddresses } from 'src/common/utils/blockchain'; + +export function checkUnsupportedReceiverAddress( + receiverAddress?: string, + fromAddress?: string +): void | never { + if (receiverAddress && (!fromAddress || !compareAddresses(receiverAddress, fromAddress))) { + throw new UnsupportedReceiverAddressError(); + } +} diff --git a/SDK-mstr/src/features/common/utils/get-from-without-fee.ts b/SDK-mstr/src/features/common/utils/get-from-without-fee.ts new file mode 100644 index 0000000..75f2006 --- /dev/null +++ b/SDK-mstr/src/features/common/utils/get-from-without-fee.ts @@ -0,0 +1,24 @@ +import { PriceTokenAmount } from 'src/common/tokens'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; + +export function getFromWithoutFee( + from: PriceTokenAmount, + platformFeePercent: number | undefined +): PriceTokenAmount { + if (!platformFeePercent) { + return new PriceTokenAmount({ + ...from.asStruct, + weiAmount: from.weiAmount + }); + } + const feeAmount = Web3Pure.toWei( + from.tokenAmount.multipliedBy(platformFeePercent).dividedBy(100), + from.decimals, + 1 + ); + return new PriceTokenAmount({ + ...from.asStruct, + weiAmount: from.weiAmount.minus(feeAmount) + }); +} diff --git a/SDK-mstr/src/features/common/utils/get-price-tokens-from-input-tokens.ts b/SDK-mstr/src/features/common/utils/get-price-tokens-from-input-tokens.ts new file mode 100644 index 0000000..4fc745d --- /dev/null +++ b/SDK-mstr/src/features/common/utils/get-price-tokens-from-input-tokens.ts @@ -0,0 +1,66 @@ +import BigNumber from 'bignumber.js'; +import { PriceToken, PriceTokenAmount, Token } from 'src/common/tokens'; +import { TokenBaseStruct } from 'src/common/tokens/models/token-base-struct'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +export async function getPriceTokensFromInputTokens( + from: + | Token + | { + address: string; + blockchain: T; + } + | PriceToken, + fromAmount: string | number | BigNumber, + to: + | Token + | string + | { + address: string; + blockchain: T; + } + | PriceToken +): Promise<{ + from: PriceTokenAmount; + to: PriceToken; +}> { + let fromPriceTokenPromise: Promise>; + + if (from instanceof PriceToken) { + fromPriceTokenPromise = new Promise(resolve => resolve(from)); + } else if (from instanceof Token) { + fromPriceTokenPromise = PriceToken.createFromToken(from); + } else { + fromPriceTokenPromise = PriceToken.createToken(from); + } + + let toPriceTokenPromise: Promise>; + + if (to instanceof PriceToken) { + toPriceTokenPromise = new Promise(resolve => resolve(to)); + } else if (to instanceof Token) { + toPriceTokenPromise = PriceToken.createFromToken(to as Token); + } else if (typeof to === 'object') { + toPriceTokenPromise = PriceToken.createToken(to as TokenBaseStruct); + } else { + toPriceTokenPromise = PriceToken.createToken({ + address: to as string, + blockchain: from.blockchain + }); + } + + const [fromPriceToken, toPriceToken] = await Promise.all([ + fromPriceTokenPromise, + toPriceTokenPromise + ]); + + const fromPriceTokenAmount = new PriceTokenAmount({ + ...fromPriceToken.asStruct, + tokenAmount: new BigNumber(fromAmount) + }); + + return { + from: fromPriceTokenAmount, + to: toPriceToken + }; +} diff --git a/SDK-mstr/src/features/common/utils/token-native-address-proxy.ts b/SDK-mstr/src/features/common/utils/token-native-address-proxy.ts new file mode 100644 index 0000000..bc208e7 --- /dev/null +++ b/SDK-mstr/src/features/common/utils/token-native-address-proxy.ts @@ -0,0 +1,38 @@ +import { RubicSdkError } from 'src/common/errors'; +import { Token } from 'src/common/tokens'; + +export function createTokenNativeAddressProxy( + token: T, + wrappedNativeAddress: string, + useLowerCase = true +): T { + const wethAbleAddress = token.isNative ? wrappedNativeAddress : token.address; + return new Proxy(token, { + get: (target, key) => { + if (!(key in target)) { + return undefined; + } + if (key === 'address') { + return useLowerCase ? wethAbleAddress.toLowerCase() : wethAbleAddress; + } + return target[key as keyof T]; + } + }); +} + +export function createTokenNativeAddressProxyInPathStartAndEnd( + path: T[] | ReadonlyArray, + wrappedNativeAddress: string +): ReadonlyArray { + if (!path?.[0]) { + throw new RubicSdkError('Path cannot be empty'); + } + const token = path[path.length - 1]; + if (!token) { + throw new RubicSdkError("Path's tokens has to be defined"); + } + + return [createTokenNativeAddressProxy(path[0], wrappedNativeAddress)] + .concat(path.slice(1, path.length - 1)) + .concat(createTokenNativeAddressProxy(token, wrappedNativeAddress)); +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/constants/cross-chain-providers.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/constants/cross-chain-providers.ts new file mode 100644 index 0000000..8c73295 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/constants/cross-chain-providers.ts @@ -0,0 +1,45 @@ +import { ArbitrumRbcBridgeProvider } from 'src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/arbitrum-rbc-bridge-provider'; +import { ArchonBridgeProvider } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/archon-bridge-provider'; +import { BridgersCrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/bridgers-cross-chain-provider'; +import { CbridgeCrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-provider'; +import { ChangenowCrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/changenow-cross-chain-provider'; +import { DebridgeCrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/debridge-cross-chain-provider'; +import { LifiCrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/lifi-cross-chain-provider'; +import { PulseChainCrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/pulse-chain-cross-chain-provider'; +//import { ScrollBridgeProvider } from 'src/features/cross-chain/calculation-manager/providers/scroll-bridge/scroll-bridge-provider'; +import { SquidrouterCrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/squidrouter-provider/squidrouter-cross-chain-provider'; +import { SymbiosisCrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/symbiosis-provider/symbiosis-cross-chain-provider'; +import { XyCrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/xy-provider/xy-cross-chain-provider'; + +import { LayerZeroBridgeProvider } from '../providers/layerzero-bridge/layerzero-bridge-provider'; +import { MesonCrossChainProvider } from '../providers/meson-provider/meson-cross-chain-provider'; +import { OrbiterBridgeProvider } from '../providers/orbiter-bridge/orbiter-bridge-provider'; +import { RangoCrossChainProvider } from '../providers/rango-provider/rango-cross-chain-provider'; +// import { StargateCrossChainProvider } from '../providers/stargate-provider/stargate-cross-chain-provider'; +import { TaikoBridgeProvider } from '../providers/taiko-bridge/taiko-bridge-provider'; + +const proxyProviders = [ + SymbiosisCrossChainProvider, + // StargateCrossChainProvider, + XyCrossChainProvider, + CbridgeCrossChainProvider, + LifiCrossChainProvider, + SquidrouterCrossChainProvider, + RangoCrossChainProvider, + PulseChainCrossChainProvider, + OrbiterBridgeProvider, + ArchonBridgeProvider, + MesonCrossChainProvider +] as const; + +const nonProxyProviders = [ + DebridgeCrossChainProvider, + BridgersCrossChainProvider, + ChangenowCrossChainProvider, + ArbitrumRbcBridgeProvider, + TaikoBridgeProvider, + LayerZeroBridgeProvider + // ScrollBridgeProvider +] as const; + +export const CrossChainProviders = [...proxyProviders, ...nonProxyProviders] as const; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/constants/default-cross-chain-calculation-options.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/constants/default-cross-chain-calculation-options.ts new file mode 100644 index 0000000..f8ea7cf --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/constants/default-cross-chain-calculation-options.ts @@ -0,0 +1,16 @@ +import { RequiredCrossChainManagerCalculationOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-manager-options'; + +export const defaultCrossChainCalculationOptions: Omit< + RequiredCrossChainManagerCalculationOptions, + 'providerAddress' +> = { + fromSlippageTolerance: 0.015, + toSlippageTolerance: 0.015, + gasCalculation: 'disabled', + disabledProviders: [], + timeout: 25_000, + slippageTolerance: 0.03, + deadline: 20, + changenowFullyEnabled: false, + enableTestnets: false +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/constants/default-provider-addresses.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/constants/default-provider-addresses.ts new file mode 100644 index 0000000..41338b4 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/constants/default-provider-addresses.ts @@ -0,0 +1,4 @@ +export const defaultProviderAddresses = { + crossChain: '0x0000000000000000000000000000000000000000', + onChain: '0x9dd6562f51510696e66C63108Ec0f11c3c4f6571' +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/cross-chain-manager.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/cross-chain-manager.ts new file mode 100644 index 0000000..abe80a5 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/cross-chain-manager.ts @@ -0,0 +1,347 @@ +import BigNumber from 'bignumber.js'; +import { from, map, merge, Observable, startWith, switchMap } from 'rxjs'; +import { fromPromise } from 'rxjs/internal/observable/innerFrom'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, Token } from 'src/common/tokens'; +import { notNull } from 'src/common/utils/object'; +import { combineOptions } from 'src/common/utils/options'; +import pTimeout from 'src/common/utils/p-timeout'; +import { Mutable } from 'src/common/utils/types'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { ProviderAddress } from 'src/core/sdk/models/provider-address'; +import { + ProxySupportedBlockchain, + proxySupportedBlockchains +} from 'src/features/common/constants/proxy-supported-blockchain'; +import { getPriceTokensFromInputTokens } from 'src/features/common/utils/get-price-tokens-from-input-tokens'; +import { CrossChainProviders } from 'src/features/cross-chain/calculation-manager/constants/cross-chain-providers'; +import { defaultCrossChainCalculationOptions } from 'src/features/cross-chain/calculation-manager/constants/default-cross-chain-calculation-options'; +import { defaultProviderAddresses } from 'src/features/cross-chain/calculation-manager/constants/default-provider-addresses'; +import { + CrossChainManagerCalculationOptions, + RequiredCrossChainManagerCalculationOptions +} from 'src/features/cross-chain/calculation-manager/models/cross-chain-manager-options'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CrossChainReactivelyCalculatedTradeData } from 'src/features/cross-chain/calculation-manager/models/cross-chain-reactively-calculated-trade-data'; +import { + CROSS_CHAIN_TRADE_TYPE, + CrossChainTradeType +} from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CrossChainTypedTradeProviders } from 'src/features/cross-chain/calculation-manager/models/cross-chain-typed-trade-provider'; +import { WrappedCrossChainTradeOrNull } from 'src/features/cross-chain/calculation-manager/models/wrapped-cross-chain-trade-or-null'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { WrappedCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/models/wrapped-cross-chain-trade'; +import { compareCrossChainTrades } from 'src/features/cross-chain/calculation-manager/utils/compare-cross-chain-trades'; + +/** + * Contains method to calculate best cross-chain trade. + */ +export class CrossChainManager { + public readonly tradeProviders: CrossChainTypedTradeProviders = CrossChainProviders.reduce( + (acc, ProviderClass) => { + const provider = new ProviderClass(); + acc[provider.type] = provider; + return acc; + }, + {} as Mutable + ); + + constructor(private readonly providerAddress: ProviderAddress) {} + + /** + * Calculates cross-chain trades and sorts them by exchange courses. + * Wrapped trade object may contain error, but sometimes course can be + * calculated even with thrown error (e.g. min/max amount error). + * + * @example + * ```ts + * const fromBlockchain = BLOCKCHAIN_NAME.ETHEREUM; + * // ETH + * const fromTokenAddress = '0x0000000000000000000000000000000000000000'; + * const fromAmount = 1; + * const toBlockchain = BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN; + * // BUSD + * const toTokenAddress = '0xe9e7cea3dedca5984780bafc599bd69add087d56'; + * + * const wrappedTrades = await sdk.crossChainManager.calculateTrade( + * { blockchain: fromBlockchain, address: fromTokenAddress }, + * fromAmount, + * { blockchain: toBlockchain, address: toTokenAddress } + * ); + * const bestTrade = wrappedTrades[0]; + * + * wrappedTrades.forEach(wrappedTrade => { + * if (wrappedTrade.trade) { + * console.log(wrappedTrade.tradeType, `to amount: ${wrappedTrade.trade.to.tokenAmount.toFormat(3)}`)); + * } + * if (wrappedTrade.error) { + * console.error(wrappedTrade.tradeType, 'error: wrappedTrade.error'); + * } + * }); + * + * ``` + * + * @param fromToken Token to sell. + * @param fromAmount Amount to sell. + * @param toToken Token to get. + * @param options Additional options. + * @returns Array of sorted wrapped cross-chain trades with possible errors. + */ + public async calculateTrade( + fromToken: + | Token + | { + address: string; + blockchain: BlockchainName; + } + | PriceToken, + fromAmount: string | number | BigNumber, + toToken: + | Token + | { + address: string; + blockchain: BlockchainName; + } + | PriceToken, + options?: CrossChainManagerCalculationOptions + ): Promise { + if (toToken instanceof Token && fromToken.blockchain === toToken.blockchain) { + throw new RubicSdkError('Blockchains of `from` and `to` tokens must be different.'); + } + + const { from, to } = await getPriceTokensFromInputTokens(fromToken, fromAmount, toToken); + const { disabledProviders, ...providerOptions } = this.getFullOptions( + from.blockchain, + options + ); + + const providers = this.getNotDisabledProviders( + from.blockchain, + to.blockchain, + disabledProviders + ); + + const calculationPromises = providers.map(provider => + this.getProviderCalculationPromise(provider, from, to, providerOptions) + ); + const wrappedTrades = (await Promise.all(calculationPromises)).filter(notNull); + if (!wrappedTrades?.length) { + throw new RubicSdkError('No success providers calculation for the trade'); + } + + return wrappedTrades.sort((nextTrade, prevTrade) => + compareCrossChainTrades(nextTrade, prevTrade) + ); + } + + /** + * Calculates cross-chain trades reactively in sequence. + * Contains wrapped trade object which may contain error, but sometimes course can be + * calculated even with thrown error (e.g. min/max amount error). + * + * @example + * ```ts + * const fromBlockchain = BLOCKCHAIN_NAME.ETHEREUM; + * // ETH + * const fromTokenAddress = '0x0000000000000000000000000000000000000000'; + * const fromAmount = 1; + * const toBlockchain = BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN; + * // BUSD + * const toTokenAddress = '0xe9e7cea3dedca5984780bafc599bd69add087d56'; + * + * sdk.crossChain.calculateTrade( + * { blockchain: fromBlockchain, address: fromTokenAddress }, + * fromAmount, + * { blockchain: toBlockchain, address: toTokenAddress } + * ).subscribe(tradeData => { + * console.log(tradeData.total) // 3 + * console.log(tradeData.calculated) // 0 or 1 ... or tradeData.total + * + * const wrappedTrade = tradeData.wrappedTrade; + * if (wrappedTrade) { + * console.log(wrappedTrade.tradeType, `to amount: ${wrappedTrade.trade.to.tokenAmount.toFormat(3)}`)); + * if (wrappedTrade.error) { + * console.error(wrappedTrade.tradeType, 'error: wrappedTrade.error'); + * } + * } + * }); + * + * ``` + * + * @param fromToken Token to sell. + * @param fromAmount Amount to sell. + * @param toToken Token to get. + * @param options Additional options. + * @returns Observable of cross-chain providers calculation data with best trade and possible errors. + */ + public calculateTradesReactively( + fromToken: + | Token + | { + address: string; + blockchain: BlockchainName; + } + | PriceToken, + fromAmount: string | number | BigNumber, + toToken: + | Token + | { + address: string; + blockchain: BlockchainName; + } + | PriceToken, + options?: CrossChainManagerCalculationOptions + ): Observable { + if (toToken instanceof Token && fromToken.blockchain === toToken.blockchain) { + throw new RubicSdkError('Blockchains of from and to tokens must be different.'); + } + + return from(getPriceTokensFromInputTokens(fromToken, fromAmount, toToken)).pipe( + switchMap(({ from, to }) => { + const { disabledProviders, ...providerOptions } = this.getFullOptions( + from.blockchain, + options + ); + + const providers = this.getNotDisabledProviders( + from.blockchain, + to.blockchain, + disabledProviders + ); + const totalTrades = providers.length; + + return merge( + ...providers.map(provider => + fromPromise( + this.getProviderCalculationPromise(provider, from, to, providerOptions) + ) + ) + ).pipe( + map((wrappedTrade, index) => { + return { + total: totalTrades, + calculated: index + 1, + wrappedTrade + }; + }), + startWith({ + total: totalTrades, + calculated: 0, + wrappedTrade: null + }) + ); + }) + ); + } + + private getFullOptions( + fromBlockchain: BlockchainName, + options?: CrossChainManagerCalculationOptions + ): RequiredCrossChainManagerCalculationOptions { + const providerAddress = options?.providerAddress + ? options.providerAddress + : this.getProviderAddress(fromBlockchain); + const useProxy = this.getProxyConfig(fromBlockchain, options); + + return combineOptions( + { ...options, useProxy }, + { + ...defaultCrossChainCalculationOptions, + providerAddress, + useProxy + } + ); + } + + private getNotDisabledProviders( + fromBlockchain: BlockchainName, + toBlockchain: BlockchainName, + disabledProviders: CrossChainTradeType[] + ): CrossChainProvider[] { + const providers = ( + Object.entries(this.tradeProviders) as [CrossChainTradeType, CrossChainProvider][] + ) + .filter(([type, provider]) => { + if (disabledProviders.includes(type)) { + return false; + } + + return provider.areSupportedBlockchains(fromBlockchain, toBlockchain); + }) + .map(([_type, provider]) => provider); + if (!providers.length) { + throw new RubicSdkError(`There are no providers for trade`); + } + + return providers; + } + + private async getProviderCalculationPromise( + provider: CrossChainProvider, + from: PriceTokenAmount, + to: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + try { + const wrappedTrade = await pTimeout( + provider.calculate(from, to, options), + options.timeout + ); + if (!wrappedTrade) { + return null; + } + + return { + ...wrappedTrade, + tradeType: provider.type + }; + } catch (err: unknown) { + console.debug( + `[RUBIC_SDK] Trade calculation error occurred for ${provider.type} trade provider.`, + err + ); + return { + trade: null, + tradeType: provider.type, + error: CrossChainProvider.parseError(err) + }; + } + } + + private getProviderAddress(fromBlockchain: BlockchainName): string { + let chainType: keyof ProviderAddress | undefined; + try { + chainType = BlockchainsInfo.getChainType(fromBlockchain) as keyof ProviderAddress; + } catch {} + + let providerAddress = defaultProviderAddresses.crossChain; + if ( + chainType && + this.providerAddress?.[chainType]?.crossChain && + this.providerAddress[chainType]!.crossChain !== undefined + ) { + providerAddress = this.providerAddress[chainType]!.crossChain!; + } + return providerAddress; + } + + private getProxyConfig( + fromBlockchain: BlockchainName, + options: CrossChainManagerCalculationOptions | undefined + ) { + const isBlockchainSupportedByProxy = proxySupportedBlockchains.includes( + fromBlockchain as ProxySupportedBlockchain + ); + return Object.fromEntries( + Object.values(CROSS_CHAIN_TRADE_TYPE).map(key => { + return [ + key, + isBlockchainSupportedByProxy + ? options?.useProxy?.[key as CrossChainTradeType] ?? true + : false + ]; + }) + ) as Record; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-manager-options.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-manager-options.ts new file mode 100644 index 0000000..34b27d9 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-manager-options.ts @@ -0,0 +1,25 @@ +import { + CrossChainOptions, + RequiredCrossChainOptions +} from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CrossChainTradeType } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { LifiSubProvider } from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-bridge-types'; +import { MarkRequired } from 'ts-essentials'; + +export type CrossChainManagerCalculationOptions = CrossChainOptions & { + /** + * An array of disabled cross-chain providers. + */ + readonly disabledProviders?: CrossChainTradeType[]; + + /** + * @deprecated Will be renamed to `lifiDisabledProviders` in the next major update + */ + readonly lifiDisabledBridgeTypes?: LifiSubProvider[]; +}; + +export type RequiredCrossChainManagerCalculationOptions = MarkRequired< + CrossChainManagerCalculationOptions, + 'disabledProviders' +> & + RequiredCrossChainOptions; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-options.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-options.ts new file mode 100644 index 0000000..a58e370 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-options.ts @@ -0,0 +1,90 @@ +import { RangoTradeType } from 'src/features/common/providers/rango/models/rango-api-trade-types'; +import { CrossChainTradeType } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { LifiSubProvider } from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-bridge-types'; +import { MarkRequired } from 'ts-essentials'; + +export interface CrossChainOptions { + /** + * Slippage in source network (for Celer and Rubic). + * Takes value from 0 to 1. + */ + fromSlippageTolerance?: number; + + /** + * Slippage in target network (for Celer and Rubic). + * Takes value from 0 to 1. + */ + toSlippageTolerance?: number; + + /** + * Enables or disables gas fee calculation. + */ + gasCalculation?: 'enabled' | 'disabled'; + + /** + * @internal + * Integrator address. + */ + providerAddress?: string; + + /** + * Deadline for transaction (for Symbiosis). + */ + deadline?: number; + + /** + * Overall slippage (for Symbiosis). + * Takes value from 0 to 1. + */ + slippageTolerance?: number; + + /** + * Address to send transaction, otherwise connected wallet is used (necessary for Symbiosis). + */ + fromAddress?: string; + + /** + * Address to send transaction, otherwise connected wallet is used (necessary for Symbiosis). + */ + receiverAddress?: string; + + /** + * Timeout for each cross-chain provider. Calculation for provider is cancelled, after timeout is passed. + */ + timeout?: number; + + /** + * @deprecated Will be renamed to `lifiDisabledProviders` in the next major update + */ + lifiDisabledBridgeTypes?: LifiSubProvider[]; + + /** + * Providers disabled in platform config for rango + */ + rangoDisabledProviders?: RangoTradeType[]; + + /** + * True, if changenow must be calculated for non-evm source blockchains. + */ + changenowFullyEnabled?: boolean; + + useProxy?: Record; + + /** + * True if test networks are enabled. + */ + enableTestnets?: boolean; +} + +export type RequiredCrossChainOptions = MarkRequired< + CrossChainOptions, + | 'fromSlippageTolerance' + | 'toSlippageTolerance' + | 'slippageTolerance' + | 'gasCalculation' + | 'deadline' + | 'providerAddress' + | 'timeout' + | 'changenowFullyEnabled' + | 'enableTestnets' +>; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-reactively-calculated-trade-data.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-reactively-calculated-trade-data.ts new file mode 100644 index 0000000..ef78b7e --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-reactively-calculated-trade-data.ts @@ -0,0 +1,21 @@ +import { WrappedCrossChainTradeOrNull } from 'src/features/cross-chain/calculation-manager/models/wrapped-cross-chain-trade-or-null'; + +/** + * Cross-chain providers data. + */ +export interface CrossChainReactivelyCalculatedTradeData { + /** + * Total amount of providers to calculate. + */ + total: number; + + /** + * Calculated amount of providers at current moment. + */ + calculated: number; + + /** + * Last calculated trade. + */ + wrappedTrade: WrappedCrossChainTradeOrNull; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-trade-type.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-trade-type.ts new file mode 100644 index 0000000..edadc2e --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-trade-type.ts @@ -0,0 +1,24 @@ +export const CROSS_CHAIN_TRADE_TYPE = { + SYMBIOSIS: 'symbiosis', + LIFI: 'lifi', + DEBRIDGE: 'dln', + BRIDGERS: 'bridgers', + MULTICHAIN: 'multichain', + XY: 'xy', + CELER_BRIDGE: 'celer_bridge', + CHANGENOW: 'changenow', + STARGATE: 'stargate', + ARBITRUM: 'arbitrum', + SQUIDROUTER: 'squidrouter', + SCROLL_BRIDGE: 'scroll_bridge', + TAIKO_BRIDGE: 'taiko_bridge', + RANGO: 'rango', + PULSE_CHAIN_BRIDGE: 'pulsechain_bridge', + ORBITER_BRIDGE: 'orbiter_bridge', + LAYERZERO: 'layerzero', + ARCHON_BRIDGE: 'archon_bridge', + MESON: 'meson' +} as const; + +export type CrossChainTradeType = + (typeof CROSS_CHAIN_TRADE_TYPE)[keyof typeof CROSS_CHAIN_TRADE_TYPE]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-typed-trade-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-typed-trade-provider.ts new file mode 100644 index 0000000..3a4ab08 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/models/cross-chain-typed-trade-provider.ts @@ -0,0 +1,6 @@ +import { CrossChainTradeType } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; + +export type CrossChainTypedTradeProviders = Readonly< + Record +>; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/models/wrapped-cross-chain-trade-or-null.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/models/wrapped-cross-chain-trade-or-null.ts new file mode 100644 index 0000000..22c0e5f --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/models/wrapped-cross-chain-trade-or-null.ts @@ -0,0 +1,3 @@ +import { WrappedCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/models/wrapped-cross-chain-trade'; + +export type WrappedCrossChainTradeOrNull = WrappedCrossChainTrade | null; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/arbitrum-rbc-bridge-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/arbitrum-rbc-bridge-provider.ts new file mode 100644 index 0000000..de2fbfa --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/arbitrum-rbc-bridge-provider.ts @@ -0,0 +1,117 @@ +import { getL2Network } from '@arbitrum/sdk'; +import { NotSupportedTokensError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { ArbitrumRbcBridgeTrade } from 'src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/arbitrum-rbc-bridge-trade'; +import { + ArbitrumRbcBridgeSupportedBlockchain, + arbitrumRbcBridgeSupportedBlockchains +} from 'src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/arbitrum-rbc-bridge-supported-blockchain'; +import { CbridgeCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-supported-blockchains'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; + +export class ArbitrumRbcBridgeProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.ARBITRUM; + + private readonly l1TokenAddress = '0x3330BFb7332cA23cd071631837dC289B09C33333'; + + private readonly l2TokenAddress = '0x10aaed289a7b1b0155bf4b86c862f297e84465e0'; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is ArbitrumRbcBridgeSupportedBlockchain { + return arbitrumRbcBridgeSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public async calculate( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise> { + const fromBlockchain = fromToken.blockchain as CbridgeCrossChainSupportedBlockchain; + const toBlockchain = toToken.blockchain as CbridgeCrossChainSupportedBlockchain; + + if (!this.areSupportedBlockchains(fromBlockchain, toBlockchain)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + if ( + !( + (compareAddresses(fromToken.address, this.l1TokenAddress) && + compareAddresses(toToken.address, this.l2TokenAddress)) || + (compareAddresses(fromToken.address, this.l2TokenAddress) && + compareAddresses(toToken.address, this.l1TokenAddress)) + ) + ) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + try { + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: fromToken.tokenAmount + }); + + const l2network = await getL2Network(42161); + const gasData = + options.gasCalculation === 'enabled' + ? await ArbitrumRbcBridgeTrade.getGasData(fromToken, to, l2network) + : null; + + return { + trade: new ArbitrumRbcBridgeTrade( + { + from: fromToken, + to, + gasData, + l2network + }, + options.providerAddress, + await this.getRoutePath(fromToken, to) + ), + tradeType: this.type + }; + } catch (err) { + const rubicSdkError = CrossChainProvider.parseError(err); + + return { + trade: null, + error: rubicSdkError, + tradeType: this.type + }; + } + } + + protected async getFeeInfo( + _fromBlockchain: CbridgeCrossChainSupportedBlockchain, + _providerAddress: string, + _percentFeeToken: PriceTokenAmount, + _useProxy: boolean + ): Promise { + return {}; + } + + protected async getRoutePath( + fromToken: PriceTokenAmount, + toToken: PriceTokenAmount + ): Promise { + return [{ type: 'cross-chain', provider: this.type, path: [fromToken, toToken] }]; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/arbitrum-rbc-bridge-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/arbitrum-rbc-bridge-trade.ts new file mode 100644 index 0000000..fda7c7d --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/arbitrum-rbc-bridge-trade.ts @@ -0,0 +1,307 @@ +import { + Erc20Bridger, + getL2Network, + L1TransactionReceipt, + L2Network, + L2ToL1MessageReader, + L2TransactionReceipt +} from '@arbitrum/sdk'; +import { JsonRpcProvider } from '@ethersproject/providers'; +import BigNumber from 'bignumber.js'; +import { BigNumber as EtherBigNumber } from 'ethers'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Injector } from 'src/core/injector/injector'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { arbitrumRbcBridgeContractAddress } from 'src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/arbitrum-rbc-bridge-contract-address'; +import { ArbitrumRbcBridgeSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/arbitrum-rbc-bridge-supported-blockchain'; +import { l1Erc20GatewayAbi } from 'src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/l1-erc20-gateway-abi'; +import { l2Erc20GatewayAbi } from 'src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/l2-erc20-gateway-abi'; +import { outboxAbi } from 'src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/outbox-abi'; +import { retryableFactoryAbi } from 'src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/retryable-factory-abi'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { MethodDecoder } from 'src/features/cross-chain/calculation-manager/utils/decode-method'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; +import { TransactionReceipt } from 'web3-eth'; + +export class ArbitrumRbcBridgeTrade extends EvmCrossChainTrade { + /** @internal */ + public static async getGasData( + from: PriceTokenAmount, + to: PriceTokenAmount, + l2network: L2Network + ): Promise { + try { + const trade = new ArbitrumRbcBridgeTrade( + { + from, + to, + gasData: null, + l2network + }, + EvmWeb3Pure.EMPTY_ADDRESS, + [] + ); + + return getCrossChainGasData(trade); + } catch (_err) { + return null; + } + } + + public readonly onChainSubtype = { from: undefined, to: undefined }; + + public readonly type = CROSS_CHAIN_TRADE_TYPE.ARBITRUM; + + public readonly isAggregator = false; + + public readonly bridgeType = BRIDGE_TYPE.ARBITRUM; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly gasData: GasData | null; + + private get fromBlockchain(): ArbitrumRbcBridgeSupportedBlockchain { + return this.from.blockchain as ArbitrumRbcBridgeSupportedBlockchain; + } + + protected get fromContractAddress(): string { + return arbitrumRbcBridgeContractAddress[this.fromBlockchain].providerGateway; + } + + public readonly feeInfo: FeeInfo = {}; + + public readonly onChainTrade = null; + + protected get methodName(): string { + return this.onChainTrade + ? 'swapAndStartBridgeTokensViaGenericCrossChain' + : 'startBridgeTokensViaGenericCrossChain'; + } + + private readonly bridge: Erc20Bridger; + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + gasData: GasData | null; + l2network: L2Network; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.gasData = crossChainTrade.gasData; + this.toTokenAmountMin = crossChainTrade.to.tokenAmount; + this.bridge = new Erc20Bridger(crossChainTrade.l2network); + } + + public async getContractParams(_options: GetContractParamsOptions): Promise { + throw new Error('Method is not supported'); + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + const rpcProviders = Injector.web3PublicService.rpcProvider; + const l1Provider = new JsonRpcProvider(rpcProviders[BLOCKCHAIN_NAME.ETHEREUM]!.rpcList[0]!); + const l2Provider = new JsonRpcProvider(rpcProviders[BLOCKCHAIN_NAME.ARBITRUM]!.rpcList[0]!); + let contractParams: ContractParams | null = null; + + if (this.fromBlockchain === BLOCKCHAIN_NAME.ETHEREUM) { + const depositRequest = await this.bridge.getDepositRequest({ + from: this.walletAddress, + amount: EtherBigNumber.from(this.from.stringWeiAmount), + erc20L1Address: this.from.address, + l1Provider, + l2Provider, + destinationAddress: receiverAddress || this.walletAddress + }); + + const { to, data, value } = depositRequest.txRequest; + const methodArguments = MethodDecoder.decodeMethod( + l1Erc20GatewayAbi.find(method => method.name === 'outboundTransfer')!, + data as string + )!.params.map(param => param.value); + + contractParams = { + contractAddress: to, + contractAbi: l1Erc20GatewayAbi, + methodName: 'outboundTransfer', + methodArguments, + value: value.toString() + }; + } else { + const withdrawRequest = await this.bridge.getWithdrawalRequest({ + from: this.walletAddress, + amount: EtherBigNumber.from(this.from.stringWeiAmount), + destinationAddress: receiverAddress || this.walletAddress, + erc20l1Address: this.to.address + }); + const { to, data, value } = withdrawRequest.txRequest; + + const decoded = MethodDecoder.decodeMethod( + l2Erc20GatewayAbi.find(method => method.name === 'outboundTransfer')!, + data as string + ); + const methodArguments = decoded.params.map(param => param.value || '0x'); + + contractParams = { + contractAddress: to, + contractAbi: l2Erc20GatewayAbi, + methodName: 'outboundTransfer', + methodArguments, + value: value.toString() + }; + } + + const config = EvmWeb3Pure.encodeMethodCall( + contractParams.contractAddress, + contractParams.contractAbi, + contractParams.methodName, + contractParams.methodArguments, + contractParams.value + ); + + return { config, amount: this.to.stringWeiAmount }; + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + return fromUsd.dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: null, + slippage: 0, + routePath: this.routePath + }; + } + + public static async claimTargetTokens( + sourceTransaction: string, + options: SwapTransactionOptions + ): Promise { + const web3Private = Injector.web3PrivateService.getWeb3PrivateByBlockchain( + BLOCKCHAIN_NAME.ETHEREUM + ); + await web3Private.checkBlockchainCorrect(BLOCKCHAIN_NAME.ETHEREUM); + + const rpcProviders = Injector.web3PublicService.rpcProvider; + const l1Provider = new JsonRpcProvider( + rpcProviders[BLOCKCHAIN_NAME.ETHEREUM]!.rpcList[0]!, + 1 + ); + const l2Provider = new JsonRpcProvider( + rpcProviders[BLOCKCHAIN_NAME.ARBITRUM]!.rpcList[0]!, + 42161 + ); + const targetReceipt = await l2Provider.getTransactionReceipt(sourceTransaction); + const l2TxReceipt = new L2TransactionReceipt(targetReceipt); + const [event] = l2TxReceipt.getL2ToL1Events(); + if (!event) { + throw new RubicSdkError('Transaction is not ready'); + } + const messageReader = new L2ToL1MessageReader(l1Provider, event); + + const proof = await messageReader.getOutboxProof(l2Provider); + const l2network = await getL2Network(42161); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + }; + + return web3Private.tryExecuteContractMethod( + l2network.ethBridge.outbox, + outboxAbi, + 'executeTransaction', + [ + proof, + (event as unknown as { position: EtherBigNumber }).position.toString(), + event.caller, + event.destination, + event.arbBlockNum.toString(), + event.ethBlockNum.toString(), + event.timestamp.toString(), + event.callvalue.toString(), + event.data + ], + { + onTransactionHash, + gas: gasLimit, + gasPriceOptions + } + ); + } + + public static async redeemTokens( + sourceTransactionHash: string, + options: SwapTransactionOptions + ): Promise { + const rpcProviders = Injector.web3PublicService.rpcProvider; + const l1Provider = new JsonRpcProvider( + rpcProviders[BLOCKCHAIN_NAME.ETHEREUM]!.rpcList[0]!, + 1 + ); + const l2Provider = new JsonRpcProvider( + rpcProviders[BLOCKCHAIN_NAME.ARBITRUM]!.rpcList[0]!, + 42161 + ); + + const receipt = await l1Provider.getTransactionReceipt(sourceTransactionHash); + const messages = await new L1TransactionReceipt(receipt).getL1ToL2Messages(l2Provider); + const creationIdMessage = messages.find(el => el.retryableCreationId); + if (!creationIdMessage) { + throw new RubicSdkError('Can not find creation id message.'); + } + const { retryableCreationId } = creationIdMessage; + + const web3Private = Injector.web3PrivateService.getWeb3PrivateByBlockchain( + BLOCKCHAIN_NAME.ARBITRUM + ); + await web3Private.checkBlockchainCorrect(BLOCKCHAIN_NAME.ARBITRUM); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + }; + + return web3Private.tryExecuteContractMethod( + '0x000000000000000000000000000000000000006E', + retryableFactoryAbi, + 'redeem', + [retryableCreationId], + { + onTransactionHash, + gas: gasLimit, + gasPriceOptions + } + ); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/arbitrum-rbc-bridge-contract-address.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/arbitrum-rbc-bridge-contract-address.ts new file mode 100644 index 0000000..a067d71 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/arbitrum-rbc-bridge-contract-address.ts @@ -0,0 +1,19 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { ArbitrumRbcBridgeSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/arbitrum-rbc-bridge-supported-blockchain'; +import { UniversalContract } from 'src/features/cross-chain/calculation-manager/providers/common/models/universal-contract'; + +export const arbitrumRbcBridgeContractAddress: Record< + ArbitrumRbcBridgeSupportedBlockchain, + UniversalContract +> = { + [BLOCKCHAIN_NAME.ARBITRUM]: { + providerGateway: '0x09e9222E96E7B4AE2a407B98d48e330053351EEe', + providerRouter: '0x5288c571Fd7aD117beA99bF60FE0846C4E84F933', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + }, + [BLOCKCHAIN_NAME.ETHEREUM]: { + providerGateway: '0xa3A7B6F88361F48403514059F1F16C8E78d60EeC', + providerRouter: '0x72Ce9c846789fdB6fC1f34aC4AD25Dd9ef7031ef', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + } +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/arbitrum-rbc-bridge-supported-blockchain.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/arbitrum-rbc-bridge-supported-blockchain.ts new file mode 100644 index 0000000..b3b0555 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/arbitrum-rbc-bridge-supported-blockchain.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const arbitrumRbcBridgeSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.ARBITRUM +] as const; + +export type ArbitrumRbcBridgeSupportedBlockchain = + (typeof arbitrumRbcBridgeSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/l1-erc20-gateway-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/l1-erc20-gateway-abi.ts new file mode 100644 index 0000000..46a134c --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/l1-erc20-gateway-abi.ts @@ -0,0 +1,33 @@ +import { AbiItem } from 'web3-utils'; + +export const l1Erc20GatewayAbi: AbiItem[] = [ + { + inputs: [ + { internalType: 'address', name: '_l1Token', type: 'address' }, + { internalType: 'address', name: '_to', type: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + { internalType: 'uint256', name: '_maxGas', type: 'uint256' }, + { internalType: 'uint256', name: '_gasPriceBid', type: 'uint256' }, + { internalType: 'bytes', name: '_data', type: 'bytes' } + ], + name: 'outboundTransfer', + outputs: [{ internalType: 'bytes', name: 'res', type: 'bytes' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: '_l1Token', type: 'address' }, + { internalType: 'address', name: '_refundTo', type: 'address' }, + { internalType: 'address', name: '_to', type: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + { internalType: 'uint256', name: '_maxGas', type: 'uint256' }, + { internalType: 'uint256', name: '_gasPriceBid', type: 'uint256' }, + { internalType: 'bytes', name: '_data', type: 'bytes' } + ], + name: 'outboundTransferCustomRefund', + outputs: [{ internalType: 'bytes', name: 'res', type: 'bytes' }], + stateMutability: 'payable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/l2-erc20-gateway-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/l2-erc20-gateway-abi.ts new file mode 100644 index 0000000..4aef5a3 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/l2-erc20-gateway-abi.ts @@ -0,0 +1,30 @@ +import { AbiItem } from 'web3-utils'; + +export const l2Erc20GatewayAbi: AbiItem[] = [ + { + inputs: [ + { internalType: 'address', name: '_l1Token', type: 'address' }, + { internalType: 'address', name: '_to', type: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + { internalType: 'bytes', name: '_data', type: 'bytes' } + ], + name: 'outboundTransfer', + outputs: [{ internalType: 'bytes', name: '', type: 'bytes' }], + stateMutability: 'payable', + type: 'function' + } + // { + // inputs: [ + // { internalType: 'address', name: '_l1Token', type: 'address' }, + // { internalType: 'address', name: '_to', type: 'address' }, + // { internalType: 'uint256', name: '_amount', type: 'uint256' }, + // { internalType: 'uint256', name: '', type: 'uint256' }, + // { internalType: 'uint256', name: '', type: 'uint256' }, + // { internalType: 'bytes', name: '_data', type: 'bytes' } + // ], + // name: 'outboundTransfer', + // outputs: [{ internalType: 'bytes', name: 'res', type: 'bytes' }], + // stateMutability: 'payable', + // type: 'function' + // } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/outbox-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/outbox-abi.ts new file mode 100644 index 0000000..3733706 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/outbox-abi.ts @@ -0,0 +1,21 @@ +import { AbiItem } from 'web3-utils'; + +export const outboxAbi: AbiItem[] = [ + { + inputs: [ + { internalType: 'bytes32[]', name: 'proof', type: 'bytes32[]' }, + { internalType: 'uint256', name: 'index', type: 'uint256' }, + { internalType: 'address', name: 'l2Sender', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'l2Block', type: 'uint256' }, + { internalType: 'uint256', name: 'l1Block', type: 'uint256' }, + { internalType: 'uint256', name: 'l2Timestamp', type: 'uint256' }, + { internalType: 'uint256', name: 'value', type: 'uint256' }, + { internalType: 'bytes', name: 'data', type: 'bytes' } + ], + name: 'executeTransaction', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/retryable-factory-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/retryable-factory-abi.ts new file mode 100644 index 0000000..16d0b1f --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/retryable-factory-abi.ts @@ -0,0 +1,11 @@ +import { AbiItem } from 'web3-utils'; + +export const retryableFactoryAbi: AbiItem[] = [ + { + inputs: [{ internalType: 'bytes32', name: 'ticketId', type: 'bytes32' }], + name: 'redeem', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'nonpayable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/archon-bridge-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/archon-bridge-provider.ts new file mode 100644 index 0000000..1dba67c --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/archon-bridge-provider.ts @@ -0,0 +1,185 @@ +import { NotSupportedTokensError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { ArchonBridgeTrade } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/archon-bridge-trade'; +import { ArchonContractService } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/archon-contract-service'; +import { + ArchonBridgeSupportedBlockchain, + archonBridgeSupportedBlockchains +} from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-supported-blockchain'; +import { + eonAvalancheTokensMapping, + eonEthTokensMapping, + supportedEonTokens +} from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/supported-tokens'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; + +export class ArchonBridgeProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.ARCHON_BRIDGE; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is ArchonBridgeSupportedBlockchain { + return archonBridgeSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public areSupportedBlockchains( + fromBlockchain: BlockchainName, + toBlockchain: BlockchainName + ): boolean { + return ( + this.isSupportedBlockchain(fromBlockchain) && + this.isSupportedBlockchain(toBlockchain) && + (fromBlockchain === BLOCKCHAIN_NAME.HORIZEN_EON || + toBlockchain === BLOCKCHAIN_NAME.HORIZEN_EON) + ); + } + + public async calculate( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + const fromBlockchain = fromToken.blockchain as ArchonBridgeSupportedBlockchain; + const toBlockchain = toToken.blockchain as ArchonBridgeSupportedBlockchain; + + if (!this.areSupportedBlockchains(fromBlockchain, toBlockchain)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + if (this.checkUnsupportedTokens(fromToken, toToken)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + try { + const feeInfo = await this.getFeeInfo( + fromBlockchain, + options.providerAddress, + fromToken, + options?.useProxy?.[this.type] ?? true + ); + const fromWithoutFee = getFromWithoutFee( + fromToken, + feeInfo.rubicProxy?.platformFee?.percent + ); + + const nativeToken = await PriceToken.createFromToken( + nativeTokensList[fromToken.blockchain] + ); + const cryptoFee = await ArchonContractService.fetchLayerZeroFee(fromToken, toToken); + feeInfo.provider = { + cryptoFee: { + amount: Web3Pure.fromWei(cryptoFee, nativeToken.decimals), + token: nativeToken + } + }; + + const providerFeePercent = await ArchonContractService.fetchDepositFeeBps( + fromToken, + toToken + ); + const toAmount = fromWithoutFee.tokenAmount.multipliedBy(1 - providerFeePercent); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: toAmount + }); + + const gasData = null; + + return { + trade: new ArchonBridgeTrade( + { + from: fromToken, + to, + gasData, + feeInfo + }, + options.providerAddress, + await this.getRoutePath(fromToken, to) + ), + tradeType: this.type + }; + } catch (err) { + const rubicSdkError = CrossChainProvider.parseError(err); + + return { + trade: null, + error: rubicSdkError, + tradeType: this.type + }; + } + } + + protected async getFeeInfo( + fromBlockchain: ArchonBridgeSupportedBlockchain, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } + + protected async getRoutePath( + fromToken: PriceTokenAmount, + toToken: PriceTokenAmount + ): Promise { + return [{ type: 'cross-chain', provider: this.type, path: [fromToken, toToken] }]; + } + + private checkUnsupportedTokens(fromToken: PriceTokenAmount, toToken: PriceToken): boolean { + const fromBlockchain = fromToken.blockchain as ArchonBridgeSupportedBlockchain; + const toBlockchain = toToken.blockchain as ArchonBridgeSupportedBlockchain; + + const [eonToken, nonEonBlockchain, nonEonToken] = + fromBlockchain === BLOCKCHAIN_NAME.HORIZEN_EON + ? [fromToken, toBlockchain, toToken] + : [toToken, fromBlockchain, fromToken]; + + const eonTokenAddress = Object.values(supportedEonTokens).find(el => + compareAddresses(el, eonToken.address) + ); + if (!eonTokenAddress) { + return true; + } + const fromNetworkAddresses = + nonEonBlockchain === BLOCKCHAIN_NAME.ETHEREUM + ? eonEthTokensMapping + : eonAvalancheTokensMapping; + const nonEonAddresses = fromNetworkAddresses[eonTokenAddress]; + const allowSwap = nonEonAddresses.some(address => + compareAddresses(address, nonEonToken.address) + ); + + return !allowSwap; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/archon-bridge-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/archon-bridge-trade.ts new file mode 100644 index 0000000..1aa59d7 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/archon-bridge-trade.ts @@ -0,0 +1,249 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { ArchonContractService } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/archon-contract-service'; +import { archonBridgeAbi } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-abi'; +import { ArchonBridgeSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-supported-blockchain'; +import { archonWrapBridgeAbi } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-wrap-bridge-abi'; +import { layerZeroIds } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/layer-zero-ids'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; + +export class ArchonBridgeTrade extends EvmCrossChainTrade { + /** @internal */ + public static async getGasData( + _from: PriceTokenAmount, + _to: PriceTokenAmount + ): Promise { + return null; + } + + public readonly onChainSubtype = { from: undefined, to: undefined }; + + public readonly type = CROSS_CHAIN_TRADE_TYPE.ARCHON_BRIDGE; + + public readonly isAggregator = false; + + public readonly bridgeType = BRIDGE_TYPE.ARCHON_BRIDGE; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly gasData: GasData | null; + + private get fromBlockchain(): ArchonBridgeSupportedBlockchain { + return this.from.blockchain as ArchonBridgeSupportedBlockchain; + } + + protected get fromContractAddress(): string { + if (this.isProxyTrade) { + return rubicProxyContractAddress[this.fromBlockchain].gateway; + } + return ArchonContractService.getWeb3AndAddress(this.from, this.to).contract.address; + } + + public readonly feeInfo: FeeInfo = {}; + + public readonly onChainTrade = null; + + protected get methodName(): string { + return this.onChainTrade + ? 'swapAndStartBridgeTokensViaGenericCrossChain' + : 'startBridgeTokensViaGenericCrossChain'; + } + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + gasData: GasData | null; + feeInfo: FeeInfo; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.gasData = crossChainTrade.gasData; + this.toTokenAmountMin = crossChainTrade.to.tokenAmount; + this.feeInfo = crossChainTrade.feeInfo; + } + + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + await this.checkTradeErrors(); + await this.checkAllowanceAndApprove(options); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + // eslint-disable-next-line no-useless-catch + try { + const { data, value, to } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress + ); + + await this.web3Private.trySendTransaction(to, { + data, + value, + onTransactionHash, + gas: gasLimit, + gasPriceOptions + }); + + return transactionHash!; + } catch (err) { + throw err; + } + } + + public async getContractParams(options: GetContractParamsOptions): Promise { + const { + data, + value: providerValue, + to: providerRouter + } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress + ); + + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData(options, { + walletAddress: this.walletAddress, + fromTokenAmount: this.from, + toTokenAmount: this.to, + srcChainTrade: null, + providerAddress: this.providerAddress, + type: `native:${this.bridgeType}`, + fromAddress: this.walletAddress + }); + const extraNativeFee = this.from.isNative + ? new BigNumber(providerValue).minus(this.from.stringWeiAmount).toFixed() + : new BigNumber(providerValue).toFixed(); + const providerData = await ProxyCrossChainEvmTrade.getGenericProviderData( + providerRouter, + data!, + this.fromBlockchain, + providerRouter, + extraNativeFee + ); + + const methodArguments = [bridgeData, providerData]; + + const value = this.getSwapValue(providerValue); + + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + return fromUsd.dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: 0, + slippage: 0, + routePath: this.routePath + }; + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + const { contract } = ArchonContractService.getWeb3AndAddress(this.from, this.to); + const providerFee = this.feeInfo.provider?.cryptoFee?.amount || new BigNumber(0); + const nativeToken = nativeTokensList[this.fromBlockchain]; + const fromValueAmount = this.from.isNative ? this.from.tokenAmount : new BigNumber(0); + const value = Web3Pure.toWei(fromValueAmount.plus(providerFee), nativeToken.decimals); + const methodArguments = []; + let config: EvmEncodeConfig | null = null; + + if (contract.type === 'originRouter') { + if (!this.from.isNative) { + methodArguments.push(this.from.address); + } + methodArguments.push( + this.from.stringWeiAmount, + receiverAddress || this.walletAddress, + [this.walletAddress, '0x0000000000000000000000000000000000000000'], + '0x' + ); + + config = EvmWeb3Pure.encodeMethodCall( + contract.address, + archonBridgeAbi, + this.from.isNative ? 'bridgeNative' : 'bridge', + methodArguments, + value + ); + } else { + methodArguments.push( + this.from.address, + layerZeroIds[this.to.blockchain as ArchonBridgeSupportedBlockchain], + this.from.stringWeiAmount, + receiverAddress || this.walletAddress, + true, + [this.walletAddress, '0x0000000000000000000000000000000000000000'], + '0x' + ); + + config = EvmWeb3Pure.encodeMethodCall( + contract.address, + archonWrapBridgeAbi, + this.from.blockchain === BLOCKCHAIN_NAME.HORIZEN_EON && this.from.isNative + ? 'bridgeNative' + : 'bridge', + methodArguments, + value + ); + } + + return { config, amount: this.to.stringWeiAmount }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/archon-contract-service.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/archon-contract-service.ts new file mode 100644 index 0000000..52a3d4f --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/archon-contract-service.ts @@ -0,0 +1,105 @@ +import BigNumber from 'bignumber.js'; +import { Token } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { Injector } from 'src/core/injector/injector'; +import { archonBridgeAbi } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-abi'; +import { + archonBridgeInContractAddress, + archonBridgeOutContractAddress +} from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-out-contract-address'; +import { ArchonBridgeSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-supported-blockchain'; +import { archonWrapBridgeAbi } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-wrap-bridge-abi'; +import { layerZeroIds } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/layer-zero-ids'; +import { + eonAvalancheTokensMapping, + eonEthTokensMapping, + supportedEonTokens +} from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/supported-tokens'; + +export class ArchonContractService { + public static async fetchDepositFeeBps(fromToken: Token, toToken: Token): Promise { + const { web3, contract } = ArchonContractService.getWeb3AndAddress(fromToken, toToken); + let fee: string; + + if (contract.type === 'originRouter') { + fee = await web3.callContractMethod( + contract.address, + archonBridgeAbi, + 'depositFeeBps', + [] + ); + } else { + fee = await web3.callContractMethod( + contract.address, + archonWrapBridgeAbi, + 'withdrawalFeeBps', + [] + ); + } + + return new BigNumber(fee).dividedBy(10_000).toNumber(); + } + + public static async fetchLayerZeroFee(fromToken: Token, toToken: Token): Promise { + const { web3, contract } = ArchonContractService.getWeb3AndAddress(fromToken, toToken); + + if (contract.type === 'originRouter') { + const fee = await web3.callContractMethod<{ nativeFee: string }>( + contract.address, + archonBridgeAbi, + 'estimateBridgeFee', + [false, '0x'] + ); + return fee.nativeFee; + } + + const remoteChainId = layerZeroIds[toToken.blockchain as ArchonBridgeSupportedBlockchain]; + const fee = await web3.callContractMethod<{ nativeFee: string }>( + contract.address, + archonWrapBridgeAbi, + 'estimateBridgeFee', + [remoteChainId, false, '0x'] + ); + return fee.nativeFee; + } + + public static getWeb3AndAddress( + fromToken: Token, + toToken: Token + ): { web3: EvmWeb3Public; contract: { address: string; type: string } } { + const web3 = Injector.web3PublicService.getWeb3Public(fromToken.blockchain); + const contract = { address: '', type: '' }; + + if (fromToken.blockchain === BLOCKCHAIN_NAME.HORIZEN_EON) { + const toBlockchain = toToken.blockchain as Exclude< + ArchonBridgeSupportedBlockchain, + typeof BLOCKCHAIN_NAME.HORIZEN_EON + >; + const contractOut = archonBridgeOutContractAddress[toBlockchain]; + contract.address = fromToken.isNative + ? contractOut.originRouter + : contractOut.wrapRouter; + contract.type = fromToken.isNative ? 'originRouter' : 'wrapRouter'; + } else { + const fromBlockchain = fromToken.blockchain as Exclude< + ArchonBridgeSupportedBlockchain, + typeof BLOCKCHAIN_NAME.HORIZEN_EON + >; + const contractIn = archonBridgeInContractAddress[fromBlockchain]; + const fromNetworkAddresses = + toToken.blockchain === BLOCKCHAIN_NAME.ETHEREUM + ? eonEthTokensMapping + : eonAvalancheTokensMapping; + + const wzenNonEonAddress = fromNetworkAddresses[supportedEonTokens.wzen][0]!; + const isWzen = compareAddresses(fromToken.address, wzenNonEonAddress); + + contract.address = isWzen ? contractIn.wrapRouter : contractIn.originRouter; + contract.type = isWzen ? 'wrapRouter' : 'originRouter'; + } + + return { web3, contract }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-abi.ts new file mode 100644 index 0000000..021e525 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-abi.ts @@ -0,0 +1,862 @@ +import { AbiItem } from 'web3-utils'; + +export const archonBridgeAbi: AbiItem[] = [ + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + name: 'LDtoSDConversionRate', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'PT_MINT', + outputs: [ + { + internalType: 'uint8', + name: '', + type: 'uint8' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'PT_UNLOCK', + outputs: [ + { + internalType: 'uint8', + name: '', + type: 'uint8' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'TOTAL_BPS', + outputs: [ + { + internalType: 'uint16', + name: '', + type: 'uint16' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'token', + type: 'address' + } + ], + name: 'accruedFeeLD', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'automaticFeesTransfer', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'token', + type: 'address' + }, + { + internalType: 'uint256', + name: 'amountLD', + type: 'uint256' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + components: [ + { + internalType: 'address payable', + name: 'refundAddress', + type: 'address' + }, + { + internalType: 'address', + name: 'zroPaymentAddress', + type: 'address' + } + ], + internalType: 'struct LzLib.CallParams', + name: 'callParams', + type: 'tuple' + }, + { + internalType: 'bytes', + name: 'adapterParams', + type: 'bytes' + } + ], + name: 'bridge', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountLD', + type: 'uint256' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + components: [ + { + internalType: 'address payable', + name: 'refundAddress', + type: 'address' + }, + { + internalType: 'address', + name: 'zroPaymentAddress', + type: 'address' + } + ], + internalType: 'struct LzLib.CallParams', + name: 'callParams', + type: 'tuple' + }, + { + internalType: 'bytes', + name: 'adapterParams', + type: 'bytes' + } + ], + name: 'bridgeNative', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [], + name: 'depositFeeBps', + outputs: [ + { + internalType: 'uint16', + name: '', + type: 'uint16' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'bool', + name: 'useZro', + type: 'bool' + }, + { + internalType: 'bytes', + name: 'adapterParams', + type: 'bytes' + } + ], + name: 'estimateBridgeFee', + outputs: [ + { + internalType: 'uint256', + name: 'nativeFee', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'zroFee', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '', + type: 'uint16' + }, + { + internalType: 'bytes', + name: '', + type: 'bytes' + }, + { + internalType: 'uint64', + name: '', + type: 'uint64' + } + ], + name: 'failedMessages', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'feeReceiver', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_srcChainId', + type: 'uint16' + }, + { + internalType: 'bytes', + name: '_srcAddress', + type: 'bytes' + } + ], + name: 'forceResumeReceive', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_version', + type: 'uint16' + }, + { + internalType: 'uint16', + name: '_chainId', + type: 'uint16' + }, + { + internalType: 'address', + name: '', + type: 'address' + }, + { + internalType: 'uint256', + name: '_configType', + type: 'uint256' + } + ], + name: 'getConfig', + outputs: [ + { + internalType: 'bytes', + name: '', + type: 'bytes' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_remoteChainId', + type: 'uint16' + } + ], + name: 'getTrustedRemoteAddress', + outputs: [ + { + internalType: 'bytes', + name: '', + type: 'bytes' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_srcChainId', + type: 'uint16' + }, + { + internalType: 'bytes', + name: '_srcAddress', + type: 'bytes' + } + ], + name: 'isTrustedRemote', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'lzEndpoint', + outputs: [ + { + internalType: 'contract ILayerZeroEndpoint', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_srcChainId', + type: 'uint16' + }, + { + internalType: 'bytes', + name: '_srcAddress', + type: 'bytes' + }, + { + internalType: 'uint64', + name: '_nonce', + type: 'uint64' + }, + { + internalType: 'bytes', + name: '_payload', + type: 'bytes' + } + ], + name: 'lzReceive', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '', + type: 'uint16' + }, + { + internalType: 'uint16', + name: '', + type: 'uint16' + } + ], + name: 'minDstGasLookup', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_srcChainId', + type: 'uint16' + }, + { + internalType: 'bytes', + name: '_srcAddress', + type: 'bytes' + }, + { + internalType: 'uint64', + name: '_nonce', + type: 'uint64' + }, + { + internalType: 'bytes', + name: '_payload', + type: 'bytes' + } + ], + name: 'nonblockingLzReceive', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [], + name: 'owner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'precrime', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'token', + type: 'address' + }, + { + internalType: 'uint8', + name: 'sharedDecimals', + type: 'uint8' + } + ], + name: 'registerToken', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [], + name: 'remoteChainId', + outputs: [ + { + internalType: 'uint16', + name: '', + type: 'uint16' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'renounceOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_srcChainId', + type: 'uint16' + }, + { + internalType: 'bytes', + name: '_srcAddress', + type: 'bytes' + }, + { + internalType: 'uint64', + name: '_nonce', + type: 'uint64' + }, + { + internalType: 'bytes', + name: '_payload', + type: 'bytes' + } + ], + name: 'retryMessage', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_version', + type: 'uint16' + }, + { + internalType: 'uint16', + name: '_chainId', + type: 'uint16' + }, + { + internalType: 'uint256', + name: '_configType', + type: 'uint256' + }, + { + internalType: 'bytes', + name: '_config', + type: 'bytes' + } + ], + name: 'setConfig', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_depositFeeBps', + type: 'uint16' + } + ], + name: 'setDepositFeeBps', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_feeReceiver', + type: 'address' + }, + { + internalType: 'bool', + name: '_automaticFeesTransfer', + type: 'bool' + } + ], + name: 'setFeeReceiver', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_dstChainId', + type: 'uint16' + }, + { + internalType: 'uint16', + name: '_packetType', + type: 'uint16' + }, + { + internalType: 'uint256', + name: '_minGas', + type: 'uint256' + } + ], + name: 'setMinDstGas', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_precrime', + type: 'address' + } + ], + name: 'setPrecrime', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_version', + type: 'uint16' + } + ], + name: 'setReceiveVersion', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_remoteChainId', + type: 'uint16' + } + ], + name: 'setRemoteChainId', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_version', + type: 'uint16' + } + ], + name: 'setSendVersion', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_srcChainId', + type: 'uint16' + }, + { + internalType: 'bytes', + name: '_path', + type: 'bytes' + } + ], + name: 'setTrustedRemote', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '_remoteChainId', + type: 'uint16' + }, + { + internalType: 'bytes', + name: '_remoteAddress', + type: 'bytes' + } + ], + name: 'setTrustedRemoteAddress', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'bool', + name: '_useCustomAdapterParams', + type: 'bool' + } + ], + name: 'setUseCustomAdapterParams', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + name: 'supportedTokens', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + name: 'totalValueLockedSD', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'newOwner', + type: 'address' + } + ], + name: 'transferOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint16', + name: '', + type: 'uint16' + } + ], + name: 'trustedRemoteLookup', + outputs: [ + { + internalType: 'bytes', + name: '', + type: 'bytes' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'token', + type: 'address' + } + ], + name: 'unregisterToken', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [], + name: 'useCustomAdapterParams', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'weth', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'token', + type: 'address' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'amountLD', + type: 'uint256' + } + ], + name: 'withdrawFee', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + stateMutability: 'payable', + type: 'receive' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-out-contract-address.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-out-contract-address.ts new file mode 100644 index 0000000..a1f54e8 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-out-contract-address.ts @@ -0,0 +1,35 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { ArchonBridgeSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-supported-blockchain'; + +type EonContract = { wrapRouter: string; originRouter: string; rubicRouter: string }; +export const archonBridgeOutContractAddress: Record< + Exclude, + EonContract +> = { + [BLOCKCHAIN_NAME.AVALANCHE]: { + wrapRouter: '0xA2C2214dD03a60404C5AdeF4514E77fC00668592', + originRouter: '0x7A302432D99DE20bc622e9148b690f22ef21436e', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + }, + [BLOCKCHAIN_NAME.ETHEREUM]: { + wrapRouter: '0xA2C2214dD03a60404C5AdeF4514E77fC00668592', + originRouter: '0x4fd89120A6d34024Cb86a9a0d7819565Fe4eC351', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + } +}; + +export const archonBridgeInContractAddress: Record< + Exclude, + EonContract +> = { + [BLOCKCHAIN_NAME.AVALANCHE]: { + wrapRouter: '0xB2F5d60530C5E589bd9326e4c57933F611a624C6', + originRouter: '0x0c81b1905125ED89C42a0aDa098adfd461f8A9C5', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + }, + [BLOCKCHAIN_NAME.ETHEREUM]: { + wrapRouter: '0x8dE1Fb1F8a23202C7282716AE0089c6a96e07995', + originRouter: '0x954367cb2028e704B62a4093f648BE453aCA3989', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + } +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-supported-blockchain.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-supported-blockchain.ts new file mode 100644 index 0000000..1cd1d7e --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-supported-blockchain.ts @@ -0,0 +1,13 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const archonBridgeSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.HORIZEN_EON +] as const; + +export type ArchonBridgeSupportedBlockchain = (typeof archonBridgeSupportedBlockchains)[number]; +export type ArchonBridgeNonEonSupportedBlockchain = Exclude< + ArchonBridgeSupportedBlockchain, + typeof BLOCKCHAIN_NAME.HORIZEN_EON +>; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-wrap-bridge-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-wrap-bridge-abi.ts new file mode 100644 index 0000000..87fa768 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-wrap-bridge-abi.ts @@ -0,0 +1,48 @@ +import { AbiItem } from 'web3-utils'; + +export const archonWrapBridgeAbi: AbiItem[] = [ + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'bridge', + inputs: [ + { type: 'address', name: 'localToken', internalType: 'address' }, + { type: 'uint16', name: 'remoteChainId', internalType: 'uint16' }, + { type: 'uint256', name: 'amount', internalType: 'uint256' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'bool', name: 'unwrapWeth', internalType: 'bool' }, + { + type: 'tuple', + name: 'callParams', + internalType: 'struct LzLib.CallParams', + components: [ + { type: 'address', name: 'refundAddress', internalType: 'address payable' }, + { type: 'address', name: 'zroPaymentAddress', internalType: 'address' } + ] + }, + { type: 'bytes', name: 'adapterParams', internalType: 'bytes' } + ] + }, + { + type: 'function', + stateMutability: 'view', + outputs: [ + { type: 'uint256', name: 'nativeFee', internalType: 'uint256' }, + { type: 'uint256', name: 'zroFee', internalType: 'uint256' } + ], + name: 'estimateBridgeFee', + inputs: [ + { type: 'uint16', name: 'remoteChainId', internalType: 'uint16' }, + { type: 'bool', name: 'useZro', internalType: 'bool' }, + { type: 'bytes', name: 'adapterParams', internalType: 'bytes' } + ] + }, + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'uint16', name: '', internalType: 'uint16' }], + name: 'withdrawalFeeBps', + inputs: [] + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/layer-zero-ids.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/layer-zero-ids.ts new file mode 100644 index 0000000..a7fe004 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/layer-zero-ids.ts @@ -0,0 +1,8 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { ArchonBridgeSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/archon-bridge-supported-blockchain'; + +export const layerZeroIds: Record = { + [BLOCKCHAIN_NAME.HORIZEN_EON]: '215', + [BLOCKCHAIN_NAME.ETHEREUM]: '101', + [BLOCKCHAIN_NAME.AVALANCHE]: '106' +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/supported-tokens.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/supported-tokens.ts new file mode 100644 index 0000000..7c9e76d --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/archon-bridge/constants/supported-tokens.ts @@ -0,0 +1,45 @@ +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; + +export const supportedEonTokens = { + weth: '0x2c2E0B0c643aB9ad03adBe9140627A645E99E054', + wavax: '0x6318374DFb468113E06d3463ec5Ed0B6Ae0F0982', + usdc: '0xCc44eB064CD32AAfEEb2ebb2a47bE0B882383b53', + usdt: '0xA167bcAb6791304EDa9B636C8beEC75b3D2829E6', + dai: '0x38C2a6953F86a7453622B1E7103b738239728754', + link: '0xDF8DBA35962Aa0fAD7ade0Df07501c54Ec7c4A89', + btc: '0x1d7fb99AED3C365B4DEf061B7978CE5055Dfc1e7', + zen: EvmWeb3Pure.EMPTY_ADDRESS, + wzen: '0xF5cB8652a84329A2016A386206761f455bCEDab6' +} as const; + +type SupportedEonTokens = (typeof supportedEonTokens)[keyof typeof supportedEonTokens]; + +export const eonEthTokensMapping: Record = { + [supportedEonTokens.weth]: [ + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + EvmWeb3Pure.EMPTY_ADDRESS + ], + [supportedEonTokens.usdc]: ['0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'], + [supportedEonTokens.usdt]: ['0xdac17f958d2ee523a2206206994597c13d831ec7'], + [supportedEonTokens.dai]: ['0x6b175474e89094c44da98b954eedeac495271d0f'], + [supportedEonTokens.link]: ['0x514910771af9ca656af840dff83e8264ecf986ca'], + [supportedEonTokens.btc]: ['0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599'], + [supportedEonTokens.wavax]: [], // No token address for WAVAX + [supportedEonTokens.zen]: ['0xd21475D90686c9A6FDBe0849cb6670fEc2aC9E21'], + [supportedEonTokens.wzen]: ['0xd21475D90686c9A6FDBe0849cb6670fEc2aC9E21'] +}; + +export const eonAvalancheTokensMapping: Record = { + [supportedEonTokens.weth]: [], // No token address for WETH + [supportedEonTokens.usdc]: ['0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e'], + [supportedEonTokens.usdt]: ['0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7'], + [supportedEonTokens.dai]: ['0xd586e7f844cea2f87f50152665bcbc2c279d8d70'], + [supportedEonTokens.link]: ['0x5947bb275c521040051d82396192181b413227a3'], + [supportedEonTokens.btc]: ['0x50b7545627a5162f82a992c33b87adc75187b218'], + [supportedEonTokens.wavax]: [ + '0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7', + EvmWeb3Pure.EMPTY_ADDRESS + ], + [supportedEonTokens.zen]: ['0xAA1dA1591cBF7f2Df46884E7144297FF15Ea3a7f'], + [supportedEonTokens.wzen]: ['0xAA1dA1591cBF7f2Df46884E7144297FF15Ea3a7f'] +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/bridgers-cross-chain-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/bridgers-cross-chain-provider.ts new file mode 100644 index 0000000..e742fb6 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/bridgers-cross-chain-provider.ts @@ -0,0 +1,234 @@ +import BigNumber from 'bignumber.js'; +import { + BridgersPairIsUnavailableError, + MaxAmountError, + MinAmountError, + NotSupportedTokensError +} from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + TronBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { TronTransactionConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-transaction-config'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { bridgersNativeAddress } from 'src/features/common/providers/bridgers/constants/bridgers-native-address'; +import { toBridgersBlockchain } from 'src/features/common/providers/bridgers/constants/to-bridgers-blockchain'; +import { + BridgersQuoteRequest, + BridgersQuoteResponse +} from 'src/features/common/providers/bridgers/models/bridgers-quote-api'; +import { createTokenNativeAddressProxy } from 'src/features/common/utils/token-native-address-proxy'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { + BridgersCrossChainSupportedBlockchain, + bridgersCrossChainSupportedBlockchains, + BridgersEvmCrossChainSupportedBlockchain +} from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/constants/bridgers-cross-chain-supported-blockchain'; +import { EvmBridgersCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/evm-bridgers-trade/evm-bridgers-cross-chain-trade'; +import { TronBridgersCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/tron-bridgers-trade/tron-bridgers-cross-chain-trade'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { tronCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/constants/tron-common-cross-chain-abi'; +import { AbiItem } from 'web3-utils'; + +export class BridgersCrossChainProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.BRIDGERS; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is BridgersCrossChainSupportedBlockchain { + return bridgersCrossChainSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public override areSupportedBlockchains( + fromBlockchain: BlockchainName, + toBlockchain: BlockchainName + ): boolean { + return ( + (fromBlockchain === BLOCKCHAIN_NAME.TRON && this.isSupportedBlockchain(toBlockchain)) || + (this.isSupportedBlockchain(fromBlockchain) && toBlockchain === BLOCKCHAIN_NAME.TRON) + ); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise> { + const fromBlockchain = from.blockchain as BridgersCrossChainSupportedBlockchain; + const toBlockchain = toToken.blockchain as BridgersCrossChainSupportedBlockchain; + if (!this.areSupportedBlockchains(fromBlockchain, toBlockchain)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + try { + const contractAbi = BlockchainsInfo.isTronBlockchainName(fromBlockchain) + ? tronCommonCrossChainAbi + : evmCommonCrossChainAbi; + + let feeInfo = await this.getFeeInfo( + fromBlockchain, + options.providerAddress, + from, + false, + contractAbi + ); + // const fromWithoutFee = getFromWithoutFee( + // from, + // feeInfo.rubicProxy?.platformFee?.percent + // ); + const fromWithoutFee = from; + + const fromTokenAddress = createTokenNativeAddressProxy( + from, + bridgersNativeAddress, + false + ).address; + const toTokenAddress = createTokenNativeAddressProxy( + toToken, + bridgersNativeAddress, + false + ).address; + const quoteRequest: BridgersQuoteRequest = { + fromTokenAddress, + toTokenAddress, + fromTokenAmount: fromWithoutFee.stringWeiAmount, + fromTokenChain: toBridgersBlockchain[fromBlockchain], + toTokenChain: toBridgersBlockchain[toBlockchain] + }; + const quoteResponse = await this.httpClient.post( + 'https://sswap.swft.pro/api/sswap/quote', + quoteRequest + ); + const transactionData = quoteResponse.data?.txData; + if (quoteResponse.resCode !== 100 || !transactionData) { + return { + trade: null, + error: CrossChainProvider.parseError(new BridgersPairIsUnavailableError()), + tradeType: this.type + }; + } + + if (from.tokenAmount.lt(transactionData.depositMin)) { + return { + trade: null, + error: new MinAmountError( + new BigNumber(transactionData.depositMin), + from.symbol + ), + tradeType: this.type + }; + } + if (from.tokenAmount.gt(transactionData.depositMax)) { + return { + trade: null, + error: new MaxAmountError( + new BigNumber(transactionData.depositMax), + from.symbol + ), + tradeType: this.type + }; + } + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + blockchain: toBlockchain, + tokenAmount: new BigNumber(transactionData.toTokenAmount) + }); + const toTokenAmountMin = Web3Pure.fromWei( + transactionData.amountOutMin, + toToken.decimals + ); + + if (BlockchainsInfo.isEvmBlockchainName(fromBlockchain)) { + const gasData = + options.gasCalculation === 'enabled' && options.receiverAddress + ? await EvmBridgersCrossChainTrade.getGasData( + from as PriceTokenAmount, + to as PriceTokenAmount, + options.receiverAddress, + options.providerAddress, + feeInfo + ) + : null; + + return { + trade: new EvmBridgersCrossChainTrade( + { + from: from as PriceTokenAmount, + to: to as PriceTokenAmount, + toTokenAmountMin, + feeInfo, + gasData, + slippage: options.slippageTolerance + }, + options.providerAddress, + await this.getRoutePath(from, to) + ), + tradeType: this.type + }; + } + return { + trade: new TronBridgersCrossChainTrade( + { + from: from as PriceTokenAmount, + to: to as PriceTokenAmount, + toTokenAmountMin, + feeInfo, + slippage: options.slippageTolerance, + contractAddress: transactionData.contractAddress + }, + options.providerAddress, + await this.getRoutePath(from, to) + ), + tradeType: this.type + }; + } catch (err: unknown) { + return { + trade: null, + error: CrossChainProvider.parseError(err), + tradeType: this.type + }; + } + } + + protected override async getFeeInfo( + fromBlockchain: BridgersCrossChainSupportedBlockchain, + _providerAddress: string, + _percentFeeToken: PriceTokenAmount, + _useProxy: boolean, + _contractAbi: AbiItem[] + ): Promise { + const nativeToken = await PriceToken.createFromToken(nativeTokensList[fromBlockchain]); + return { + rubicProxy: { + fixedFee: { + amount: new BigNumber(0), + token: nativeToken + } + } + }; + } + + protected async getRoutePath( + fromToken: PriceTokenAmount, + toToken: PriceTokenAmount + ): Promise { + return [{ type: 'cross-chain', provider: this.type, path: [fromToken, toToken] }]; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/constants/bridgers-cross-chain-supported-blockchain.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/constants/bridgers-cross-chain-supported-blockchain.ts new file mode 100644 index 0000000..06e7930 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/constants/bridgers-cross-chain-supported-blockchain.ts @@ -0,0 +1,15 @@ +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +export const bridgersCrossChainSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.TRON +] as const; + +export type BridgersCrossChainSupportedBlockchain = + (typeof bridgersCrossChainSupportedBlockchains)[number]; + +export type BridgersEvmCrossChainSupportedBlockchain = BridgersCrossChainSupportedBlockchain & + EvmBlockchainName; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/evm-bridgers-trade/evm-bridgers-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/evm-bridgers-trade/evm-bridgers-cross-chain-trade.ts new file mode 100644 index 0000000..5979a4b --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/evm-bridgers-trade/evm-bridgers-cross-chain-trade.ts @@ -0,0 +1,231 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { TronBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { bridgersContractAddresses } from 'src/features/common/providers/bridgers/models/bridgers-contract-addresses'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { BridgersEvmCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/constants/bridgers-cross-chain-supported-blockchain'; +import { EvmBridgersTransactionData } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/evm-bridgers-trade/models/evm-bridgers-transaction-data'; +import { getProxyMethodArgumentsAndTransactionData } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/utils/get-proxy-method-arguments-and-transaction-data'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; +import { MarkRequired } from 'ts-essentials'; + +export class EvmBridgersCrossChainTrade extends EvmCrossChainTrade { + /** @internal */ + public static async getGasData( + from: PriceTokenAmount, + to: PriceTokenAmount, + receiverAddress: string, + providerAddress: string, + feeInfo: FeeInfo + ): Promise { + const trade = new EvmBridgersCrossChainTrade( + { + from, + to, + toTokenAmountMin: new BigNumber(0), + feeInfo, + gasData: null, + slippage: 0 + }, + providerAddress || EvmWeb3Pure.EMPTY_ADDRESS, + [] + ); + + return getCrossChainGasData(trade, receiverAddress); + } + + public readonly type = CROSS_CHAIN_TRADE_TYPE.BRIDGERS; + + public readonly isAggregator = false; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly gasData: GasData; + + public readonly feeInfo: FeeInfo; + + public readonly onChainSubtype = { from: undefined, to: undefined }; + + public readonly bridgeType = BRIDGE_TYPE.BRIDGERS; + + public readonly priceImpact: number | null; + + private readonly slippage: number; + + protected get fromContractAddress(): string { + return this.isProxyTrade + ? rubicProxyContractAddress[this.from.blockchain].gateway + : bridgersContractAddresses[this.from.blockchain]; + } + + protected get methodName(): string { + return ''; + } + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + toTokenAmountMin: BigNumber; + feeInfo: FeeInfo; + gasData: GasData; + slippage: number; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.toTokenAmountMin = crossChainTrade.toTokenAmountMin; + this.feeInfo = crossChainTrade.feeInfo; + this.gasData = crossChainTrade.gasData; + this.priceImpact = this.from.calculatePriceImpactPercent(this.to); + this.slippage = crossChainTrade.slippage; + } + + protected async swapDirect( + options: MarkRequired + ): Promise { + await this.checkTradeErrors(); + await this.checkReceiverAddress(options.receiverAddress, true); + + await this.checkAllowanceAndApprove(options); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + // eslint-disable-next-line no-useless-catch + try { + const fromWithoutFee = getFromWithoutFee( + this.from, + this.feeInfo.rubicProxy?.platformFee?.percent + ); + + const { transactionData } = + await getProxyMethodArgumentsAndTransactionData( + this.from, + fromWithoutFee, + this.to, + this.toTokenAmountMin, + this.walletAddress, + this.providerAddress, + options, + this.checkAmountChange + ); + + await this.web3Private.trySendTransaction(transactionData.to, { + data: transactionData.data, + value: transactionData.value, + onTransactionHash, + gas: gasLimit, + gasPriceOptions + }); + + return transactionHash!; + } catch (err) { + throw err; + } + } + + public async encode( + options: MarkRequired + ): Promise { + return super.encode(options); + } + + protected async getContractParams( + options: MarkRequired + ): Promise { + const fromWithoutFee = getFromWithoutFee( + this.from, + this.feeInfo.rubicProxy?.platformFee?.percent + ); + const { methodArguments, transactionData } = + await getProxyMethodArgumentsAndTransactionData( + this.from, + fromWithoutFee, + this.to, + this.toTokenAmountMin, + this.walletAddress, + this.providerAddress, + options, + this.checkAmountChange + ); + + const encodedData = transactionData.data; + methodArguments.push(encodedData); + + const value = this.getSwapValue(transactionData.value); + + return { + contractAddress: this.fromContractAddress, + contractAbi: evmCommonCrossChainAbi, + methodName: this.methodName, + methodArguments, + value + }; + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + return fromUsd.dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact ?? null, + slippage: this.slippage * 100, + routePath: this.routePath + }; + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: EvmBridgersTransactionData; amount: string }> { + const fromWithoutFee = getFromWithoutFee( + this.from, + this.feeInfo.rubicProxy?.platformFee?.percent + ); + const { transactionData: config, amountOut: amount } = + await getProxyMethodArgumentsAndTransactionData( + this.from, + fromWithoutFee, + this.to, + this.toTokenAmountMin, + this.walletAddress, + this.providerAddress, + { receiverAddress: receiverAddress! }, + this.checkAmountChange + ); + + return { config, amount }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/evm-bridgers-trade/models/evm-bridgers-transaction-data.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/evm-bridgers-trade/models/evm-bridgers-transaction-data.ts new file mode 100644 index 0000000..52f030e --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/evm-bridgers-trade/models/evm-bridgers-transaction-data.ts @@ -0,0 +1,5 @@ +export interface EvmBridgersTransactionData { + data: string; + to: string; + value: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/tron-bridgers-trade/models/tron-bridgers-transaction-data.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/tron-bridgers-trade/models/tron-bridgers-transaction-data.ts new file mode 100644 index 0000000..7015926 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/tron-bridgers-trade/models/tron-bridgers-transaction-data.ts @@ -0,0 +1,11 @@ +import { TronParameters } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-parameters'; + +export interface TronBridgersTransactionData { + functionName: string; + options: { + feeLimit: number; + callValue: string; + }; + parameter: TronParameters; + to: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/tron-bridgers-trade/tron-bridgers-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/tron-bridgers-trade/tron-bridgers-cross-chain-trade.ts new file mode 100644 index 0000000..71b970d --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/tron-bridgers-trade/tron-bridgers-cross-chain-trade.ts @@ -0,0 +1,223 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { TronBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { TronTransactionConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-transaction-config'; +import { TronWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/tron-web3-pure'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { BridgersEvmCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/constants/bridgers-cross-chain-supported-blockchain'; +import { TronBridgersTransactionData } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/tron-bridgers-trade/models/tron-bridgers-transaction-data'; +import { getProxyMethodArgumentsAndTransactionData } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/utils/get-proxy-method-arguments-and-transaction-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { tronCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/constants/tron-common-cross-chain-abi'; +import { tronNativeSwapAbi } from 'src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/constants/tron-native-swap-abi'; +import { TronContractParams } from 'src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/models/tron-contract-params'; +import { TronGetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/models/tron-get-contract-params-options'; +import { TronCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/tron-cross-chain-trade'; +import { MarkRequired } from 'ts-essentials'; + +export class TronBridgersCrossChainTrade extends TronCrossChainTrade { + public readonly type = CROSS_CHAIN_TRADE_TYPE.BRIDGERS; + + public readonly isAggregator = false; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly feeInfo: FeeInfo; + + public readonly onChainSubtype = { from: undefined, to: undefined }; + + public readonly bridgeType = BRIDGE_TYPE.BRIDGERS; + + public readonly priceImpact: number | null; + + private readonly slippage: number; + + private readonly contractAddress: string; + + protected get fromContractAddress(): string { + // return rubicProxyContractAddress[this.from.blockchain]; + return this.contractAddress; + } + + protected get methodName(): string { + return ''; + } + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + toTokenAmountMin: BigNumber; + feeInfo: FeeInfo; + slippage: number; + contractAddress: string; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.toTokenAmountMin = crossChainTrade.toTokenAmountMin; + this.feeInfo = crossChainTrade.feeInfo; + this.priceImpact = this.from.calculatePriceImpactPercent(this.to); + this.slippage = crossChainTrade.slippage; + this.contractAddress = crossChainTrade.contractAddress; + } + + public async swap( + options: MarkRequired + ): Promise { + return this.swapDirect(options); + } + + private async swapDirect( + options: MarkRequired + ): Promise { + await this.checkTradeErrors(); + await this.checkReceiverAddress(options.receiverAddress, true); + await this.checkAllowanceAndApprove(options); + + const { onConfirm } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + // eslint-disable-next-line no-useless-catch + try { + const fromWithoutFee = getFromWithoutFee( + this.from, + this.feeInfo.rubicProxy?.platformFee?.percent + ); + + const { transactionData } = + await getProxyMethodArgumentsAndTransactionData( + this.from, + fromWithoutFee, + this.to, + this.toTokenAmountMin, + this.walletAddress, + this.providerAddress, + options, + this.checkAmountChange + ); + + await this.web3Private.executeContractMethod( + transactionData.to, + tronNativeSwapAbi, + this.from.isNative ? 'swapEth' : 'swap', + transactionData.parameter.map(el => el.value), + { + onTransactionHash, + callValue: transactionData.options.callValue, + feeLimit: transactionData.options.feeLimit + } + ); + + return transactionHash!; + } catch (err) { + throw err; + } + } + + protected async getContractParams( + options: TronGetContractParamsOptions + ): Promise { + const fromWithoutFee = getFromWithoutFee( + this.from, + this.feeInfo.rubicProxy?.platformFee?.percent + ); + const { methodArguments, transactionData } = + await getProxyMethodArgumentsAndTransactionData( + this.from, + fromWithoutFee, + this.to, + this.toTokenAmountMin, + this.walletAddress, + this.providerAddress, + options, + this.checkAmountChange + ); + + const encodedData = TronWeb3Pure.encodeMethodSignature( + transactionData.functionName, + transactionData.parameter + ); + methodArguments.push(encodedData); + + const value = this.getSwapValue(transactionData.options.callValue); + const { feeLimit } = transactionData.options; + + return { + contractAddress: this.fromContractAddress, + contractAbi: tronCommonCrossChainAbi, + methodName: this.methodName, + methodArguments, + value, + feeLimit + }; + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + return fromUsd.dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: null, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact ?? null, + slippage: this.slippage * 100, + routePath: this.routePath + }; + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: TronTransactionConfig; amount: string }> { + const fromWithoutFee = getFromWithoutFee( + this.from, + this.feeInfo.rubicProxy?.platformFee?.percent + ); + const { transactionData, amountOut } = + await getProxyMethodArgumentsAndTransactionData( + this.from, + fromWithoutFee, + this.to, + this.toTokenAmountMin, + this.walletAddress, + this.providerAddress, + { receiverAddress: receiverAddress! }, + this.checkAmountChange + ); + + const encodedData = TronWeb3Pure.encodeMethodSignature( + transactionData.functionName, + transactionData.parameter + ); + + return { + amount: amountOut, + config: { + data: encodedData, + callValue: transactionData.options.callValue, + feeLimit: transactionData.options.feeLimit, + to: transactionData.to + } + }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/utils/get-proxy-method-arguments-and-transaction-data.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/utils/get-proxy-method-arguments-and-transaction-data.ts new file mode 100644 index 0000000..65924ff --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/bridgers-provider/utils/get-proxy-method-arguments-and-transaction-data.ts @@ -0,0 +1,124 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { TronWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/tron-web3-pure'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { bridgersNativeAddress } from 'src/features/common/providers/bridgers/constants/bridgers-native-address'; +import { toBridgersBlockchain } from 'src/features/common/providers/bridgers/constants/to-bridgers-blockchain'; +import { bridgersContractAddresses } from 'src/features/common/providers/bridgers/models/bridgers-contract-addresses'; +import { + BridgersQuoteRequest, + BridgersQuoteResponse +} from 'src/features/common/providers/bridgers/models/bridgers-quote-api'; +import { + BridgersSwapRequest, + BridgersSwapResponse +} from 'src/features/common/providers/bridgers/models/bridgers-swap-api'; +import { createTokenNativeAddressProxy } from 'src/features/common/utils/token-native-address-proxy'; +import { BridgersCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/constants/bridgers-cross-chain-supported-blockchain'; +import { EvmBridgersTransactionData } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/evm-bridgers-trade/models/evm-bridgers-transaction-data'; +import { TronBridgersTransactionData } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/tron-bridgers-trade/models/tron-bridgers-transaction-data'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { MarkRequired } from 'ts-essentials'; + +export async function getProxyMethodArgumentsAndTransactionData< + T extends EvmBridgersTransactionData | TronBridgersTransactionData +>( + from: PriceTokenAmount, + fromWithoutFee: PriceTokenAmount, + to: PriceTokenAmount, + toTokenAmountMin: BigNumber, + walletAddress: string, + providerAddress: string, + options: MarkRequired, + checkAmountFn: (newWeiAmount: string, oldWeiAmount: string) => void +): Promise<{ + methodArguments: unknown[]; + transactionData: T; + amountOut: string; +}> { + const amountOutMin = Web3Pure.toWei(toTokenAmountMin, to.decimals); + const fromTokenAddress = createTokenNativeAddressProxy( + fromWithoutFee, + bridgersNativeAddress, + !(from.blockchain === BLOCKCHAIN_NAME.TRON) + ).address; + const toTokenAddress = createTokenNativeAddressProxy( + to, + bridgersNativeAddress, + !(to.blockchain === BLOCKCHAIN_NAME.TRON) + ).address; + const fromAddress = options.fromAddress || walletAddress; + const swapRequest: BridgersSwapRequest = { + fromTokenAddress, + toTokenAddress, + fromAddress, + toAddress: options.receiverAddress, + fromTokenChain: toBridgersBlockchain[fromWithoutFee.blockchain], + toTokenChain: toBridgersBlockchain[to.blockchain], + fromTokenAmount: fromWithoutFee.stringWeiAmount, + amountOutMin, + equipmentNo: fromAddress.slice(0, 32), + sourceFlag: 'rubic' + }; + + const swapData = await Injector.httpClient.post>( + 'https://sswap.swft.pro/api/sswap/swap', + swapRequest + ); + const transactionData = swapData.data?.txData; + + const quoteRequest: BridgersQuoteRequest = { + fromTokenAddress, + toTokenAddress, + fromTokenAmount: fromWithoutFee.stringWeiAmount, + fromTokenChain: toBridgersBlockchain[from.blockchain], + toTokenChain: toBridgersBlockchain[to.blockchain] + }; + const quoteResponse = await Injector.httpClient.post( + 'https://sswap.swft.pro/api/sswap/quote', + quoteRequest + ); + const transactionQuoteData = quoteResponse.data?.txData; + + if (transactionQuoteData?.amountOutMin) { + checkAmountFn( + transactionQuoteData.amountOutMin, + Web3Pure.toWei(toTokenAmountMin, to.decimals) + ); + } + + const dstTokenAddress = BlockchainsInfo.isTronBlockchainName(to.blockchain) + ? TronWeb3Pure.addressToHex(to.address) + : to.address; + const receiverAddress = BlockchainsInfo.isTronBlockchainName(to.blockchain) + ? TronWeb3Pure.addressToHex(options.receiverAddress) + : options.receiverAddress; + const contractAddress = transactionData?.to || bridgersContractAddresses[from.blockchain]; + + const methodArguments: unknown[] = [ + 'native:bridgers', + [ + from.address, + from.stringWeiAmount, + blockchainId[to.blockchain], + dstTokenAddress, + amountOutMin, + receiverAddress, + providerAddress, + contractAddress + ] + ]; + if (!from.isNative) { + methodArguments.push(contractAddress); + } + + return { + methodArguments, + transactionData: { ...transactionData, to: contractAddress }, + amountOut: amountOutMin + }; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-api-service.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-api-service.ts new file mode 100644 index 0000000..bc585d8 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-api-service.ts @@ -0,0 +1,64 @@ +// @ts-ignore +import getRequestOptions from 'cbridge-revert-manager'; +import { Injector } from 'src/core/injector/injector'; +import { CbridgeEstimateAmountRequest } from 'src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-estimate-amount-request'; +import { CbridgeEstimateAmountResponse } from 'src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-estimate-amount-response'; +import { CbridgeStatusResponse } from 'src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-status-response'; +import { CbridgeTransferConfigsResponse } from 'src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-transfer-configs-response'; + +export class CbridgeCrossChainApiService { + private static readonly apiEndpoint = 'https://cbridge-prod2.celer.app/v2/'; + + private static readonly testnetApiEndpoint = 'https://cbridge-v2-test.celer.network/v2/'; + + public static async getTransferConfigs(options: { + useTestnet: boolean; + }): Promise { + const apiUrl = options.useTestnet + ? CbridgeCrossChainApiService.testnetApiEndpoint + : CbridgeCrossChainApiService.apiEndpoint; + return Injector.httpClient.get( + `${apiUrl}getTransferConfigs` + ); + } + + public static async fetchEstimateAmount( + requestParams: CbridgeEstimateAmountRequest, + options: { useTestnet: boolean } + ): Promise { + const apiUrl = options.useTestnet + ? CbridgeCrossChainApiService.testnetApiEndpoint + : CbridgeCrossChainApiService.apiEndpoint; + return Injector.httpClient.get(`${apiUrl}estimateAmt`, { + params: { ...requestParams } + }); + } + + public static async fetchTradeStatus( + transferId: string, + options: { + useTestnet: boolean; + } + ): Promise { + const apiUrl = options.useTestnet + ? CbridgeCrossChainApiService.testnetApiEndpoint + : CbridgeCrossChainApiService.apiEndpoint; + return Injector.httpClient.post(`${apiUrl}getTransferStatus`, { + transfer_id: transferId + }); + } + + public static async withdrawLiquidity( + transferId: string, + estimatedReceivedAmt: string, + options: { + useTestnet: boolean; + } + ): Promise { + const apiUrl = options.useTestnet + ? CbridgeCrossChainApiService.testnetApiEndpoint + : CbridgeCrossChainApiService.apiEndpoint; + const body: object = await getRequestOptions(transferId, estimatedReceivedAmt); + return Injector.httpClient.post(`${apiUrl}withdrawLiquidity`, body); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-provider.ts new file mode 100644 index 0000000..a658bf8 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-provider.ts @@ -0,0 +1,409 @@ +import BigNumber from 'bignumber.js'; +import { + MaxAmountError, + MinAmountError, + NotSupportedTokensError, + RubicSdkError +} from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { PriceTokenAmountStruct } from 'src/common/tokens/price-token-amount'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CbridgeCrossChainApiService } from 'src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-api-service'; +import { CbridgeCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-trade'; +import { cbridgeContractAbi } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-contract-abi'; +import { cbridgeContractAddress } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-contract-address'; +import { + CbridgeCrossChainSupportedBlockchain, + cbridgeSupportedBlockchains +} from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-supported-blockchains'; +import { TokenInfo } from 'src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-chain-token-info'; +import { CbridgeEstimateAmountRequest } from 'src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-estimate-amount-request'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { typedTradeProviders } from 'src/features/on-chain/calculation-manager/constants/trade-providers/typed-trade-providers'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +interface CelerConfig { + address: string; + supportedFromToken: TokenInfo | undefined; + supportedToToken: TokenInfo | undefined; + isBridge: boolean; + possibleTransitToken: TokenInfo | undefined; +} + +export class CbridgeCrossChainProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.CELER_BRIDGE; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is CbridgeCrossChainSupportedBlockchain { + return cbridgeSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public async calculate( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise> { + const fromBlockchain = fromToken.blockchain as CbridgeCrossChainSupportedBlockchain; + const toBlockchain = toToken.blockchain as CbridgeCrossChainSupportedBlockchain; + const useProxy = options?.useProxy?.[this.type] ?? true; + + if (!this.areSupportedBlockchains(fromBlockchain, toBlockchain)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + try { + const config = await this.fetchContractAddressAndCheckTokens(fromToken, toToken); + if (!config.supportedToToken) { + throw new RubicSdkError('To token is not supported'); + } + + const feeInfo = await this.getFeeInfo( + fromBlockchain, + options.providerAddress, + fromToken, + useProxy + ); + const fromWithoutFee = getFromWithoutFee( + fromToken, + feeInfo.rubicProxy?.platformFee?.percent + ); + + let onChainTrade: EvmOnChainTrade | null = null; + let transitTokenAmount = fromWithoutFee.tokenAmount; + let transitMinAmount = transitTokenAmount; + let transitToken = fromWithoutFee; + + if (!config.isBridge) { + if (!useProxy) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + onChainTrade = await this.getOnChainTrade( + fromWithoutFee, + [], + options.slippageTolerance, + config.possibleTransitToken!.token.address + ); + if (!onChainTrade) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + transitTokenAmount = onChainTrade.to.tokenAmount; + transitMinAmount = onChainTrade.toTokenAmountMin.tokenAmount; + transitToken = onChainTrade.to; + + const defaultTransit = new PriceTokenAmount({ + ...onChainTrade.to.asStructWithAmount + }); + const transitConfig = await this.fetchContractAddressAndCheckTokens( + defaultTransit, + toToken + ); + const celerTransitTokenStruct: PriceTokenAmountStruct = { + blockchain: fromToken.blockchain, + address: transitToken.address, + name: onChainTrade + ? transitConfig.possibleTransitToken!.name + : transitConfig.supportedFromToken!.name, + symbol: onChainTrade + ? transitConfig.possibleTransitToken!.token.symbol + : transitConfig.supportedFromToken!.token.symbol, + decimals: onChainTrade + ? transitConfig.possibleTransitToken!.token.decimal + : transitConfig.supportedFromToken!.token.decimal, + price: new BigNumber(0), + tokenAmount: transitTokenAmount + }; + transitToken = transitConfig?.supportedFromToken + ? new PriceTokenAmount(celerTransitTokenStruct) + : defaultTransit; + } + + const { amount, maxSlippage } = await this.getEstimates( + transitToken, + toToken, + options, + config + ); + if (!amount) { + throw new RubicSdkError('Can not estimate trade'); + } + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: Web3Pure.fromWei(amount, toToken.decimals) + }); + + const gasData = + options.gasCalculation === 'enabled' + ? await CbridgeCrossChainTrade.getGasData( + fromToken, + to, + onChainTrade, + feeInfo, + maxSlippage, + config.address, + options.providerAddress, + options.receiverAddress || this.getWalletAddress(fromToken.blockchain) + ) + : null; + + const amountsErrors = await this.getMinMaxAmountsErrors(transitToken, feeInfo); + + return { + trade: new CbridgeCrossChainTrade( + { + from: fromToken, + to, + gasData, + priceImpact: fromToken.calculatePriceImpactPercent(to), + slippage: options.slippageTolerance, + feeInfo: feeInfo, + maxSlippage, + contractAddress: config.address, + transitMinAmount, + onChainTrade + }, + options.providerAddress, + await this.getRoutePath(fromToken, transitToken, to, onChainTrade) + ), + error: amountsErrors, + tradeType: this.type + }; + } catch (err) { + const rubicSdkError = CrossChainProvider.parseError(err); + + return { + trade: null, + error: rubicSdkError, + tradeType: this.type + }; + } + } + + private async fetchContractAddressAndCheckTokens( + fromTokenOrNative: PriceTokenAmount, + toTokenOrNative: PriceToken + ): Promise { + let fromToken = fromTokenOrNative; + const useTestnet = BlockchainsInfo.isTestBlockchainName(fromToken.blockchain); + if (fromToken.isNative) { + const wrappedFrom = wrappedNativeTokensList[fromTokenOrNative.blockchain]!; + const token = await PriceTokenAmount.createToken({ + ...wrappedFrom, + tokenAmount: fromTokenOrNative.tokenAmount + }); + fromToken = token as PriceTokenAmount; + } + let toToken = toTokenOrNative; + if (toToken.symbol === nativeTokensList[toToken.blockchain].symbol) { + const wrappedTo = wrappedNativeTokensList[toTokenOrNative.blockchain]!; + const token = await PriceToken.createToken(wrappedTo); + toToken = token as PriceToken; + } + + const config = await CbridgeCrossChainApiService.getTransferConfigs({ useTestnet }); + const fromChainId = blockchainId[fromToken.blockchain]; + const toChainId = blockchainId[toToken.blockchain]; + if ( + !config.chains.some(chain => chain.id === fromChainId) || + !config.chains.some(chain => chain.id === toChainId) + ) { + throw new RubicSdkError('Not supported chain'); + } + + const supportedFromToken = config.chain_token?.[fromChainId]?.token.find(el => + compareAddresses(el.token.address, fromToken.address) + ); + + const supportedToToken = config.chain_token?.[toChainId]?.token.find(el => + compareAddresses(el.token.address, toToken.address) + ); + + if (!supportedToToken) { + throw new RubicSdkError('Not supported tokens'); + } + + const possibleTransitToken = config.chain_token?.[fromChainId]?.token.find( + el => el.token.symbol === supportedToToken!.token.symbol + ); + + return { + supportedFromToken, + supportedToToken, + address: config.chains.find(chain => chain.id === fromChainId)!.contract_addr, + isBridge: supportedFromToken?.token.symbol === supportedToToken?.token.symbol, + possibleTransitToken + }; + } + + private async getEstimates( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions, + config: CelerConfig + ): Promise<{ amount: string; maxSlippage: number; fee: string }> { + let tokenSymbol = fromToken.symbol; + const useTestnet = BlockchainsInfo.isTestBlockchainName(fromToken.blockchain); + if (config.isBridge) { + tokenSymbol = config.supportedFromToken?.token.symbol || tokenSymbol; + } + const requestParams: CbridgeEstimateAmountRequest = { + src_chain_id: blockchainId[fromToken.blockchain], + dst_chain_id: blockchainId[toToken.blockchain], + token_symbol: tokenSymbol, + usr_addr: options?.receiverAddress || this.getWalletAddress(fromToken.blockchain), + slippage_tolerance: Number((options.slippageTolerance * 1_000_000).toFixed(0)), + amt: fromToken.stringWeiAmount + }; + const { estimated_receive_amt, max_slippage, base_fee } = + await CbridgeCrossChainApiService.fetchEstimateAmount(requestParams, { useTestnet }); + return { amount: estimated_receive_amt, maxSlippage: max_slippage, fee: base_fee }; + } + + private async getOnChainTrade( + from: PriceTokenAmount, + _availableDexes: string[], + slippageTolerance: number, + transitTokenAddress: string + ): Promise { + const fromBlockchain = from.blockchain as CbridgeCrossChainSupportedBlockchain; + + const dexes = Object.values(typedTradeProviders[fromBlockchain]).filter( + dex => dex.supportReceiverAddress + ); + const to = await PriceToken.createToken({ + address: transitTokenAddress, + blockchain: fromBlockchain + }); + const onChainTrades = ( + await Promise.allSettled( + dexes.map(dex => + dex.calculate(from, to, { + slippageTolerance, + gasCalculation: 'disabled', + useProxy: false + }) + ) + ) + ) + .filter(value => value.status === 'fulfilled') + .map(value => (value as PromiseFulfilledResult).value) + .sort((a, b) => b.to.tokenAmount.comparedTo(a.to.tokenAmount)); + + if (!onChainTrades.length) { + return null; + } + return onChainTrades[0]!; + } + + private async getMinMaxAmountsErrors( + fromToken: PriceTokenAmount, + _feeInfo: FeeInfo + ): Promise { + try { + const fromBlockchain = fromToken.blockchain as CbridgeCrossChainSupportedBlockchain; + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + + const fromTokenAddress = fromToken.isNative + ? wrappedNativeTokensList[fromBlockchain]!.address + : fromToken.address; + + const minAmountString = await web3Public.callContractMethod( + cbridgeContractAddress[fromBlockchain].providerRouter, + cbridgeContractAbi, + 'minSend', + [fromTokenAddress] + ); + const minAmount = new BigNumber(minAmountString); + if (minAmount.gte(fromToken.stringWeiAmount)) { + return new MinAmountError( + Web3Pure.fromWei(minAmount, fromToken.decimals), + fromToken.symbol + ); + } + + const maxAmountString = await web3Public.callContractMethod( + cbridgeContractAddress[fromBlockchain].providerRouter, + cbridgeContractAbi, + 'maxSend', + [fromTokenAddress] + ); + const maxAmount = new BigNumber(maxAmountString); + if (maxAmount.lt(fromToken.stringWeiAmount)) { + return new MaxAmountError( + Web3Pure.fromWei(maxAmount, fromToken.decimals), + fromToken.symbol + ); + } + } catch { + return undefined; + } + return undefined; + } + + protected async getFeeInfo( + fromBlockchain: CbridgeCrossChainSupportedBlockchain, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } + + protected async getRoutePath( + from: PriceTokenAmount, + transit: PriceTokenAmount, + to: PriceTokenAmount, + onChainTrade: EvmOnChainTrade | null + ): Promise { + const routePath: RubicStep[] = []; + if (onChainTrade) { + routePath.push({ + type: 'on-chain', + path: [from, transit], + provider: onChainTrade.type + }); + } + routePath.push({ + type: 'cross-chain', + path: [transit, to], + provider: CROSS_CHAIN_TRADE_TYPE.CELER_BRIDGE + }); + return routePath; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-trade.ts new file mode 100644 index 0000000..b6e95fc --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-trade.ts @@ -0,0 +1,284 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { cbridgeContractAbi } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-contract-abi'; +import { cbridgeContractAddress } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-contract-address'; +import { CbridgeCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-supported-blockchains'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { OnChainSubtype } from 'src/features/cross-chain/calculation-manager/providers/common/models/on-chain-subtype'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +export class CbridgeCrossChainTrade extends EvmCrossChainTrade { + /** @internal */ + public static async getGasData( + from: PriceTokenAmount, + toToken: PriceTokenAmount, + onChainTrade: EvmOnChainTrade | null, + feeInfo: FeeInfo, + maxSlippage: number, + celerContractAddress: string, + providerAddress: string, + receiverAddress: string + ): Promise { + try { + const trade = new CbridgeCrossChainTrade( + { + from, + to: toToken, + gasData: null, + priceImpact: 0, + slippage: 0, + feeInfo: feeInfo!, + maxSlippage, + contractAddress: celerContractAddress, + transitMinAmount: new BigNumber(0), + onChainTrade: onChainTrade + }, + providerAddress || EvmWeb3Pure.EMPTY_ADDRESS, + [] + ); + + return getCrossChainGasData(trade, receiverAddress); + } catch (_err) { + return null; + } + } + + public readonly type = CROSS_CHAIN_TRADE_TYPE.CELER_BRIDGE; + + public readonly isAggregator = false; + + public readonly bridgeType = BRIDGE_TYPE.CELER_BRIDGE; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly priceImpact: number | null; + + public readonly gasData: GasData | null; + + private get fromBlockchain(): CbridgeCrossChainSupportedBlockchain { + return this.from.blockchain as CbridgeCrossChainSupportedBlockchain; + } + + protected get fromContractAddress(): string { + return this.isProxyTrade + ? rubicProxyContractAddress[this.fromBlockchain].gateway + : cbridgeContractAddress[this.fromBlockchain].providerGateway; + } + + public readonly feeInfo: FeeInfo; + + private readonly slippage: number; + + private readonly maxSlippage: number; + + private readonly celerContractAddress: string; + + public readonly onChainSubtype: OnChainSubtype; + + public readonly onChainTrade: EvmOnChainTrade | null; + + protected get methodName(): string { + return this.onChainTrade + ? 'swapAndStartBridgeTokensViaGenericCrossChain' + : 'startBridgeTokensViaGenericCrossChain'; + } + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + gasData: GasData | null; + priceImpact: number | null; + slippage: number; + feeInfo: FeeInfo; + maxSlippage: number; + contractAddress: string; + transitMinAmount: BigNumber; + onChainTrade: EvmOnChainTrade | null; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.gasData = crossChainTrade.gasData; + this.priceImpact = crossChainTrade.priceImpact; + this.slippage = crossChainTrade.slippage; + this.toTokenAmountMin = crossChainTrade.to.tokenAmount.multipliedBy( + 1 - crossChainTrade.maxSlippage / 10_000_000 + ); + this.feeInfo = crossChainTrade.feeInfo; + this.maxSlippage = crossChainTrade.maxSlippage; + this.celerContractAddress = crossChainTrade.contractAddress; + + this.onChainSubtype = crossChainTrade.onChainTrade + ? { from: crossChainTrade.onChainTrade.type, to: undefined } + : { from: undefined, to: undefined }; + this.onChainTrade = crossChainTrade.onChainTrade; + } + + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + await this.checkTradeErrors(); + await this.checkAllowanceAndApprove(options); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + // eslint-disable-next-line no-useless-catch + try { + const { data, to, value } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options.receiverAddress || this.walletAddress + ); + + await this.web3Private.trySendTransaction(to, { + data, + value, + onTransactionHash, + gas: gasLimit, + gasPriceOptions + }); + + return transactionHash!; + } catch (err) { + throw err; + } + } + + public async getContractParams(options: GetContractParamsOptions): Promise { + const receiverAddress = options?.receiverAddress || this.walletAddress; + const { + data, + to, + value: providerValue + } = await this.setTransactionConfig(false, options?.useCacheData || false, receiverAddress); + + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData(options, { + walletAddress: this.walletAddress, + fromTokenAmount: this.from, + toTokenAmount: this.to, + srcChainTrade: this.onChainTrade, + providerAddress: this.providerAddress, + type: `native:${this.type}`, + fromAddress: this.walletAddress + }); + const swapData = + this.onChainTrade && + (await ProxyCrossChainEvmTrade.getSwapData(options, { + walletAddress: this.walletAddress, + contractAddress: rubicProxyContractAddress[this.from.blockchain].router, + fromTokenAmount: this.from, + toTokenAmount: this.onChainTrade.to, + onChainEncodeFn: this.onChainTrade.encode.bind(this.onChainTrade) + })); + + const providerData = await ProxyCrossChainEvmTrade.getGenericProviderData( + to, + data!, + this.fromBlockchain, + to, + '0' + ); + + const methodArguments = swapData + ? [bridgeData, swapData, providerData] + : [bridgeData, providerData]; + + const value = this.getSwapValue( + new BigNumber(this.from.isNative ? this.from.stringWeiAmount : 0).plus( + providerValue?.toString() + ) + ); + + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + return fromUsd.dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact ?? null, + slippage: this.maxSlippage / 10_000, + routePath: this.routePath + }; + } + + protected async getTransactionConfigAndAmount(receiverAddress: string): Promise<{ + config: EvmEncodeConfig; + amount: string; + }> { + const params: (string | number)[] = [receiverAddress]; + const transitToken = this.onChainTrade ? this.onChainTrade.to : this.from; + if (!transitToken.isNative) { + params.push(transitToken.address); + } + params.push( + transitToken.stringWeiAmount, + blockchainId[this.to.blockchain], + Date.now(), + this.maxSlippage + ); + const encode = EvmWeb3Pure.encodeMethodCall( + cbridgeContractAddress[this.fromBlockchain].providerRouter, + cbridgeContractAbi, + transitToken.isNative ? 'sendNative' : 'send', + params, + transitToken.isNative ? this.from.stringWeiAmount : '0' + ); + return { + config: { data: encode.data, to: encode.to, value: encode.value }, + amount: this.to.stringWeiAmount + }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-contract-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-contract-abi.ts new file mode 100644 index 0000000..f759e68 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-contract-abi.ts @@ -0,0 +1,196 @@ +import { AbiItem } from 'web3-utils'; + +export const cbridgeContractAbi: AbiItem[] = [ + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'bytes32', + name: 'transferId', + type: 'bytes32' + }, + { + indexed: false, + internalType: 'address', + name: 'sender', + type: 'address' + }, + { + indexed: false, + internalType: 'address', + name: 'receiver', + type: 'address' + }, + { + indexed: false, + internalType: 'address', + name: 'token', + type: 'address' + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256' + }, + { + indexed: false, + internalType: 'uint64', + name: 'dstChainId', + type: 'uint64' + }, + { + indexed: false, + internalType: 'uint64', + name: 'nonce', + type: 'uint64' + }, + { + indexed: false, + internalType: 'uint32', + name: 'maxSlippage', + type: 'uint32' + } + ], + name: 'Send', + type: 'event' + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + name: 'maxSend', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + name: 'minSend', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_receiver', + type: 'address' + }, + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'uint256', + name: '_amount', + type: 'uint256' + }, + { + internalType: 'uint64', + name: '_dstChainId', + type: 'uint64' + }, + { + internalType: 'uint64', + name: '_nonce', + type: 'uint64' + }, + { + internalType: 'uint32', + name: '_maxSlippage', + type: 'uint32' + } + ], + name: 'send', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_receiver', + type: 'address' + }, + { + internalType: 'uint256', + name: '_amount', + type: 'uint256' + }, + { + internalType: 'uint64', + name: '_dstChainId', + type: 'uint64' + }, + { + internalType: 'uint64', + name: '_nonce', + type: 'uint64' + }, + { + internalType: 'uint32', + name: '_maxSlippage', + type: 'uint32' + } + ], + name: 'sendNative', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'bytes', + name: '_wdmsg', + type: 'bytes' + }, + { + internalType: 'bytes[]', + name: '_sigs', + type: 'bytes[]' + }, + { + internalType: 'address[]', + name: '_signers', + type: 'address[]' + }, + { + internalType: 'uint256[]', + name: '_powers', + type: 'uint256[]' + } + ], + name: 'withdraw', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-contract-address.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-contract-address.ts new file mode 100644 index 0000000..1168610 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-contract-address.ts @@ -0,0 +1,74 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { CbridgeCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-supported-blockchains'; +import { UniversalContract } from 'src/features/cross-chain/calculation-manager/providers/common/models/universal-contract'; + +export const cbridgeContractAddress: Record< + CbridgeCrossChainSupportedBlockchain, + UniversalContract +> = { + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: { + providerGateway: '0xdd90E5E87A2081Dcf0391920868eBc2FFB81a1aF', + providerRouter: '0xdd90E5E87A2081Dcf0391920868eBc2FFB81a1aF', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + }, + [BLOCKCHAIN_NAME.POLYGON]: { + providerGateway: '0x88DCDC47D2f83a99CF0000FDF667A468bB958a78', + providerRouter: '0x88DCDC47D2f83a99CF0000FDF667A468bB958a78', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + }, + [BLOCKCHAIN_NAME.AVALANCHE]: { + providerGateway: '0xef3c714c9425a8F3697A9C969Dc1af30ba82e5d4', + providerRouter: '0xef3c714c9425a8F3697A9C969Dc1af30ba82e5d4', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + }, + [BLOCKCHAIN_NAME.ETHEREUM]: { + providerGateway: '0x5427FEFA711Eff984124bFBB1AB6fbf5E3DA1820', + providerRouter: '0x5427FEFA711Eff984124bFBB1AB6fbf5E3DA1820', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + }, + [BLOCKCHAIN_NAME.FANTOM]: { + providerGateway: '0x374B8a9f3eC5eB2D97ECA84Ea27aCa45aa1C57EF', + providerRouter: '0x374B8a9f3eC5eB2D97ECA84Ea27aCa45aa1C57EF', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + }, + [BLOCKCHAIN_NAME.ARBITRUM]: { + providerGateway: '0x1619DE6B6B20eD217a58d00f37B9d47C7663feca', + providerRouter: '0x1619DE6B6B20eD217a58d00f37B9d47C7663feca', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + }, + [BLOCKCHAIN_NAME.AURORA]: { + providerGateway: '0x841ce48F9446C8E281D3F1444cB859b4A6D0738C', + providerRouter: '0x841ce48F9446C8E281D3F1444cB859b4A6D0738C', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + }, + [BLOCKCHAIN_NAME.OPTIMISM]: { + providerGateway: '0x9D39Fc627A6d9d9F8C831c16995b209548cc3401', + providerRouter: '0x9D39Fc627A6d9d9F8C831c16995b209548cc3401', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + }, + [BLOCKCHAIN_NAME.ASTAR_EVM]: { + providerGateway: '0x841ce48F9446C8E281D3F1444cB859b4A6D0738C', + providerRouter: '0x841ce48F9446C8E281D3F1444cB859b4A6D0738C', + rubicRouter: '0x841ce48F9446C8E281D3F1444cB859b4A6D0738C' + }, + [BLOCKCHAIN_NAME.FUJI]: { + providerGateway: '0xe95E3a9f1a45B5EDa71781448F6047d7B7e31cbF', + providerRouter: '0xe95E3a9f1a45B5EDa71781448F6047d7B7e31cbF', + rubicRouter: '0x841ce48F9446C8E281D3F1444cB859b4A6D0738C' + }, + [BLOCKCHAIN_NAME.MUMBAI]: { + providerGateway: '0x841ce48F9446C8E281D3F1444cB859b4A6D0738C', + providerRouter: '0x841ce48F9446C8E281D3F1444cB859b4A6D0738C', + rubicRouter: '0x841ce48F9446C8E281D3F1444cB859b4A6D0738C' + }, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN_TESTNET]: { + providerGateway: '0xf89354F314faF344Abd754924438bA798E306DF2', + providerRouter: '0xf89354F314faF344Abd754924438bA798E306DF2', + rubicRouter: '0x841ce48F9446C8E281D3F1444cB859b4A6D0738C' + }, + [BLOCKCHAIN_NAME.GOERLI]: { + providerGateway: '0x358234B325EF9eA8115291A8b81b7d33A2Fa762D', + providerRouter: '0x358234B325EF9eA8115291A8b81b7d33A2Fa762D', + rubicRouter: '0x841ce48F9446C8E281D3F1444cB859b4A6D0738C' + } +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-supported-blockchains.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-supported-blockchains.ts new file mode 100644 index 0000000..c575d26 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-supported-blockchains.ts @@ -0,0 +1,19 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const cbridgeSupportedBlockchains = [ + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.AURORA, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.ASTAR_EVM, + BLOCKCHAIN_NAME.GOERLI, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN_TESTNET, + BLOCKCHAIN_NAME.MUMBAI, + BLOCKCHAIN_NAME.FUJI +] as const; + +export type CbridgeCrossChainSupportedBlockchain = (typeof cbridgeSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-chain-token-info.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-chain-token-info.ts new file mode 100644 index 0000000..9a185b1 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-chain-token-info.ts @@ -0,0 +1,24 @@ +interface Token { + readonly symbol: string; + readonly address: string; + readonly decimal: number; + readonly xfer_disabled: boolean; +} + +export interface TokenInfo { + readonly token: Token; + readonly name: string; + readonly icon: string; + readonly delay_period: number; + readonly delay_threshold: string; + readonly inbound_epoch_cap: string; + readonly inbound_lmt: string; + readonly liq_add_disabled: boolean; + readonly liq_agg_rm_src_disabled: boolean; + readonly liq_rm_disabled: boolean; + readonly transfer_disabled: boolean; +} + +export interface CbridgeChainTokenInfo { + readonly token: TokenInfo[]; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-chain.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-chain.ts new file mode 100644 index 0000000..7331ceb --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-chain.ts @@ -0,0 +1,10 @@ +export interface CbridgeChain { + // @ts-ignore + readonly id: number; + readonly name: string; + readonly icon: string; + readonly block_delay: string; + readonly gas_token_symbol: string; + readonly explore_url: string; + readonly contract_addr: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-estimate-amount-request.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-estimate-amount-request.ts new file mode 100644 index 0000000..3b75a39 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-estimate-amount-request.ts @@ -0,0 +1,9 @@ +export interface CbridgeEstimateAmountRequest { + readonly src_chain_id: number; + readonly dst_chain_id: number; + readonly token_symbol: string; + readonly usr_addr?: string; + readonly slippage_tolerance: number; + readonly amt: string; + readonly is_pegged?: boolean; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-estimate-amount-response.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-estimate-amount-response.ts new file mode 100644 index 0000000..16c2a80 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-estimate-amount-response.ts @@ -0,0 +1,9 @@ +export interface CbridgeEstimateAmountResponse { + readonly eq_value_token_amt: string; + readonly bridge_rate: number; + readonly perc_fee: string; + readonly base_fee: string; + readonly slippage_tolerance: number; + readonly max_slippage: number; + readonly estimated_receive_amt: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-status-response.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-status-response.ts new file mode 100644 index 0000000..8964f3f --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-status-response.ts @@ -0,0 +1,73 @@ +export const TRANSFER_HISTORY_STATUS = { + TRANSFER_UNKNOWN: 'TRANSFER_UNKNOWN', + TRANSFER_SUBMITTING: 'TRANSFER_SUBMITTING', + TRANSFER_FAILED: 'TRANSFER_FAILED', + TRANSFER_WAITING_FOR_SGN_CONFIRMATION: 'TRANSFER_WAITING_FOR_SGN_CONFIRMATION', + TRANSFER_WAITING_FOR_FUND_RELEASE: 'TRANSFER_WAITING_FOR_FUND_RELEASE', + TRANSFER_COMPLETED: 'TRANSFER_COMPLETED', + TRANSFER_TO_BE_REFUNDED: 'TRANSFER_TO_BE_REFUNDED', + TRANSFER_REQUESTING_REFUND: 'TRANSFER_REQUESTING_REFUND', + TRANSFER_REFUND_TO_BE_CONFIRMED: 'TRANSFER_REFUND_TO_BE_CONFIRMED', + TRANSFER_CONFIRMING_YOUR_REFUND: 'TRANSFER_CONFIRMING_YOUR_REFUND', + TRANSFER_REFUNDED: 'TRANSFER_REFUNDED', + TRANSFER_DELAYED: 'TRANSFER_DELAYED' +} as const; + +export const TRANSFER_HISTORY_STATUS_CODE: Record = { + 0: 'TRANSFER_UNKNOWN', + 1: 'TRANSFER_SUBMITTING', + 2: 'TRANSFER_FAILED', + 3: 'TRANSFER_WAITING_FOR_SGN_CONFIRMATION', + 4: 'TRANSFER_WAITING_FOR_FUND_RELEASE', + 5: 'TRANSFER_COMPLETED', + 6: 'TRANSFER_TO_BE_REFUNDED', + 7: 'TRANSFER_REQUESTING_REFUND', + 8: 'TRANSFER_REFUND_TO_BE_CONFIRMED', + 9: 'TRANSFER_CONFIRMING_YOUR_REFUND', + 10: 'TRANSFER_REFUNDED', + 11: 'TRANSFER_DELAYED' +}; + +export type TransferHistoryStatus = + (typeof TRANSFER_HISTORY_STATUS)[keyof typeof TRANSFER_HISTORY_STATUS]; + +export const XFER_STATUS = { + UNKNOWN: 'UNKNOWN', + OK_TO_RELAY: 'OK_TO_RELAY', + SUCCESS: 'SUCCESS', + BAD_LIQUIDITY: 'BAD_LIQUIDITY', + BAD_SLIPPAGE: 'BAD_SLIPPAGE', + BAD_TOKEN: 'BAD_TOKEN', + REFUND_REQUESTED: 'REFUND_REQUESTED', + REFUND_DONE: 'REFUND_DONE', + BAD_XFER_DISABLED: 'BAD_XFER_DISABLED', + BAD_DEST_CHAIN: 'BAD_DEST_CHAIN' +} as const; + +export const XFER_STATUS_CODE: Record = { + 0: 'UNKNOWN', + 1: 'OK_TO_RELAY', + 2: 'SUCCESS', + 3: 'BAD_LIQUIDITY', + 4: 'BAD_SLIPPAGE', + 5: 'BAD_TOKEN', + 6: 'REFUND_REQUESTED', + 7: 'REFUND_DONE', + 8: 'BAD_XFER_DISABLED', + 9: 'BAD_DEST_CHAIN' +}; + +export type XferStatus = (typeof XFER_STATUS)[keyof typeof XFER_STATUS]; + +export interface CbridgeStatusResponse { + readonly err: null | string; + readonly status: number; + readonly wd_onchain: string | null; + readonly sorted_sigs: string[]; + readonly signers: string[]; + readonly powers: string[]; + readonly refund_reason: number; + readonly block_delay: number; + readonly src_block_tx_link: string; + readonly dst_block_tx_link: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-transfer-configs-response.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-transfer-configs-response.ts new file mode 100644 index 0000000..fdd65f2 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-transfer-configs-response.ts @@ -0,0 +1,10 @@ +import { CbridgeChain } from 'src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-chain'; +import { CbridgeChainTokenInfo } from 'src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-chain-token-info'; +import { PeggedPairConfig } from 'src/features/cross-chain/calculation-manager/providers/cbridge/models/pegged-pair-config'; + +export interface CbridgeTransferConfigsResponse { + readonly chains: CbridgeChain[]; + readonly chain_token: { [P: number]: CbridgeChainTokenInfo }; + readonly farming_reward_contract_addr: string; + readonly pegged_pair_configs: PeggedPairConfig[]; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/pegged-pair-config.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/pegged-pair-config.ts new file mode 100644 index 0000000..ffbd8dc --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/cbridge/models/pegged-pair-config.ts @@ -0,0 +1,12 @@ +import { CbridgeChainTokenInfo } from 'src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-chain-token-info'; + +export interface PeggedPairConfig { + readonly org_chain_id: string; + readonly org_token: CbridgeChainTokenInfo; + readonly pegged_chain_id: string; + readonly pegged_token: CbridgeChainTokenInfo; + readonly pegged_deposit_contract_addr: string; + readonly pegged_burn_contract_addr: string; + readonly vault_version: number; + readonly bridge_version: number; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/changenow-cross-chain-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/changenow-cross-chain-provider.ts new file mode 100644 index 0000000..5179784 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/changenow-cross-chain-provider.ts @@ -0,0 +1,384 @@ +import BigNumber from 'bignumber.js'; +import { MaxAmountError, MinAmountError, NotSupportedTokensError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, Token } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { Web3PublicSupportedBlockchain } from 'src/core/blockchain/web3-public-service/models/web3-public-storage'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CbridgeCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-supported-blockchains'; +import { ChangenowCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/changenow-cross-chain-trade'; +import { + changenowApiBlockchain, + ChangenowCrossChainSupportedBlockchain +} from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; +import { + ChangenowProxySupportedBlockchain, + changenowProxySupportedBlockchains +} from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-proxy-supported-blockchains'; +import { nativeTokensData } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/native-addresses'; +import { + ChangenowCurrenciesResponse, + ChangenowCurrency +} from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-currencies-api'; +import { ChangenowTrade } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-trade'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { typedTradeProviders } from 'src/features/on-chain/calculation-manager/constants/trade-providers/typed-trade-providers'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +import { ChangeNowCrossChainApiService } from './services/changenow-cross-chain-api-service'; + +export class ChangenowCrossChainProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.CHANGENOW; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is ChangenowCrossChainSupportedBlockchain { + return Object.keys(changenowApiBlockchain).includes(blockchain); + } + + public isSupportedProxyBlockchain( + blockchain: BlockchainName + ): blockchain is ChangenowProxySupportedBlockchain { + return changenowProxySupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + // eslint-disable-next-line complexity + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise> { + const fromBlockchain = from.blockchain; + const toBlockchain = toToken.blockchain; + + const useProxy = + this.isSupportedProxyBlockchain(fromBlockchain) && + (options?.useProxy?.[this.type] || false); + + if ( + !this.areSupportedBlockchains(fromBlockchain, toBlockchain) || + (!options.changenowFullyEnabled && !BlockchainsInfo.isEvmBlockchainName(fromBlockchain)) + ) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + const { fromCurrency, toCurrency, transitCurrency, nativeCurrency } = + await this.getChangenowCurrencies( + from as Token, + toToken as Token + ); + if (!toCurrency || (!fromCurrency && !transitCurrency && !nativeCurrency)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + const feeInfo = await this.getFeeInfo( + fromBlockchain as unknown as EvmBlockchainName, + options.providerAddress, + from, + useProxy + ); + const fromWithoutFee = getFromWithoutFee(from, feeInfo.rubicProxy?.platformFee?.percent); + + let onChainTrade: EvmOnChainTrade | null = null; + let transitMinAmount = fromWithoutFee.tokenAmount; + let transitFromToken = fromWithoutFee; + + if (!fromCurrency) { + if (!useProxy) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + onChainTrade = transitCurrency + ? await this.getOnChainTrade( + fromWithoutFee, + [], + 0.02, + transitCurrency.tokenContract! + ) + : null; + if (!onChainTrade) { + onChainTrade = await this.getOnChainTrade( + fromWithoutFee, + [], + options.slippageTolerance, + nativeTokensList[fromBlockchain].address + ); + } + if (!onChainTrade) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + transitMinAmount = onChainTrade.toTokenAmountMin.tokenAmount; + transitFromToken = onChainTrade.to; + } + const transit = onChainTrade ? transitCurrency! || nativeCurrency! : fromCurrency!; + + try { + const toAmount = await this.getToAmount(transit, toCurrency, transitMinAmount); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: toAmount + }) as PriceTokenAmount; + const changenowTrade: ChangenowTrade = { + from: from as PriceTokenAmount, + to, + toTokenAmountMin: toAmount, + fromCurrency: transit, + toCurrency, + feeInfo, + gasData: null, + onChainTrade + }; + const gasData = + options.gasCalculation === 'enabled' + ? await ChangenowCrossChainTrade.getGasData( + changenowTrade, + options.providerAddress, + options.receiverAddress + ) + : null; + const trade = new ChangenowCrossChainTrade( + { ...changenowTrade, gasData }, + options.providerAddress, + await this.getRoutePath(from, to) + ); + + const error = await this.checkMinMaxAmounts(transitFromToken, transit, toCurrency); + if (error) { + return { + trade, + error, + tradeType: this.type + }; + } + + return { trade, tradeType: this.type }; + } catch { + const error = await this.checkMinMaxAmounts(transitFromToken, transit, toCurrency); + if (error) { + return { + trade: null, + error, + tradeType: this.type + }; + } + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + } + + private async getChangenowCurrencies( + from: Token, + to: Token + ): Promise<{ + fromCurrency?: ChangenowCurrency; + toCurrency?: ChangenowCurrency; + nativeCurrency?: ChangenowCurrency; + transitCurrency?: ChangenowCurrency; + }> { + const currencies = await ChangeNowCrossChainApiService.getCurrencies(); + + const nativeToken = nativeTokensList[ + from.blockchain + ] as Token; + + return { + fromCurrency: this.getCurrency(currencies, from), + toCurrency: this.getCurrency(currencies, to), + nativeCurrency: this.getCurrency(currencies, nativeToken), + transitCurrency: this.getTransitCurrency(currencies, from.blockchain) + }; + } + + private async checkMinMaxAmounts( + from: PriceTokenAmount, + fromCurrency: ChangenowCurrency, + toCurrency: ChangenowCurrency + ): Promise { + const { minAmount, maxAmount } = await this.getMinMaxRange(fromCurrency, toCurrency); + if (minAmount.gt(from.tokenAmount)) { + return new MinAmountError(minAmount, from.symbol); + } + if (maxAmount?.lt(from.tokenAmount)) { + return new MaxAmountError(maxAmount, from.symbol); + } + return null; + } + + private isNativeAddress( + token: Token, + currency: ChangenowCurrency + ): boolean { + return nativeTokensData.some( + nativeTokenData => + token.blockchain === nativeTokenData.blockchain && + compareAddresses(nativeTokenData.address, token.address) && + currency.network === changenowApiBlockchain[nativeTokenData.blockchain] && + currency.ticker === nativeTokenData.ticker + ); + } + + private async getToAmount( + fromCurrency: ChangenowCurrency, + toCurrency: ChangenowCurrency, + fromAmount: BigNumber + ): Promise { + const res = await ChangeNowCrossChainApiService.getQuoteTx({ + fromCurrency: fromCurrency.ticker, + toCurrency: toCurrency.ticker, + fromAmount: fromAmount.toFixed(), + fromNetwork: fromCurrency.network, + toNetwork: toCurrency.network + }); + + return new BigNumber(res.toAmount); + } + + private async getMinMaxRange( + fromCurrency: ChangenowCurrency, + toCurrency: ChangenowCurrency + ): Promise<{ minAmount: BigNumber; maxAmount: BigNumber | null }> { + const response = await ChangeNowCrossChainApiService.getMinMaxRange({ + fromCurrency: fromCurrency.ticker, + toCurrency: toCurrency.ticker, + fromNetwork: fromCurrency.network, + toNetwork: toCurrency.network + }); + + return { + minAmount: new BigNumber(response.minAmount), + maxAmount: response.maxAmount ? new BigNumber(response.maxAmount) : null + }; + } + + protected override async getFeeInfo( + fromBlockchain: Web3PublicSupportedBlockchain, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } + + private async getOnChainTrade( + from: PriceTokenAmount, + _availableDexes: string[], + slippageTolerance: number, + transitTokenAddress: string + ): Promise { + const fromBlockchain = from.blockchain as CbridgeCrossChainSupportedBlockchain; + + const dexes = Object.values(typedTradeProviders[fromBlockchain]).filter( + dex => dex.supportReceiverAddress + ); + const to = await PriceToken.createToken({ + address: transitTokenAddress, + blockchain: fromBlockchain + }); + const onChainTrades = ( + await Promise.allSettled( + dexes.map(dex => + dex.calculate(from, to, { + slippageTolerance, + gasCalculation: 'disabled', + useProxy: false + }) + ) + ) + ) + .filter(value => value.status === 'fulfilled') + .map(value => (value as PromiseFulfilledResult).value) + .sort((a, b) => b.to.tokenAmount.comparedTo(a.to.tokenAmount)); + + if (!onChainTrades.length) { + return null; + } + return onChainTrades[0]!; + } + + private getCurrency( + currencies: ChangenowCurrenciesResponse, + token: Token + ): ChangenowCurrency | undefined { + if (!token) { + return undefined; + } + const apiBlockchain = + token.blockchain === BLOCKCHAIN_NAME.AVALANCHE && + EvmWeb3Pure.isNativeAddress(token.address) + ? 'cchain' + : changenowApiBlockchain[token.blockchain]; + + return currencies.find( + currency => + currency.network === apiBlockchain && + ((currency.tokenContract === null && + (token.isNative || this.isNativeAddress(token, currency))) || + (currency.tokenContract && + compareAddresses(token.address, currency.tokenContract))) + ); + } + + private getTransitCurrency( + currencies: ChangenowCurrenciesResponse, + blockchain: ChangenowCrossChainSupportedBlockchain + ): ChangenowCurrency | undefined { + const apiBlockchain = changenowApiBlockchain[blockchain]; + return currencies.find( + currency => currency.network === apiBlockchain && currency.tokenContract !== null + ); + } + + protected async getRoutePath( + from: PriceTokenAmount, + to: PriceTokenAmount + ): Promise { + return [ + { + type: 'cross-chain', + provider: CROSS_CHAIN_TRADE_TYPE.CHANGENOW, + path: [from, to] + } + ]; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/changenow-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/changenow-cross-chain-trade.ts new file mode 100644 index 0000000..53cec09 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/changenow-cross-chain-trade.ts @@ -0,0 +1,462 @@ +import BigNumber from 'bignumber.js'; +import { FailedToCheckForTransactionReceiptError, RubicSdkError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { getGasOptions } from 'src/common/utils/options'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { ERC20_TOKEN_ABI } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/constants/erc-20-token-abi'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { ChangenowCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; +import { ChangenowCurrency } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-currencies-api'; +import { ChangenowPaymentInfo } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-payment-info'; +import { ChangenowTrade } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-trade'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { MarkRequired } from 'ts-essentials'; +import { TransactionConfig } from 'web3-core'; + +import { convertGasDataToBN } from '../../utils/convert-gas-price'; +import { TradeInfo } from '../common/models/trade-info'; +import { ChangenowSwapRequestBody, ChangenowSwapResponse } from './models/changenow-swap.api'; +import { ChangeNowCrossChainApiService } from './services/changenow-cross-chain-api-service'; + +export class ChangenowCrossChainTrade extends EvmCrossChainTrade { + private paymentInfo: ChangenowSwapResponse | null = null; + + /** + * used in rubic-app to send as changenow_id to backend + */ + public get changenowId(): string { + return this.paymentInfo ? this.paymentInfo.id : ''; + } + + /** @internal */ + public static async getGasData( + changenowTrade: ChangenowTrade, + providerAddress: string, + receiverAddress?: string + ): Promise { + const fromBlockchain = changenowTrade.from.blockchain; + const walletAddress = + BlockchainsInfo.isEvmBlockchainName(fromBlockchain) && + Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address; + if (!walletAddress) { + return null; + } + + try { + const { contractAddress, contractAbi, methodName, methodArguments, value } = + await new ChangenowCrossChainTrade( + changenowTrade, + providerAddress || EvmWeb3Pure.EMPTY_ADDRESS, + [] + ).getContractParams({ receiverAddress: receiverAddress || walletAddress }, true); + + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const [gasLimit, gasDetails] = await Promise.all([ + web3Public.getEstimatedGas( + contractAbi, + contractAddress, + methodName, + methodArguments, + walletAddress, + value + ), + convertGasDataToBN(await Injector.gasPriceApi.getGasPrice(fromBlockchain)) + ]); + + if (!gasLimit?.isFinite()) { + return null; + } + + const increasedGasLimit = Web3Pure.calculateGasMargin(gasLimit, 1.2); + return { + gasLimit: increasedGasLimit, + ...gasDetails + }; + } catch (_err) { + return null; + } + } + + protected get methodName(): string { + return this.onChainTrade + ? 'swapAndStartBridgeTokensViaTransfer' + : 'startBridgeTokensViaTransfer'; + } + + public readonly type = CROSS_CHAIN_TRADE_TYPE.CHANGENOW; + + public readonly isAggregator = false; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly gasData: GasData; + + public readonly feeInfo: FeeInfo; + + public readonly onChainSubtype; + + public readonly bridgeType = BRIDGE_TYPE.CHANGENOW; + + public readonly priceImpact: number | null; + + private readonly fromCurrency: ChangenowCurrency; + + private readonly toCurrency: ChangenowCurrency; + + private get transitToken(): PriceTokenAmount { + return this.onChainTrade ? this.onChainTrade.toTokenAmountMin : this.from; + } + + protected get fromContractAddress(): string { + if (this.isProxyTrade) { + return rubicProxyContractAddress[this.from.blockchain].gateway; + } + throw new RubicSdkError('No contract address for changenow provider'); + } + + protected get web3Private(): EvmWeb3Private { + if (!BlockchainsInfo.isEvmBlockchainName(this.from.blockchain)) { + throw new RubicSdkError('Cannot retrieve web3 private'); + } + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(this.from.blockchain); + } + + public readonly onChainTrade: EvmOnChainTrade | null; + + public get estimatedGas(): BigNumber | null { + if (!this.gasData) { + return null; + } + + if (this.gasData.baseFee && this.gasData.maxPriorityFeePerGas) { + return Web3Pure.fromWei(this.gasData.baseFee).plus( + Web3Pure.fromWei(this.gasData.maxPriorityFeePerGas) + ); + } + + if (this.gasData.gasPrice) { + return Web3Pure.fromWei(this.gasData.gasPrice).multipliedBy(this.gasData.gasLimit); + } + + return null; + } + + constructor(crossChainTrade: ChangenowTrade, providerAddress: string, routePath: RubicStep[]) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from as PriceTokenAmount; + this.to = crossChainTrade.to; + this.toTokenAmountMin = crossChainTrade.toTokenAmountMin; + + this.fromCurrency = crossChainTrade.fromCurrency; + this.toCurrency = crossChainTrade.toCurrency; + + this.feeInfo = crossChainTrade.feeInfo; + this.gasData = crossChainTrade.gasData; + + this.priceImpact = this.from.calculatePriceImpactPercent(this.to); + + this.onChainSubtype = crossChainTrade.onChainTrade + ? { from: crossChainTrade.onChainTrade.type, to: undefined } + : { from: undefined, to: undefined }; + this.onChainTrade = crossChainTrade.onChainTrade; + } + + public async swapDirect(options: SwapTransactionOptions = {}): Promise { + if (!BlockchainsInfo.isEvmBlockchainName(this.from.blockchain)) { + throw new RubicSdkError("For non-evm chains use 'getChangenowPostTrade' method"); + } + + await this.checkTradeErrors(); + await this.checkReceiverAddress( + options.receiverAddress, + !BlockchainsInfo.isEvmBlockchainName(this.to.blockchain), + CROSS_CHAIN_TRADE_TYPE.CHANGENOW + ); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + try { + await this.setTransactionConfig( + false, + options.useCacheData || false, + options?.receiverAddress || this.walletAddress + ); + if (!this.paymentInfo) { + throw new Error('Payin address is not set'); + } + + if (this.from.isNative) { + await this.web3Private.trySendTransaction(this.paymentInfo.payinAddress, { + value: this.from.weiAmount, + onTransactionHash, + gasPriceOptions + }); + } else { + await this.web3Private.tryExecuteContractMethod( + this.from.address, + ERC20_TOKEN_ABI, + 'transfer', + [this.paymentInfo.payinAddress, this.from.stringWeiAmount], + { + onTransactionHash, + gas: gasLimit, + gasPriceOptions + } + ); + } + + return transactionHash!; + } catch (err) { + if (err instanceof FailedToCheckForTransactionReceiptError) { + return transactionHash!; + } + throw err; + } + } + + public async getChangenowPostTrade(receiverAddress: string): Promise { + await this.setTransactionConfig(false, false, receiverAddress); + if (!this.paymentInfo) { + throw new Error('Payin address is not set'); + } + const extraField = this.paymentInfo.payinExtraIdName + ? { + name: this.paymentInfo.payinExtraIdName, + value: this.paymentInfo.payinExtraId + } + : null; + + return { + id: this.paymentInfo.id, + depositAddress: this.paymentInfo.payinAddress, + ...(extraField && { extraField }) + }; + } + + protected async getTransactionConfigAndAmount( + receiverAddress: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + const params: ChangenowSwapRequestBody = { + fromCurrency: this.fromCurrency.ticker, + toCurrency: this.toCurrency.ticker, + fromNetwork: this.fromCurrency.network, + toNetwork: this.toCurrency.network, + fromAmount: this.transitToken.tokenAmount.toFixed(), + address: receiverAddress, + flow: 'standard' + }; + + const res = await ChangeNowCrossChainApiService.getSwapTx(params); + const toAmountWei = Web3Pure.toWei(res.toAmount, this.to.decimals); + this.paymentInfo = res; + + const config: EvmEncodeConfig = { to: '', data: '', value: '' }; + + if (this.from.isNative) { + config.value = this.from.stringWeiAmount; + config.data = '0x'; + config.to = this.paymentInfo.payinAddress; + } else { + const encodedConfig = EvmWeb3Pure.encodeMethodCall( + this.from.address, + ERC20_TOKEN_ABI, + 'transfer', + [this.paymentInfo.payinAddress, this.from.stringWeiAmount], + '0' + ); + config.value = '0'; + config.to = this.from.address; + config.data = encodedConfig.data; + } + + return { config, amount: toAmountWei }; + } + + protected async getContractParams( + options: MarkRequired, + skipAmountChangeCheck?: boolean + ): Promise { + await this.setTransactionConfig( + skipAmountChangeCheck || false, + options.useCacheData || false, + options.receiverAddress + ); + if (!this.paymentInfo) { + throw new Error('Payin address is not set'); + } + + const toToken = this.to.clone({ address: EvmWeb3Pure.EMPTY_ADDRESS }); + + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData( + { + ...options, + receiverAddress: this.walletAddress + }, + { + walletAddress: this.walletAddress, + fromTokenAmount: this.from, + toTokenAmount: toToken, + srcChainTrade: this.onChainTrade, + providerAddress: this.providerAddress, + type: `native:${this.bridgeType}`, + fromAddress: this.walletAddress + } + ); + + const providerData = [this.paymentInfo.payinAddress]; + + const swapData = + this.onChainTrade && + (await ProxyCrossChainEvmTrade.getSwapData(options, { + walletAddress: this.walletAddress, + contractAddress: rubicProxyContractAddress[this.from.blockchain].router, + fromTokenAmount: this.from, + toTokenAmount: this.onChainTrade.to, + onChainEncodeFn: this.onChainTrade.encode.bind(this.onChainTrade) + })); + + const methodArguments = swapData + ? [bridgeData, swapData, providerData] + : [bridgeData, providerData]; + + const value = this.getSwapValue(); + + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + return fromUsd.dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact ?? null, + slippage: this.onChainTrade?.slippageTolerance + ? this.onChainTrade.slippageTolerance * 100 + : 0, + routePath: this.routePath + }; + } + + public async encode(options: EncodeTransactionOptions): Promise { + if (!BlockchainsInfo.isEvmBlockchainName(this.from.blockchain)) { + throw new RubicSdkError('Cannot encode trade for non-evm blockchain'); + } + + await this.checkFromAddress(options.fromAddress, true, CROSS_CHAIN_TRADE_TYPE.CHANGENOW); + await this.checkReceiverAddress( + options.receiverAddress, + !BlockchainsInfo.isEvmBlockchainName(this.to.blockchain), + CROSS_CHAIN_TRADE_TYPE.CHANGENOW + ); + + const { gasLimit } = options; + if (this.feeInfo?.rubicProxy?.fixedFee?.amount.gt(0)) { + const { contractAddress, contractAbi, methodName, methodArguments, value } = + await this.getContractParams({ + fromAddress: options.fromAddress, + receiverAddress: options.receiverAddress || options.fromAddress + }); + + return EvmWeb3Pure.encodeMethodCall( + contractAddress, + contractAbi, + methodName, + methodArguments, + value, + { + gas: gasLimit || this.gasData?.gasLimit.toFixed(0), + ...getGasOptions(options) + } + ); + } + return this.setTransactionConfig( + options?.skipAmountCheck || false, + options?.useCacheData || false, + options?.receiverAddress || this.walletAddress + ); + } + + public encodeApprove(): Promise { + throw new RubicSdkError('Cannot encode approve for changenow'); + } + + public async needApprove(): Promise { + if (this.isProxyTrade) { + return super.needApprove(); + } + return false; + } + + protected async setTransactionConfig( + skipAmountChangeCheck: boolean, + useCacheData: boolean, + receiverAddress?: string + ): Promise { + if (this.lastTransactionConfig && useCacheData) { + return this.lastTransactionConfig; + } + + const { config, amount } = await this.getTransactionConfigAndAmount( + receiverAddress || this.walletAddress + ); + this.lastTransactionConfig = config; + setTimeout(() => { + this.lastTransactionConfig = null; + }, 15_000); + + if (!skipAmountChangeCheck) { + this.checkAmountChange(amount, this.to.stringWeiAmount); + } + return config; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain.ts new file mode 100644 index 0000000..4307771 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain.ts @@ -0,0 +1,112 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const changeNowEvmSupportedBlockchain = { + [BLOCKCHAIN_NAME.ETHEREUM]: 'eth', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: 'bsc', + [BLOCKCHAIN_NAME.POLYGON]: 'matic', + [BLOCKCHAIN_NAME.ARBITRUM]: 'arbitrum', + [BLOCKCHAIN_NAME.BITGERT]: 'brise', + [BLOCKCHAIN_NAME.AVALANCHE]: 'avaxc', + [BLOCKCHAIN_NAME.CELO]: 'celo', + [BLOCKCHAIN_NAME.ETHEREUM_CLASSIC]: 'etc', + [BLOCKCHAIN_NAME.ETHEREUM_POW]: 'ethw', + [BLOCKCHAIN_NAME.FANTOM]: 'ftm', + [BLOCKCHAIN_NAME.IOTEX]: 'iotx', + [BLOCKCHAIN_NAME.KLAYTN]: 'klay', + [BLOCKCHAIN_NAME.ONTOLOGY]: 'ont', + [BLOCKCHAIN_NAME.OPTIMISM]: 'op', + [BLOCKCHAIN_NAME.OASIS]: 'rose', + [BLOCKCHAIN_NAME.SYSCOIN]: 'sys', + [BLOCKCHAIN_NAME.XDC]: 'xdc', + [BLOCKCHAIN_NAME.PULSECHAIN]: 'pulse', + [BLOCKCHAIN_NAME.BASE]: 'base', + [BLOCKCHAIN_NAME.ZK_SYNC]: 'zksync' +}; + +export type ChangenowEvmCrossChainSupportedBlockchain = + keyof typeof changeNowEvmSupportedBlockchain; + +export const changenowApiBlockchain = { + // Evm + ...changeNowEvmSupportedBlockchain, + // Non evm + [BLOCKCHAIN_NAME.THETA]: 'theta', + [BLOCKCHAIN_NAME.FLARE]: 'flr', + [BLOCKCHAIN_NAME.FILECOIN]: 'fil', + [BLOCKCHAIN_NAME.EOS]: 'eos', + [BLOCKCHAIN_NAME.BITCOIN_CASH]: 'bch', + [BLOCKCHAIN_NAME.BITCOIN]: 'btc', + [BLOCKCHAIN_NAME.ICP]: 'icp', + [BLOCKCHAIN_NAME.CARDANO]: 'ada', + [BLOCKCHAIN_NAME.AION]: 'aion', + [BLOCKCHAIN_NAME.ALGORAND]: 'algo', + [BLOCKCHAIN_NAME.APTOS]: 'apt', + [BLOCKCHAIN_NAME.ARDOR]: 'ardr', + [BLOCKCHAIN_NAME.ASTAR]: 'astr', + [BLOCKCHAIN_NAME.ARK]: 'ark', + [BLOCKCHAIN_NAME.COSMOS]: 'atom', + [BLOCKCHAIN_NAME.BAND_PROTOCOL]: 'band', + [BLOCKCHAIN_NAME.BITCOIN_DIAMOND]: 'bcd', + [BLOCKCHAIN_NAME.BSV]: 'bsv', + [BLOCKCHAIN_NAME.BITCOIN_GOLD]: 'btg', + [BLOCKCHAIN_NAME.CASPER]: 'cspr', + [BLOCKCHAIN_NAME.DASH]: 'dash', + [BLOCKCHAIN_NAME.DECRED]: 'dcr', + [BLOCKCHAIN_NAME.DIGI_BYTE]: 'dgb', + [BLOCKCHAIN_NAME.DIVI]: 'divi', + [BLOCKCHAIN_NAME.DOGECOIN]: 'doge', + [BLOCKCHAIN_NAME.POLKADOT]: 'dot', + [BLOCKCHAIN_NAME.MULTIVERS_X]: 'egld', + [BLOCKCHAIN_NAME.FIO_PROTOCOL]: 'fio', + [BLOCKCHAIN_NAME.FIRO]: 'firo', + [BLOCKCHAIN_NAME.FLOW]: 'flow', + [BLOCKCHAIN_NAME.HEDERA]: 'hbar', + [BLOCKCHAIN_NAME.HELIUM]: 'hnt', + [BLOCKCHAIN_NAME.ICON]: 'icx', + [BLOCKCHAIN_NAME.IOST]: 'iost', + [BLOCKCHAIN_NAME.IOTA]: 'iota', + [BLOCKCHAIN_NAME.KADENA]: 'kda', + [BLOCKCHAIN_NAME.KOMODO]: 'kmd', + [BLOCKCHAIN_NAME.KUSAMA]: 'ksm', + [BLOCKCHAIN_NAME.LISK]: 'lsk', + [BLOCKCHAIN_NAME.LITECOIN]: 'ltc', + [BLOCKCHAIN_NAME.TERRA]: 'luna', + [BLOCKCHAIN_NAME.TERRA_CLASSIC]: 'lunc', + [BLOCKCHAIN_NAME.MINA_PROTOCOL]: 'mina', + [BLOCKCHAIN_NAME.NANO]: 'nano', + [BLOCKCHAIN_NAME.NEAR]: 'near', + [BLOCKCHAIN_NAME.NEO]: 'neo', + [BLOCKCHAIN_NAME.OSMOSIS]: 'osmo', + [BLOCKCHAIN_NAME.PIVX]: 'pivx', + [BLOCKCHAIN_NAME.POLYX]: 'polyx', + [BLOCKCHAIN_NAME.QTUM]: 'qtum', + [BLOCKCHAIN_NAME.THOR_CHAIN]: 'rune', + [BLOCKCHAIN_NAME.RAVENCOIN]: 'rvn', + [BLOCKCHAIN_NAME.SIA]: 'sc', + [BLOCKCHAIN_NAME.SECRET]: 'scrt', + [BLOCKCHAIN_NAME.SOLANA]: 'sol', + [BLOCKCHAIN_NAME.STEEM]: 'steem', + [BLOCKCHAIN_NAME.STRATIS]: 'strax', + [BLOCKCHAIN_NAME.STACKS]: 'stx', + [BLOCKCHAIN_NAME.SOLAR]: 'sxp', + [BLOCKCHAIN_NAME.TON]: 'ton', + [BLOCKCHAIN_NAME.VE_CHAIN]: 'vet', + [BLOCKCHAIN_NAME.WAVES]: 'waves', + [BLOCKCHAIN_NAME.WAX]: 'waxp', + [BLOCKCHAIN_NAME.DX_CHAIN]: 'xchain', + [BLOCKCHAIN_NAME.E_CASH]: 'xec', + [BLOCKCHAIN_NAME.NEM]: 'xem', + [BLOCKCHAIN_NAME.STELLAR]: 'xlm', + [BLOCKCHAIN_NAME.MONERO]: 'xmr', + [BLOCKCHAIN_NAME.RIPPLE]: 'xrp', + [BLOCKCHAIN_NAME.TEZOS]: 'xtz', + [BLOCKCHAIN_NAME.VERGE]: 'xvg', + [BLOCKCHAIN_NAME.SYMBOL]: 'xym', + [BLOCKCHAIN_NAME.ZCASH]: 'zec', + [BLOCKCHAIN_NAME.HORIZEN]: 'zen', + [BLOCKCHAIN_NAME.ZILLIQA]: 'zil', + [BLOCKCHAIN_NAME.TRON]: 'trx', + [BLOCKCHAIN_NAME.KAVA_COSMOS]: 'kava' +}; + +export type ChangenowCrossChainSupportedBlockchain = keyof typeof changenowApiBlockchain; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-proxy-supported-blockchains.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-proxy-supported-blockchains.ts new file mode 100644 index 0000000..048084a --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-proxy-supported-blockchains.ts @@ -0,0 +1,16 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const changenowProxySupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.PULSECHAIN, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.ZK_SYNC +] as const; + +export type ChangenowProxySupportedBlockchain = (typeof changenowProxySupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/native-addresses.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/native-addresses.ts new file mode 100644 index 0000000..ee2ac39 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/native-addresses.ts @@ -0,0 +1,30 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export interface NativeTokenData { + address: string; + blockchain: string; + ticker: string; +} + +export const nativeTokensData: NativeTokenData[] = [ + { + address: '0x4200000000000000000000000000000000000042', + blockchain: BLOCKCHAIN_NAME.OPTIMISM, + ticker: 'op' + }, + { + address: '0x471ece3750da237f93b8e339c536989b8978a438', + blockchain: BLOCKCHAIN_NAME.CELO, + ticker: 'celo' + }, + { + address: 'So11111111111111111111111111111111111111111', + blockchain: BLOCKCHAIN_NAME.SOLANA, + ticker: 'sol' + }, + { + address: 'near', + blockchain: BLOCKCHAIN_NAME.NEAR, + ticker: 'near' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-currencies-api.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-currencies-api.ts new file mode 100644 index 0000000..25a7b46 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-currencies-api.ts @@ -0,0 +1,7 @@ +export interface ChangenowCurrency { + ticker: string; + network: string; + tokenContract: string | null; +} + +export type ChangenowCurrenciesResponse = ChangenowCurrency[]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-minmax-range-api.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-minmax-range-api.ts new file mode 100644 index 0000000..d1a6159 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-minmax-range-api.ts @@ -0,0 +1,11 @@ +export interface ChangenowMinMapRangeRequestParams { + fromCurrency: string; + toCurrency: string; + fromNetwork: string; + toNetwork: string; +} + +export interface ChangenowMinMaxRangeResponse { + minAmount: string; + maxAmount: string | null; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-payment-info.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-payment-info.ts new file mode 100644 index 0000000..eac55eb --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-payment-info.ts @@ -0,0 +1,8 @@ +export interface ChangenowPaymentInfo { + id: string; + depositAddress: string; + extraField?: { + name?: string; + value?: string; + }; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-quote-api.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-quote-api.ts new file mode 100644 index 0000000..05423b5 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-quote-api.ts @@ -0,0 +1,24 @@ +export interface ChangenowQuoteRequestParams { + fromCurrency: string; + toCurrency: string; + fromAmount: string; + fromNetwork: string; + toNetwork: string; +} + +export interface ChangenowQuoteResponse { + toAmount: number; + fromCurrency: string; + toCurrency: string; + fromNetwork: string; + toNetwork: string; + flow: 'standard' | 'fixed-rate'; + type: 'direct' | 'reverse'; + userId: number; + depositFee: number; + withdrawalFee: number; + validUntil: string | null; + rateId: string | null; + transactionSpeedForecast: string | null; + warningMessage: string | null; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-swap.api.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-swap.api.ts new file mode 100644 index 0000000..f6b0377 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-swap.api.ts @@ -0,0 +1,26 @@ +export interface ChangenowSwapRequestBody { + fromCurrency: string; + toCurrency: string; + fromNetwork: string; + toNetwork: string; + fromAmount: string; + address: string; + flow: string; +} + +export interface ChangenowSwapResponse { + /* used for checking tx-status */ + id: string; + payinAddress: string; + payoutAddress: string; + flow: string; + toAmount: number; + fromAmount: number; + type: 'direct' | 'reverse'; + payoutExtraId: string; + payoutExtraIdName: string; + payinExtraId: string; + payinExtraIdName: string; + refundAddress: string; + refundExtraId: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-trade.ts new file mode 100644 index 0000000..0081d33 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-trade.ts @@ -0,0 +1,29 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { ChangenowCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain'; +import { ChangenowCurrency } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/models/changenow-currencies-api'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { MarkRequired } from 'ts-essentials'; + +import { ChangenowSwapResponse } from './changenow-swap.api'; + +export interface ChangenowTrade { + from: PriceTokenAmount; + to: PriceTokenAmount; + toTokenAmountMin: BigNumber; + + fromCurrency: ChangenowCurrency; + toCurrency: ChangenowCurrency; + + feeInfo: FeeInfo; + gasData: GasData; + + onChainTrade: EvmOnChainTrade | null; +} + +export type GetPaymentInfoReturnType = MarkRequired< + Partial, + 'id' | 'payinAddress' +>; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/services/changenow-cross-chain-api-service.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/services/changenow-cross-chain-api-service.ts new file mode 100644 index 0000000..7da62c0 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/changenow-provider/services/changenow-cross-chain-api-service.ts @@ -0,0 +1,72 @@ +import { Cache } from 'src/common/utils/decorators'; +import { Injector } from 'src/core/injector/injector'; +import { changenowApiKey } from 'src/features/common/providers/changenow/constants/changenow-api-key'; +import { HttpClientParams } from 'src/features/common/providers/rango/models/rango-api-common-types'; +import { ChangenowStatusResponse } from 'src/features/cross-chain/status-manager/models/changenow-api-response'; + +import { ChangenowCurrenciesResponse } from '../models/changenow-currencies-api'; +import { + ChangenowMinMapRangeRequestParams, + ChangenowMinMaxRangeResponse +} from '../models/changenow-minmax-range-api'; +import { ChangenowQuoteRequestParams, ChangenowQuoteResponse } from '../models/changenow-quote-api'; +import { ChangenowSwapRequestBody, ChangenowSwapResponse } from '../models/changenow-swap.api'; + +export class ChangeNowCrossChainApiService { + public static changenowApiEndpoint = 'https://api.changenow.io/v2'; + + @Cache({ + maxAge: 15_000 + }) + public static getSwapTx(body: ChangenowSwapRequestBody): Promise { + return Injector.httpClient.post( + `${ChangeNowCrossChainApiService.changenowApiEndpoint}/exchange`, + body, + { + headers: { 'x-changenow-api-key': changenowApiKey } + } + ); + } + + public static getQuoteTx(params: ChangenowQuoteRequestParams): Promise { + return Injector.httpClient.get( + `${ChangeNowCrossChainApiService.changenowApiEndpoint}/exchange/estimated-amount?flow=standard`, + { + params: params as unknown as HttpClientParams, + headers: { 'x-changenow-api-key': changenowApiKey } + } + ); + } + + public static getMinMaxRange( + params: ChangenowMinMapRangeRequestParams + ): Promise { + return Injector.httpClient.get( + `${ChangeNowCrossChainApiService.changenowApiEndpoint}/exchange/range?flow=standard`, + { + params: params as unknown as HttpClientParams, + headers: { 'x-changenow-api-key': changenowApiKey } + } + ); + } + + public static getCurrencies(): Promise { + return Injector.httpClient.get( + `${ChangeNowCrossChainApiService.changenowApiEndpoint}/exchange/currencies`, + { + params: { active: true, flow: 'standard' }, + headers: { 'x-changenow-api-key': changenowApiKey } + } + ); + } + + public static getTxStatus(changenowId: string): Promise { + return Injector.httpClient.get( + `${ChangeNowCrossChainApiService.changenowApiEndpoint}/exchange/by-id`, + { + params: { id: changenowId }, + headers: { 'x-changenow-api-key': changenowApiKey } + } + ); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/constants/proxy-hash-errors.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/constants/proxy-hash-errors.ts new file mode 100644 index 0000000..4971737 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/constants/proxy-hash-errors.ts @@ -0,0 +1,702 @@ +export const proxyHashErrors = [ + { + text: 'CannotAuthoriseSelf', + hash: '0xa9cefcae' + }, + { + text: 'OnlyContractOwner', + hash: '0x277d76f8' + }, + { + text: 'CannotAuthoriseSelf', + hash: '0xa9cefcae' + }, + { + text: 'InvalidContract', + hash: '0x6eefed20' + }, + { + text: 'UnAuthorized', + hash: '0xbe245983' + }, + { + text: 'CalldataEmptyButInitNotZero', + hash: '0x42200566' + }, + { + text: 'FacetAddressIsNotZero', + hash: '0x79c9df22' + }, + { + text: 'FacetAddressIsZero', + hash: '0xc68ec83a' + }, + { + text: 'FacetContainsNoCode', + hash: '0xe3500600' + }, + { + text: 'FunctionAlreadyExists', + hash: '0xa023275d' + }, + { + text: 'FunctionDoesNotExist', + hash: '0xa9ad62f8' + }, + { + text: 'FunctionIsImmutable', + hash: '0xc3c5ec37' + }, + { + text: 'IncorrectFacetCutAction', + hash: '0xe548e6b5' + }, + { + text: 'InitReverted', + hash: '0xc53ebed5' + }, + { + text: 'InitZeroButCalldataNotEmpty', + hash: '0x98116860' + }, + { + text: 'NoSelectorsInFace', + hash: '0x7bc55950' + }, + { + text: 'OnlyContractOwner', + hash: '0x277d76f8' + }, + { + text: 'AlreadyInitialized', + hash: '0x0dc149f0' + }, + { + text: 'FeeTooHigh', + hash: '0xcd4e6167' + }, + { + text: 'InvalidFee', + hash: '0x58d620b3' + }, + { + text: 'OnlyContractOwner', + hash: '0x277d76f8' + }, + { + text: 'ReentrancyError', + hash: '0x29f745a7' + }, + { + text: 'ShareTooHigh', + hash: '0x2a07fc4c' + }, + { + text: 'TokenAddressIsZero', + hash: '0xdc2e5e8d' + }, + { + text: 'UnAuthorized', + hash: '0xbe245983' + }, + { + text: 'ZeroAddress', + hash: '0xd92e233d' + }, + { + text: 'CannotBridgeToSameNetwork', + hash: '0x4ac09ad3' + }, + { + text: 'ContractCallNotAllowed', + hash: '0x94539804' + }, + { + text: 'CumulativeSlippageTooHigh', + hash: '0x65ec4880' + }, + { + text: 'InformationMismatch', + hash: '0x50dc905c' + }, + { + text: 'InsufficientBalance', + hash: '0xf4d678b8' + }, + { + text: 'InvalidAmount', + hash: '0x2c5211c6' + }, + { + text: 'InvalidContract', + hash: '0x6eefed20' + }, + { + text: 'InvalidReceiver', + hash: '0x1e4ec46b' + }, + { + text: 'LengthMissmatch', + hash: '0xd4e105db' + }, + { + text: 'NativeAssetTransferFailed', + hash: '0x5a046737' + }, + { + text: 'NoSwapDataProvided', + hash: '0x0503c3ed' + }, + { + text: 'NoSwapFromZeroBalance', + hash: '0xe46e079c' + }, + { + text: 'NoTransferToNullAddress', + hash: '0x21f74345' + }, + { + text: 'NullAddrIsNotAValidSpender', + hash: '0x63ba9bff' + }, + { + text: 'NullAddrIsNotAnERC20Token', + hash: '0xd1bebf0c' + }, + { + text: 'OnlyContractOwner', + hash: '0x277d76f8' + }, + { + text: 'ReentrancyError', + hash: '0x29f745a7' + }, + { + text: 'SliceOutOfBounds', + hash: '0x3b99b53d' + }, + { + text: 'SliceOverflow', + hash: '0x47aaf07a' + }, + { + text: 'UnAuthorized', + hash: '0xbe245983' + }, + { + text: 'ContractCallNotAllowed', + hash: '0x94539804' + }, + { + text: 'CumulativeSlippageTooHigh', + hash: '0x65ec4880' + }, + { + text: 'InsufficientBalance', + hash: '0xf4d678b8' + }, + { + text: 'InvalidAmount', + hash: '0x2c5211c6' + }, + { + text: 'InvalidContract', + hash: '0x6eefed20' + }, + { + text: 'InvalidReceiver', + hash: '0x1e4ec46b' + }, + { + text: 'NativeAssetTransferFailed', + hash: '0x5a046737' + }, + { + text: 'NoSwapDataProvided', + hash: '0x0503c3ed' + }, + { + text: 'NoSwapFromZeroBalance', + hash: '0xe46e079c' + }, + { + text: 'NoTransferToNullAddress', + hash: '0x21f74345' + }, + { + text: 'NullAddrIsNotAValidSpender', + hash: '0x63ba9bff' + }, + { + text: 'NullAddrIsNotAnERC20Token', + hash: '0xd1bebf0c' + }, + { + text: 'ReentrancyError', + hash: '0x29f745a7' + }, + { + text: 'SliceOutOfBounds', + hash: '0x3b99b53d' + }, + { + text: 'SliceOverflow', + hash: '0x47aaf07a' + }, + { + text: 'UnAuthorized', + hash: '0xbe245983' + }, + { + text: 'AlreadyInitialized', + hash: '0x0dc149f0' + }, + { + text: 'CannotBridgeToSameNetwork', + hash: '0x4ac09ad3' + }, + { + text: 'ContractCallNotAllowed', + hash: '0x94539804' + }, + { + text: 'CumulativeSlippageTooHigh', + hash: '0x65ec4880' + }, + { + text: 'InformationMismatch', + hash: '0x50dc905c' + }, + { + text: 'InsufficientBalance', + hash: '0xf4d678b8' + }, + { + text: 'InvalidAmount', + hash: '0x2c5211c6' + }, + { + text: 'InvalidConfig', + hash: '0x35be3ac8' + }, + { + text: 'InvalidContract', + hash: '0x6eefed20' + }, + { + text: 'InvalidReceiver', + hash: '0x1e4ec46b' + }, + { + text: 'InvalidRouter', + hash: '0x466d7fef' + }, + { + text: 'NativeAssetTransferFailed', + hash: '0x5a046737' + }, + { + text: 'NoSwapDataProvided', + hash: '0x0503c3ed' + }, + { + text: 'NoSwapFromZeroBalance', + hash: '0xe46e079c' + }, + { + text: 'NoTransferToNullAddress', + hash: '0x21f74345' + }, + { + text: 'NotInitialized', + hash: '0x87138d5c' + }, + { + text: 'NullAddrIsNotAValidSpender', + hash: '0x63ba9bff' + }, + { + text: 'NullAddrIsNotAnERC20Token', + hash: '0xd1bebf0c' + }, + { + text: 'OnlyContractOwner', + hash: '0x277d76f8' + }, + { + text: 'ReentrancyError', + hash: '0x29f745a7' + }, + { + text: 'SliceOutOfBounds', + hash: '0x3b99b53d' + }, + { + text: 'SliceOverflow', + hash: '0x47aaf07a' + }, + { + text: 'UnAuthorized', + hash: '0xbe245983' + }, + { + text: 'NewOwnerMustNotBeSelf', + hash: '0xbf1ea9fb' + }, + { + text: 'NoNullOwner', + hash: '0x1beca374' + }, + { + text: 'NoPendingOwnershipTransfer', + hash: '0x75cdea12' + }, + { + text: 'NotPendingOwner', + hash: '0x1853971c' + }, + { + text: 'OnlyContractOwner', + hash: '0x277d76f8' + }, + { + text: 'OnlyContractOwner', + hash: '0x277d76f8' + }, + { + text: 'AlreadyInitialized', + hash: '0x0dc149f0' + }, + { + text: 'CannotBridgeToSameNetwork', + hash: '0x4ac09ad3' + }, + { + text: 'ContractCallNotAllowed', + hash: '0x94539804' + }, + { + text: 'CumulativeSlippageTooHigh', + hash: '0x65ec4880' + }, + { + text: 'InformationMismatch', + hash: '0x50dc905c' + }, + { + text: 'InsufficientBalance', + hash: '0xf4d678b8' + }, + { + text: 'InvalidAmount', + hash: '0x2c5211c6' + }, + { + text: 'InvalidConfig', + hash: '0x35be3ac8' + }, + { + text: 'InvalidContract', + hash: '0x6eefed20' + }, + { + text: 'InvalidReceiver', + hash: '0x1e4ec46b' + }, + { + text: 'InvalidStargateRouter', + hash: '0x1aba923d' + }, + { + text: 'NativeAssetNotSupported', + hash: '0x5ded5997' + }, + { + text: 'NativeAssetTransferFailed', + hash: '0x5a046737' + }, + { + text: 'NoSwapDataProvided', + hash: '0x0503c3ed' + }, + { + text: 'NoSwapFromZeroBalance', + hash: '0xe46e079c' + }, + { + text: 'NoTransferToNullAddress', + hash: '0x21f74345' + }, + { + text: 'NotInitialized', + hash: '0x87138d5c' + }, + { + text: 'NullAddrIsNotAValidSpender', + hash: '0x63ba9bff' + }, + { + text: 'NullAddrIsNotAnERC20Token', + hash: '0xd1bebf0c' + }, + { + text: 'OnlyContractOwner', + hash: '0x277d76f8' + }, + { + text: 'ReentrancyError', + hash: '0x29f745a7' + }, + { + text: 'SliceOutOfBounds', + hash: '0x3b99b53d' + }, + { + text: 'SliceOverflow', + hash: '0x47aaf07a' + }, + { + text: 'UnAuthorized', + hash: '0xbe245983' + }, + { + text: 'UnknownLayerZeroChain', + hash: '0x7c152fe3' + }, + { + text: 'UnknownStargatePool', + hash: '0x59c64529' + }, + { + text: 'CannotBridgeToSameNetwork', + hash: '0x4ac09ad3' + }, + { + text: 'ContractCallNotAllowed', + hash: '0x94539804' + }, + { + text: 'CumulativeSlippageTooHigh', + hash: '0x65ec4880' + }, + { + text: 'InformationMismatch', + hash: '0x50dc905c' + }, + { + text: 'InsufficientBalance', + hash: '0xf4d678b8' + }, + { + text: 'InvalidAmount', + hash: '0x2c5211c6' + }, + { + text: 'InvalidContract', + hash: '0x6eefed20' + }, + { + text: 'InvalidReceiver', + hash: '0x1e4ec46b' + }, + { + text: 'NativeAssetTransferFailed', + hash: '0x5a046737' + }, + { + text: 'NoSwapDataProvided', + hash: '0x0503c3ed' + }, + { + text: 'NoSwapFromZeroBalance', + hash: '0xe46e079c' + }, + { + text: 'NoTransferToNullAddress', + hash: '0x21f74345' + }, + { + text: 'NullAddrIsNotAValidSpender', + hash: '0x63ba9bff' + }, + { + text: 'NullAddrIsNotAnERC20Token', + hash: '0xd1bebf0c' + }, + { + text: 'ReentrancyError', + hash: '0x29f745a7' + }, + { + text: 'SliceOutOfBounds', + hash: '0x3b99b53d' + }, + { + text: 'SliceOverflow', + hash: '0x47aaf07a' + }, + { + text: 'UnAuthorized', + hash: '0xbe245983' + }, + { + text: 'InsufficientBalance', + hash: '0xf4d678b8' + }, + { + text: 'NativeAssetTransferFailed', + hash: '0x5a046737' + }, + { + text: 'NoTransferToNullAddress', + hash: '0x21f74345' + }, + { + text: 'NotAContract', + hash: '0x09ee12d5' + }, + { + text: 'NullAddrIsNotAnERC20Token', + hash: '0xd1bebf0c' + }, + { + text: 'UnAuthorized', + hash: '0xbe245983' + }, + { + text: 'WithdrawFailed', + hash: '0x750b219c' + }, + { + text: 'CannotBridgeToSameNetwork', + hash: '0x4ac09ad3' + }, + { + text: 'ContractCallNotAllowed', + hash: '0x94539804' + }, + { + text: 'CumulativeSlippageTooHigh', + hash: '0x65ec4880' + }, + { + text: 'InformationMismatch', + hash: '0x50dc905c' + }, + { + text: 'InsufficientBalance', + hash: '0xf4d678b8' + }, + { + text: 'InvalidAmount', + hash: '0x2c5211c6' + }, + { + text: 'InvalidContract', + hash: '0x6eefed20' + }, + { + text: 'InvalidReceiver', + hash: '0x1e4ec46b' + }, + { + text: 'NativeAssetTransferFailed', + hash: '0x5a046737' + }, + { + text: 'NoSwapDataProvided', + hash: '0x0503c3ed' + }, + { + text: 'NoSwapFromZeroBalance', + hash: '0xe46e079c' + }, + { + text: 'NoTransferToNullAddress', + hash: '0x21f74345' + }, + { + text: 'NullAddrIsNotAValidSpender', + hash: '0x63ba9bff' + }, + { + text: 'NullAddrIsNotAnERC20Token', + hash: '0xd1bebf0c' + }, + { + text: 'ReentrancyError', + hash: '0x29f745a7' + }, + { + text: 'SliceOutOfBounds', + hash: '0x3b99b53d' + }, + { + text: 'SliceOverflow', + hash: '0x47aaf07a' + }, + { + text: 'UnAuthorized', + hash: '0xbe245983' + }, + { + text: 'AddressOutOfBounds', + hash: '0x8f95a28a' + }, + { + text: 'SliceOutOfBounds', + hash: '0x3b99b53d' + }, + { + text: 'SliceOverflow', + hash: '0x47aaf07a' + }, + { + text: 'UintOutOfBounds', + hash: '0x85ec83f8' + }, + { + text: 'CalldataEmptyButInitNotZero', + hash: '0x42200566' + }, + { + text: 'FacetAddressIsNotZero', + hash: '0x79c9df22' + }, + { + text: 'FacetAddressIsZero', + hash: '0xc68ec83a' + }, + { + text: 'FacetContainsNoCode', + hash: '0xe3500600' + }, + { + text: 'FunctionAlreadyExists', + hash: '0xa023275d' + }, + { + text: 'FunctionDoesNotExist', + hash: '0xa9ad62f8' + }, + { + text: 'FunctionIsImmutable', + hash: '0xc3c5ec37' + }, + { + text: 'IncorrectFacetCutAction', + hash: '0xe548e6b5' + }, + { + text: 'InitReverted', + hash: '0xc53ebed5' + }, + { + text: 'InitZeroButCalldataNotEmpty', + hash: '0x98116860' + }, + { + text: 'NoSelectorsInFace', + hash: '0x7bc55950' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address.ts new file mode 100644 index 0000000..88ed5bc --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address.ts @@ -0,0 +1,64 @@ +import { BLOCKCHAIN_NAME, BlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +const ALTERNATIVE_ROUTER1_NETWORKS = [ + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.MANTLE, + BLOCKCHAIN_NAME.SCROLL, + BLOCKCHAIN_NAME.MANTA_PACIFIC, + BLOCKCHAIN_NAME.METIS, + BLOCKCHAIN_NAME.BLAST, + BLOCKCHAIN_NAME.ROOTSTOCK, + BLOCKCHAIN_NAME.KROMA, + BLOCKCHAIN_NAME.HORIZEN_EON, + BLOCKCHAIN_NAME.MERLIN, + BLOCKCHAIN_NAME.MODE, + BLOCKCHAIN_NAME.ZK_FAIR, + BLOCKCHAIN_NAME.ZETACHAIN, + BLOCKCHAIN_NAME.XLAYER, + BLOCKCHAIN_NAME.TAIKO, + BLOCKCHAIN_NAME.SEI +] as const; + +function isAlternativeRouter1Network(blockchain: BlockchainName): boolean { + return ALTERNATIVE_ROUTER1_NETWORKS.some(chain => chain === blockchain); +} + +export const rubicProxyContractAddress: Record< + BlockchainName, + { + gateway: string; + router: string; + } +> = Object.values(BLOCKCHAIN_NAME).reduce( + (acc, blockchain) => { + // ERC20Proxy + let gateway = '0x3335733c454805df6a77f825f266e136FB4a3333'; + // RubicMultiProxy + let router = '0x6AA981bFF95eDfea36Bdae98C26B274FfcafE8d3'; + + if (blockchain === BLOCKCHAIN_NAME.TELOS) { + router = '0xA2d8CF32C16f070702c45a5686Fdb0a1d7171AAD'; + } + if (blockchain === BLOCKCHAIN_NAME.ZK_SYNC) { + router = '0xa63c029612ddaD00A269383Ab016D1e7c14E851D'; + gateway = '0x8E70e517057e7380587Ea6990dAe81cB1Ba405ce'; + } + if (isAlternativeRouter1Network(blockchain)) { + router = '0xAf14797CcF963B1e3d028a9d51853acE16aedBA1'; + } + if (blockchain === BLOCKCHAIN_NAME.ZK_LINK) { + router = '0x1a979E2386595837BaAB90Ba12B2E2a71C652576'; + gateway = '0x72f4Cd2a1707CbecE96E82be21f243fdF93867ee'; + } + + return { ...acc, [blockchain]: { gateway, router } }; + }, + {} as Record< + BlockchainName, + { + gateway: string; + router: string; + } + > +); diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider.ts new file mode 100644 index 0000000..d8bcb99 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider.ts @@ -0,0 +1,73 @@ +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { parseError } from 'src/common/utils/errors'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3PrivateSupportedBlockchain } from 'src/core/blockchain/web3-private-service/models/web-private-supported-blockchain'; +import { Web3Public } from 'src/core/blockchain/web3-public-service/web3-public/web3-public'; +import { HttpClient } from 'src/core/http-client/models/http-client'; +import { Injector } from 'src/core/injector/injector'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CrossChainTradeType } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { AbiItem } from 'web3-utils'; + +export abstract class CrossChainProvider { + public static parseError(err: unknown): RubicSdkError { + return parseError(err, 'Cannot calculate cross-chain trade'); + } + + public abstract readonly type: CrossChainTradeType; + + protected get httpClient(): HttpClient { + return Injector.httpClient; + } + + public abstract isSupportedBlockchain(fromBlockchain: BlockchainName): boolean; + + public areSupportedBlockchains( + fromBlockchain: BlockchainName, + toBlockchain: BlockchainName + ): boolean { + return ( + this.isSupportedBlockchain(fromBlockchain) && this.isSupportedBlockchain(toBlockchain) + ); + } + + protected getFromWeb3Public(fromBlockchain: BlockchainName): Web3Public { + return Injector.web3PublicService.getWeb3Public(fromBlockchain); + } + + public abstract calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise; + + protected getWalletAddress(blockchain: Web3PrivateSupportedBlockchain): string { + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(blockchain).address; + } + + protected abstract getRoutePath(...options: unknown[]): Promise; + + /** + * Gets fee information. + * @param _fromBlockchain Source network blockchain. + * @param _providerAddress Integrator address. + * @param _percentFeeToken Protocol fee token. + * @param _useProxy Use rubic proxy or not. + * @param _contractAbi Rubic Proxy contract abi. + * @protected + * @internal + */ + protected async getFeeInfo( + _fromBlockchain: Partial, + _providerAddress: string, + _percentFeeToken: PriceToken, + _useProxy: boolean, + _contractAbi?: AbiItem[] + ): Promise { + return {}; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/cross-chain-trade.ts new file mode 100644 index 0000000..f3069de --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/cross-chain-trade.ts @@ -0,0 +1,353 @@ +import BigNumber from 'bignumber.js'; +import { + RubicSdkError, + WalletNotConnectedError, + WrongFromAddressError, + WrongReceiverAddressError +} from 'src/common/errors'; +import { UpdatedRatesError } from 'src/common/errors/cross-chain/updated-rates-error'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { BasicTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/models/basic-transaction-options'; +import { Web3Private } from 'src/core/blockchain/web3-private-service/web3-private/web3-private'; +import { Web3Public } from 'src/core/blockchain/web3-public-service/web3-public/web3-public'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { HttpClient } from 'src/core/http-client/models/http-client'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { isAddressCorrect } from 'src/features/common/utils/check-address'; +import { CrossChainTradeType } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { BridgeType } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { OnChainSubtype } from 'src/features/cross-chain/calculation-manager/providers/common/models/on-chain-subtype'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; + +/** + * Abstract class for all cross-chain providers' trades. + */ +export abstract class CrossChainTrade { + protected lastTransactionConfig: T | null = null; + + /** + * Type of calculated cross-chain trade. + */ + public abstract readonly type: CrossChainTradeType; + + /** + * Token to sell with input amount. + */ + public abstract readonly from: PriceTokenAmount; + + /** + * Token to get with output amount. + */ + public abstract readonly to: PriceTokenAmount; + + /** + * Minimum amount of output token user will get in Eth units. + */ + public abstract readonly toTokenAmountMin: BigNumber; + + /** + * Swap fee information. + */ + public abstract readonly feeInfo: FeeInfo; + + /** + * Contains on-chain providers' type used in route. + */ + public abstract readonly onChainSubtype: OnChainSubtype; + + /** + * Contains bridge provider's type used in route. + */ + public abstract readonly bridgeType: BridgeType; + + /** + * True, if provider is aggregator. + */ + public abstract readonly isAggregator: boolean; + + /** + * Promotions array. + */ + public promotions: string[] = []; + + protected abstract get fromContractAddress(): string; + + protected get httpClient(): HttpClient { + return Injector.httpClient; + } + + protected get fromWeb3Public(): Web3Public { + return Injector.web3PublicService.getWeb3Public(this.from.blockchain); + } + + protected get web3Private(): Web3Private { + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(this.from.blockchain); + } + + protected get walletAddress(): string { + return this.web3Private.address; + } + + protected abstract get methodName(): string; + + public get networkFee(): BigNumber { + return new BigNumber(this.feeInfo.rubicProxy?.fixedFee?.amount || 0).plus( + this.feeInfo.provider?.cryptoFee?.amount || 0 + ); + } + + public get platformFee(): BigNumber { + return new BigNumber(this.feeInfo.rubicProxy?.platformFee?.percent || 0).plus( + this.feeInfo.provider?.platformFee?.percent || 0 + ); + } + + protected get isProxyTrade(): boolean { + const fee = this.feeInfo.rubicProxy; + const hasFixedFee = Boolean(fee?.fixedFee?.amount?.gt(0)); + const hasPlatformFee = Number(fee?.platformFee?.percent) > 0; + + return hasFixedFee || hasPlatformFee; + } + + protected get amountToCheck(): string { + return this.to.stringWeiAmount; + } + + protected checkAmountChange(newWeiAmount: string, oldWeiAmount: string): void { + const oldAmount = new BigNumber(oldWeiAmount); + const newAmount = new BigNumber(newWeiAmount); + const changePercent = 0.5; + const acceptablePercentPriceChange = new BigNumber(changePercent).dividedBy(100); + + const amountPlusPercent = oldAmount.multipliedBy(acceptablePercentPriceChange.plus(1)); + const amountMinusPercent = oldAmount.multipliedBy( + new BigNumber(1).minus(acceptablePercentPriceChange) + ); + + const shouldThrowError = + newAmount.lt(amountMinusPercent) || newAmount.gt(amountPlusPercent); + + if (shouldThrowError) { + throw new UpdatedRatesError(oldWeiAmount, newWeiAmount); + } + } + + protected constructor( + protected readonly providerAddress: string, + protected readonly routePath: RubicStep[] + ) {} + + /** + * Returns true, if allowance is not enough. + */ + public async needApprove(): Promise { + this.checkWalletConnected(); + + if (this.from.isNative && this.from.blockchain !== BLOCKCHAIN_NAME.METIS) { + return false; + } + + const fromTokenAddress = + this.from.isNative && this.from.blockchain === BLOCKCHAIN_NAME.METIS + ? '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000' + : this.from.address; + + const allowance = await this.fromWeb3Public.getAllowance( + fromTokenAddress, + this.walletAddress, + this.fromContractAddress + ); + return this.from.weiAmount.gt(allowance); + } + + /** + * Sends approve transaction with connected wallet. + * @param options Transaction options. + * @param checkNeedApprove If true, first allowance is checked. + * @param amount Amount of tokens in approval window in spending cap field + */ + public abstract approve( + options: BasicTransactionOptions, + checkNeedApprove?: boolean, + amount?: BigNumber | 'infinity' + ): Promise; + + /** + * Sends swap transaction with connected wallet. + * If user has not enough allowance, then approve transaction will be called first. + * + * @example + * ```ts + * const onConfirm = (hash: string) => console.log(hash); + * const receipt = await trade.swap({ onConfirm }); + * ``` + * + * @param options Transaction options. + */ + public abstract swap(options?: SwapTransactionOptions): Promise; + + /** + * Builds transaction config, with encoded data. + * @param options Encode transaction options. + */ + public abstract encode(options: EncodeTransactionOptions): Promise; + + /** + * Build encoded approve transaction config. + * @param tokenAddress Address of the smart-contract corresponding to the token. + * @param spenderAddress Wallet or contract address to approve. + * @param value Token amount to approve in wei. + * @param [options] Additional options. + * @returns Encoded approve transaction config. + */ + public abstract encodeApprove( + tokenAddress: string, + spenderAddress: string, + value: BigNumber | 'infinity', + options: BasicTransactionOptions + ): Promise; + + protected async checkTradeErrors(): Promise { + this.checkWalletConnected(); + await Promise.all([ + this.checkBlockchainCorrect(), + this.checkUserBalance(), + this.checkBlockchainRequirements() + ]); + } + + protected checkWalletConnected(): never | void { + if (!this.walletAddress) { + throw new WalletNotConnectedError(); + } + } + + public async checkBlockchainRequirements(): Promise { + if (this.to.blockchain === BLOCKCHAIN_NAME.SEI && !this.to.isNative && this.walletAddress) { + const web3 = Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.SEI); + const transactionCount = await web3.getTransactionCount(this.walletAddress); + const balance = await web3.getBalance(this.walletAddress, this.to.address); + if (new BigNumber(balance).eq(0) && transactionCount === 0) { + return true; + } + } + return false; + } + + protected async checkBlockchainCorrect(): Promise { + await this.web3Private.checkBlockchainCorrect(this.from.blockchain); + } + + protected async checkUserBalance(): Promise { + await this.fromWeb3Public.checkBalance( + this.from, + this.from.tokenAmount, + this.walletAddress + ); + } + + protected async checkFromAddress( + fromAddress: string | undefined, + isRequired = false, + crossChainType?: CrossChainTradeType + ): Promise { + if (!fromAddress) { + if (isRequired) { + throw new RubicSdkError(`'fromAddress' is required option`); + } + return; + } + const isAddressCorrectValue = await isAddressCorrect( + fromAddress, + this.from.blockchain, + crossChainType + ); + if (!isAddressCorrectValue) { + throw new WrongFromAddressError(); + } + } + + protected async checkReceiverAddress( + receiverAddress: string | undefined, + isRequired = false, + crossChainType?: CrossChainTradeType + ): Promise { + if (!receiverAddress) { + if (isRequired) { + throw new RubicSdkError(`'receiverAddress' is required option`); + } + return; + } + const isAddressCorrectValue = await isAddressCorrect( + receiverAddress, + this.to.blockchain, + crossChainType + ); + if (!isAddressCorrectValue) { + throw new WrongReceiverAddressError(); + } + } + + /** + * Calculates value for swap transaction. + * @param providerValue Value, returned from cross-chain provider. + */ + protected getSwapValue(providerValue?: BigNumber | string | number | null): string { + const nativeToken = nativeTokensList[this.from.blockchain]; + const fixedFeeValue = Web3Pure.toWei( + this.feeInfo.rubicProxy?.fixedFee?.amount || 0, + nativeToken.decimals + ); + + let fromValue: BigNumber; + if (this.from.isNative) { + if (providerValue) { + fromValue = new BigNumber(providerValue).dividedBy( + 1 - (this.feeInfo.rubicProxy?.platformFee?.percent || 0) / 100 + ); + } else { + fromValue = this.from.weiAmount; + } + } else { + fromValue = new BigNumber(providerValue || 0); + } + + return new BigNumber(fromValue).plus(fixedFeeValue).toFixed(0, 0); + } + + public abstract getUsdPrice(providerFeeTokenPrice?: BigNumber): BigNumber; + + public abstract getTradeInfo(): TradeInfo; + + protected abstract getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: T; amount: string }>; + + protected async setTransactionConfig( + skipAmountChangeCheck: boolean, + useCacheData: boolean, + receiverAddress?: string + ): Promise { + if (this.lastTransactionConfig && useCacheData) { + return this.lastTransactionConfig; + } + + const { config, amount } = await this.getTransactionConfigAndAmount(receiverAddress); + this.lastTransactionConfig = config; + setTimeout(() => { + this.lastTransactionConfig = null; + }, 15_000); + + if (!skipAmountChangeCheck) { + this.checkAmountChange(amount, this.amountToCheck); + } + return config; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi.ts new file mode 100644 index 0000000..22083ce --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi.ts @@ -0,0 +1,1226 @@ +import { AbiItem } from 'web3-utils'; + +export const evmCommonCrossChainAbi: AbiItem[] = [ + { + inputs: [], + name: 'fixedNativeFee', + outputs: [ + { + internalType: 'uint256', + name: '_fixedNativeFee', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_integrator', + type: 'address' + } + ], + name: 'integratorToFeeInfo', + outputs: [ + { + components: [ + { + internalType: 'bool', + name: 'isIntegrator', + type: 'bool' + }, + { + internalType: 'uint32', + name: 'tokenFee', + type: 'uint32' + }, + { + internalType: 'uint32', + name: 'RubicTokenShare', + type: 'uint32' + }, + { + internalType: 'uint32', + name: 'RubicFixedCryptoShare', + type: 'uint32' + }, + { + internalType: 'uint128', + name: 'fixedFeeAmount', + type: 'uint128' + } + ], + internalType: 'struct IFeesFacet.IntegratorFeeInfo', + name: '_info', + type: 'tuple' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'RubicPlatformFee', + outputs: [ + { + internalType: 'uint256', + name: '_RubicPlatformFee', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + components: [ + { + internalType: 'bytes32', + name: 'transactionId', + type: 'bytes32' + }, + { + internalType: 'string', + name: 'bridge', + type: 'string' + }, + { + internalType: 'address', + name: 'integrator', + type: 'address' + }, + { + internalType: 'address', + name: 'referrer', + type: 'address' + }, + { + internalType: 'address', + name: 'sendingAssetId', + type: 'address' + }, + { + internalType: 'address', + name: 'receivingAssetId', + type: 'address' + }, + { + internalType: 'address', + name: 'receiver', + type: 'address' + }, + { + internalType: 'address', + name: 'refundee', + type: 'address' + }, + { + internalType: 'uint256', + name: 'minAmount', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'destinationChainId', + type: 'uint256' + }, + { + internalType: 'bool', + name: 'hasSourceSwaps', + type: 'bool' + }, + { + internalType: 'bool', + name: 'hasDestinationCall', + type: 'bool' + } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [ + { + internalType: 'bytes', + name: 'firstSwapCalldata', + type: 'bytes' + }, + { + internalType: 'bytes', + name: 'secondSwapCalldata', + type: 'bytes' + }, + { + internalType: 'address', + name: 'intermediateToken', + type: 'address' + }, + { + internalType: 'address', + name: 'bridgingToken', + type: 'address' + }, + { + internalType: 'address', + name: 'firstDexRouter', + type: 'address' + }, + { + internalType: 'address', + name: 'secondDexRouter', + type: 'address' + }, + { + internalType: 'address', + name: 'relayRecipient', + type: 'address' + }, + { + internalType: 'bytes', + name: 'otherSideCalldata', + type: 'bytes' + } + ], + internalType: 'struct SymbiosisFacet.SymbiosisData', + name: '_symbiosisData', + type: 'tuple' + } + ], + name: 'startBridgeTokensViaSymbiosis', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes32', name: 'transactionId', type: 'bytes32' }, + { internalType: 'string', name: 'bridge', type: 'string' }, + { internalType: 'address', name: 'integrator', type: 'address' }, + { internalType: 'address', name: 'referrer', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'address', name: 'receiver', type: 'address' }, + { internalType: 'address', name: 'refundee', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'destinationChainId', type: 'uint256' }, + { internalType: 'bool', name: 'hasSourceSwaps', type: 'bool' }, + { internalType: 'bool', name: 'hasDestinationCall', type: 'bool' } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [ + { internalType: 'address', name: 'callTo', type: 'address' }, + { internalType: 'address', name: 'approveTo', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'uint256', name: 'fromAmount', type: 'uint256' }, + { internalType: 'bytes', name: 'callData', type: 'bytes' }, + { internalType: 'bool', name: 'requiresDeposit', type: 'bool' } + ], + internalType: 'struct LibSwap.SwapData[]', + name: '_swapData', + type: 'tuple[]' + }, + { + components: [ + { internalType: 'bytes', name: 'firstSwapCalldata', type: 'bytes' }, + { internalType: 'bytes', name: 'secondSwapCalldata', type: 'bytes' }, + { internalType: 'address', name: 'intermediateToken', type: 'address' }, + { internalType: 'address', name: 'bridgingToken', type: 'address' }, + { internalType: 'address', name: 'firstDexRouter', type: 'address' }, + { internalType: 'address', name: 'secondDexRouter', type: 'address' }, + { internalType: 'address', name: 'relayRecipient', type: 'address' }, + { internalType: 'bytes', name: 'otherSideCalldata', type: 'bytes' } + ], + internalType: 'struct SymbiosisFacet.SymbiosisData', + name: '_symbiosisData', + type: 'tuple' + } + ], + name: 'swapAndStartBridgeTokensViaSymbiosis', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes32', name: 'transactionId', type: 'bytes32' }, + { internalType: 'string', name: 'bridge', type: 'string' }, + { internalType: 'address', name: 'integrator', type: 'address' }, + { internalType: 'address', name: 'referrer', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'address', name: 'receiver', type: 'address' }, + { internalType: 'address', name: 'refundee', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'destinationChainId', type: 'uint256' }, + { internalType: 'bool', name: 'hasSourceSwaps', type: 'bool' }, + { internalType: 'bool', name: 'hasDestinationCall', type: 'bool' } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [ + { internalType: 'address', name: 'callTo', type: 'address' }, + { internalType: 'address', name: 'approveTo', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'uint256', name: 'fromAmount', type: 'uint256' }, + { internalType: 'bytes', name: 'callData', type: 'bytes' }, + { internalType: 'bool', name: 'requiresDeposit', type: 'bool' } + ], + internalType: 'struct LibSwap.SwapData[]', + name: '_swapData', + type: 'tuple[]' + }, + { + components: [{ internalType: 'address', name: 'router', type: 'address' }], + internalType: 'struct MultichainFacet.MultichainData', + name: '_multichainData', + type: 'tuple' + } + ], + name: 'swapAndStartBridgeTokensViaMultichain', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'anyNative', type: 'address' }, + { internalType: 'address[]', name: 'routers', type: 'address[]' } + ], + name: 'initMultichain', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address[]', name: 'routers', type: 'address[]' }, + { internalType: 'bool[]', name: 'allowed', type: 'bool[]' } + ], + name: 'registerRouters', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes32', name: 'transactionId', type: 'bytes32' }, + { internalType: 'string', name: 'bridge', type: 'string' }, + { internalType: 'address', name: 'integrator', type: 'address' }, + { internalType: 'address', name: 'referrer', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'address', name: 'receiver', type: 'address' }, + { internalType: 'address', name: 'refundee', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'destinationChainId', type: 'uint256' }, + { internalType: 'bool', name: 'hasSourceSwaps', type: 'bool' }, + { internalType: 'bool', name: 'hasDestinationCall', type: 'bool' } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [{ internalType: 'address', name: 'router', type: 'address' }], + internalType: 'struct MultichainFacet.MultichainData', + name: '_multichainData', + type: 'tuple' + } + ], + name: 'startBridgeTokensViaMultichain', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes32', name: '_transactionId', type: 'bytes32' }, + { internalType: 'address', name: '_integrator', type: 'address' }, + { internalType: 'address', name: '_referrer', type: 'address' }, + { internalType: 'address payable', name: '_receiver', type: 'address' }, + { internalType: 'uint256', name: '_minAmount', type: 'uint256' }, + { + components: [ + { internalType: 'address', name: 'callTo', type: 'address' }, + { internalType: 'address', name: 'approveTo', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'uint256', name: 'fromAmount', type: 'uint256' }, + { internalType: 'bytes', name: 'callData', type: 'bytes' }, + { internalType: 'bool', name: 'requiresDeposit', type: 'bool' } + ], + internalType: 'struct LibSwap.SwapData[]', + name: '_swapData', + type: 'tuple[]' + } + ], + name: 'swapTokensGeneric', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { + internalType: 'bytes32', + name: 'transactionId', + type: 'bytes32' + }, + { + internalType: 'string', + name: 'bridge', + type: 'string' + }, + { + internalType: 'address', + name: 'integrator', + type: 'address' + }, + { + internalType: 'address', + name: 'referrer', + type: 'address' + }, + { + internalType: 'address', + name: 'sendingAssetId', + type: 'address' + }, + { + internalType: 'address', + name: 'receivingAssetId', + type: 'address' + }, + { + internalType: 'address', + name: 'receiver', + type: 'address' + }, + { + internalType: 'address', + name: 'refundee', + type: 'address' + }, + { + internalType: 'uint256', + name: 'minAmount', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'destinationChainId', + type: 'uint256' + }, + { + internalType: 'bool', + name: 'hasSourceSwaps', + type: 'bool' + }, + { + internalType: 'bool', + name: 'hasDestinationCall', + type: 'bool' + } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [ + { + internalType: 'address', + name: 'callTo', + type: 'address' + }, + { + internalType: 'address', + name: 'approveTo', + type: 'address' + }, + { + internalType: 'address', + name: 'sendingAssetId', + type: 'address' + }, + { + internalType: 'address', + name: 'receivingAssetId', + type: 'address' + }, + { + internalType: 'uint256', + name: 'fromAmount', + type: 'uint256' + }, + { + internalType: 'bytes', + name: 'callData', + type: 'bytes' + }, + { + internalType: 'bool', + name: 'requiresDeposit', + type: 'bool' + } + ], + internalType: 'struct LibSwap.SwapData[]', + name: '_swapData', + type: 'tuple[]' + }, + { + components: [ + { + internalType: 'uint256', + name: 'dstPoolId', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'minAmountLD', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'dstGasForCall', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'lzFee', + type: 'uint256' + }, + { + internalType: 'address payable', + name: 'refundAddress', + type: 'address' + }, + { + internalType: 'bytes', + name: 'callTo', + type: 'bytes' + }, + { + internalType: 'bytes', + name: 'callData', + type: 'bytes' + } + ], + internalType: 'struct StargateFacet.StargateData', + name: '_stargateData', + type: 'tuple' + } + ], + name: 'swapAndStartBridgeTokensViaStargate', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { + internalType: 'bytes32', + name: 'transactionId', + type: 'bytes32' + }, + { + internalType: 'string', + name: 'bridge', + type: 'string' + }, + { + internalType: 'address', + name: 'integrator', + type: 'address' + }, + { + internalType: 'address', + name: 'referrer', + type: 'address' + }, + { + internalType: 'address', + name: 'sendingAssetId', + type: 'address' + }, + { + internalType: 'address', + name: 'receivingAssetId', + type: 'address' + }, + { + internalType: 'address', + name: 'receiver', + type: 'address' + }, + { + internalType: 'address', + name: 'refundee', + type: 'address' + }, + { + internalType: 'uint256', + name: 'minAmount', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'destinationChainId', + type: 'uint256' + }, + { + internalType: 'bool', + name: 'hasSourceSwaps', + type: 'bool' + }, + { + internalType: 'bool', + name: 'hasDestinationCall', + type: 'bool' + } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [ + { + internalType: 'uint256', + name: 'dstPoolId', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'minAmountLD', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'dstGasForCall', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'lzFee', + type: 'uint256' + }, + { + internalType: 'address payable', + name: 'refundAddress', + type: 'address' + }, + { + internalType: 'bytes', + name: 'callTo', + type: 'bytes' + }, + { + internalType: 'bytes', + name: 'callData', + type: 'bytes' + } + ], + internalType: 'struct StargateFacet.StargateData', + name: '_stargateData', + type: 'tuple' + } + ], + name: 'startBridgeTokensViaStargate', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes32', name: 'transactionId', type: 'bytes32' }, + { internalType: 'string', name: 'bridge', type: 'string' }, + { internalType: 'address', name: 'integrator', type: 'address' }, + { internalType: 'address', name: 'referrer', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'address', name: 'receiver', type: 'address' }, + { internalType: 'address', name: 'refundee', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'destinationChainId', type: 'uint256' }, + { internalType: 'bool', name: 'hasSourceSwaps', type: 'bool' }, + { internalType: 'bool', name: 'hasDestinationCall', type: 'bool' } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [ + { internalType: 'address', name: 'toChainToken', type: 'address' }, + { + internalType: 'uint256', + name: 'expectedToChainTokenAmount', + type: 'uint256' + }, + { internalType: 'uint32', name: 'slippage', type: 'uint32' } + ], + internalType: 'struct XYFacet.XYData', + name: '_xyData', + type: 'tuple' + } + ], + name: 'startBridgeTokensViaXY', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes32', name: 'transactionId', type: 'bytes32' }, + { internalType: 'string', name: 'bridge', type: 'string' }, + { internalType: 'address', name: 'integrator', type: 'address' }, + { internalType: 'address', name: 'referrer', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'address', name: 'receiver', type: 'address' }, + { internalType: 'address', name: 'refundee', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'destinationChainId', type: 'uint256' }, + { internalType: 'bool', name: 'hasSourceSwaps', type: 'bool' }, + { internalType: 'bool', name: 'hasDestinationCall', type: 'bool' } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [ + { internalType: 'address', name: 'callTo', type: 'address' }, + { internalType: 'address', name: 'approveTo', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'uint256', name: 'fromAmount', type: 'uint256' }, + { internalType: 'bytes', name: 'callData', type: 'bytes' }, + { internalType: 'bool', name: 'requiresDeposit', type: 'bool' } + ], + internalType: 'struct LibSwap.SwapData[]', + name: '_swapData', + type: 'tuple[]' + }, + { + components: [ + { internalType: 'address', name: 'toChainToken', type: 'address' }, + { + internalType: 'uint256', + name: 'expectedToChainTokenAmount', + type: 'uint256' + }, + { internalType: 'uint32', name: 'slippage', type: 'uint32' } + ], + internalType: 'struct XYFacet.XYData', + name: '_xyData', + type: 'tuple' + } + ], + name: 'swapAndStartBridgeTokensViaXY', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [], + name: 'approvedDexs', + outputs: [{ internalType: 'address[]', name: 'addresses', type: 'address[]' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + components: [ + { + internalType: 'bytes32', + name: 'transactionId', + type: 'bytes32' + }, + { + internalType: 'string', + name: 'bridge', + type: 'string' + }, + { + internalType: 'address', + name: 'integrator', + type: 'address' + }, + { + internalType: 'address', + name: 'referrer', + type: 'address' + }, + { + internalType: 'address', + name: 'sendingAssetId', + type: 'address' + }, + { + internalType: 'address', + name: 'receivingAssetId', + type: 'address' + }, + { + internalType: 'address', + name: 'receiver', + type: 'address' + }, + { + internalType: 'address', + name: 'refundee', + type: 'address' + }, + { + internalType: 'uint256', + name: 'minAmount', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'destinationChainId', + type: 'uint256' + }, + { + internalType: 'bool', + name: 'hasSourceSwaps', + type: 'bool' + }, + { + internalType: 'bool', + name: 'hasDestinationCall', + type: 'bool' + } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [ + { + internalType: 'address', + name: 'router', + type: 'address' + }, + { + internalType: 'address', + name: 'approveTo', + type: 'address' + }, + { + internalType: 'uint256', + name: 'extraNative', + type: 'uint256' + }, + { + internalType: 'bytes', + name: 'callData', + type: 'bytes' + } + ], + internalType: 'struct GenericCrossChainFacet.GenericCrossChainData', + name: '_genericData', + type: 'tuple' + } + ], + name: 'startBridgeTokensViaGenericCrossChain', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { + internalType: 'bytes32', + name: 'transactionId', + type: 'bytes32' + }, + { + internalType: 'string', + name: 'bridge', + type: 'string' + }, + { + internalType: 'address', + name: 'integrator', + type: 'address' + }, + { + internalType: 'address', + name: 'referrer', + type: 'address' + }, + { + internalType: 'address', + name: 'sendingAssetId', + type: 'address' + }, + { + internalType: 'address', + name: 'receivingAssetId', + type: 'address' + }, + { + internalType: 'address', + name: 'receiver', + type: 'address' + }, + { + internalType: 'address', + name: 'refundee', + type: 'address' + }, + { + internalType: 'uint256', + name: 'minAmount', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'destinationChainId', + type: 'uint256' + }, + { + internalType: 'bool', + name: 'hasSourceSwaps', + type: 'bool' + }, + { + internalType: 'bool', + name: 'hasDestinationCall', + type: 'bool' + } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [ + { + internalType: 'address', + name: 'callTo', + type: 'address' + }, + { + internalType: 'address', + name: 'approveTo', + type: 'address' + }, + { + internalType: 'address', + name: 'sendingAssetId', + type: 'address' + }, + { + internalType: 'address', + name: 'receivingAssetId', + type: 'address' + }, + { + internalType: 'uint256', + name: 'fromAmount', + type: 'uint256' + }, + { + internalType: 'bytes', + name: 'callData', + type: 'bytes' + }, + { + internalType: 'bool', + name: 'requiresDeposit', + type: 'bool' + } + ], + internalType: 'struct LibSwap.SwapData[]', + name: '_swapData', + type: 'tuple[]' + }, + { + components: [ + { + internalType: 'address', + name: 'router', + type: 'address' + }, + { + internalType: 'address', + name: 'approveTo', + type: 'address' + }, + { + internalType: 'uint256', + name: 'extraNative', + type: 'uint256' + }, + { + internalType: 'bytes', + name: 'callData', + type: 'bytes' + } + ], + internalType: 'struct GenericCrossChainFacet.GenericCrossChainData', + name: '_genericData', + type: 'tuple' + } + ], + name: 'swapAndStartBridgeTokensViaGenericCrossChain', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [{ internalType: 'address', name: '_contract', type: 'address' }], + name: 'isContractApproved', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'bytes4', name: '_signature', type: 'bytes4' }], + name: 'isFunctionApproved', + outputs: [{ internalType: 'bool', name: 'approved', type: 'bool' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: '_router', type: 'address' }, + { internalType: 'bytes4', name: '_selector', type: 'bytes4' } + ], + name: 'getSelectorInfo', + outputs: [ + { + components: [ + { internalType: 'bool', name: 'isAvailable', type: 'bool' }, + { internalType: 'uint256', name: 'offset', type: 'uint256' } + ], + internalType: 'struct LibMappings.ProviderFunctionInfo', + name: '', + type: 'tuple' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes32', name: 'transactionId', type: 'bytes32' }, + { internalType: 'string', name: 'bridge', type: 'string' }, + { internalType: 'address', name: 'integrator', type: 'address' }, + { internalType: 'address', name: 'referrer', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'address', name: 'receiver', type: 'address' }, + { internalType: 'address', name: 'refundee', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'destinationChainId', type: 'uint256' }, + { internalType: 'bool', name: 'hasSourceSwaps', type: 'bool' }, + { internalType: 'bool', name: 'hasDestinationCall', type: 'bool' } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [ + { + internalType: 'address', + name: 'receiver', + type: 'address' + }, + { + internalType: 'bytes', + name: 'data', + type: 'bytes' + } + ], + internalType: 'struct TransferAndCallData', + name: '_transferAndCallData', + type: 'tuple' + } + ], + name: 'startBridgeTokensViaTransferAndCall', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes32', name: 'transactionId', type: 'bytes32' }, + { internalType: 'string', name: 'bridge', type: 'string' }, + { internalType: 'address', name: 'integrator', type: 'address' }, + { internalType: 'address', name: 'referrer', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'address', name: 'receiver', type: 'address' }, + { internalType: 'address', name: 'refundee', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'destinationChainId', type: 'uint256' }, + { internalType: 'bool', name: 'hasSourceSwaps', type: 'bool' }, + { internalType: 'bool', name: 'hasDestinationCall', type: 'bool' } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [ + { internalType: 'address', name: 'callTo', type: 'address' }, + { internalType: 'address', name: 'approveTo', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'uint256', name: 'fromAmount', type: 'uint256' }, + { internalType: 'bytes', name: 'callData', type: 'bytes' }, + { internalType: 'bool', name: 'requiresDeposit', type: 'bool' } + ], + internalType: 'struct LibSwap.SwapData[]', + name: '_swapData', + type: 'tuple[]' + }, + { + components: [ + { + internalType: 'address', + name: 'receiver', + type: 'address' + }, + { + internalType: 'bytes', + name: 'data', + type: 'bytes' + } + ], + internalType: 'struct TransferAndCallData', + name: '_transferAndCallData', + type: 'tuple' + } + ], + name: 'swapAndStartBridgeTokensViaTransferAndCall', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes32', name: 'transactionId', type: 'bytes32' }, + { internalType: 'string', name: 'bridge', type: 'string' }, + { internalType: 'address', name: 'integrator', type: 'address' }, + { internalType: 'address', name: 'referrer', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'address', name: 'receiver', type: 'address' }, + { internalType: 'address', name: 'refundee', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'destinationChainId', type: 'uint256' }, + { internalType: 'bool', name: 'hasSourceSwaps', type: 'bool' }, + { internalType: 'bool', name: 'hasDestinationCall', type: 'bool' } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [ + { internalType: 'address payable', name: 'destination', type: 'address' } + ], + internalType: 'struct TransferFacet.TransferData', + name: '_transferData', + type: 'tuple' + } + ], + name: 'startBridgeTokensViaTransfer', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes32', name: 'transactionId', type: 'bytes32' }, + { internalType: 'string', name: 'bridge', type: 'string' }, + { internalType: 'address', name: 'integrator', type: 'address' }, + { internalType: 'address', name: 'referrer', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'address', name: 'receiver', type: 'address' }, + { internalType: 'address', name: 'refundee', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'destinationChainId', type: 'uint256' }, + { internalType: 'bool', name: 'hasSourceSwaps', type: 'bool' }, + { internalType: 'bool', name: 'hasDestinationCall', type: 'bool' } + ], + internalType: 'struct IRubic.BridgeData', + name: '_bridgeData', + type: 'tuple' + }, + { + components: [ + { internalType: 'address', name: 'callTo', type: 'address' }, + { internalType: 'address', name: 'approveTo', type: 'address' }, + { internalType: 'address', name: 'sendingAssetId', type: 'address' }, + { internalType: 'address', name: 'receivingAssetId', type: 'address' }, + { internalType: 'uint256', name: 'fromAmount', type: 'uint256' }, + { internalType: 'bytes', name: 'callData', type: 'bytes' }, + { internalType: 'bool', name: 'requiresDeposit', type: 'bool' } + ], + internalType: 'struct LibSwap.SwapData[]', + name: '_swapData', + type: 'tuple[]' + }, + { + components: [ + { internalType: 'address payable', name: 'destination', type: 'address' } + ], + internalType: 'struct TransferFacet.TransferData', + name: '_transferData', + type: 'tuple' + } + ], + name: 'swapAndStartBridgeTokensViaTransfer', + outputs: [], + stateMutability: 'payable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi.ts new file mode 100644 index 0000000..3079686 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi.ts @@ -0,0 +1,15 @@ +import { AbiItem } from 'web3-utils'; + +export const gatewayRubicCrossChainAbi: AbiItem[] = [ + { + inputs: [ + { internalType: 'address[]', name: 'tokens', type: 'address[]' }, + { internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }, + { internalType: 'bytes', name: 'facetCallData', type: 'bytes' } + ], + name: 'startViaRubic', + outputs: [], + stateMutability: 'payable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade.ts new file mode 100644 index 0000000..b276419 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade.ts @@ -0,0 +1,324 @@ +import BigNumber from 'bignumber.js'; +import { + FailedToCheckForTransactionReceiptError, + UnnecessaryApproveError +} from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { getGasOptions } from 'src/common/utils/options'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { EvmBasicTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-basic-transaction-options'; +import { EvmTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-transaction-options'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { CrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { TransactionConfig } from 'web3-core'; +import { TransactionReceipt } from 'web3-eth'; + +export abstract class EvmCrossChainTrade extends CrossChainTrade { + public abstract readonly from: PriceTokenAmount; + + /** + * Gas fee info in source blockchain. + */ + public abstract readonly gasData: GasData; + + protected get fromWeb3Public(): EvmWeb3Public { + return Injector.web3PublicService.getWeb3Public(this.from.blockchain); + } + + protected get web3Private(): EvmWeb3Private { + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(this.from.blockchain); + } + + /** + * Gets gas fee in source blockchain. + */ + public get estimatedGas(): BigNumber | null { + if (!this.gasData) { + return null; + } + + if (this.gasData.baseFee && this.gasData.maxPriorityFeePerGas) { + return Web3Pure.fromWei(this.gasData.baseFee).plus( + Web3Pure.fromWei(this.gasData.maxPriorityFeePerGas) + ); + } + + if (this.gasData.gasPrice) { + return Web3Pure.fromWei(this.gasData.gasPrice).multipliedBy(this.gasData.gasLimit); + } + + return null; + } + + public async approve( + options: EvmBasicTransactionOptions, + checkNeedApprove = true, + amount: BigNumber | 'infinity' = 'infinity' + ): Promise { + if (checkNeedApprove) { + const needApprove = await this.needApprove(); + if (!needApprove) { + throw new UnnecessaryApproveError(); + } + } + + this.checkWalletConnected(); + await this.checkBlockchainCorrect(); + + const approveAmount = + this.from.blockchain === BLOCKCHAIN_NAME.GNOSIS || + this.from.blockchain === BLOCKCHAIN_NAME.CRONOS + ? this.from.weiAmount + : amount; + + const fromTokenAddress = + this.from.isNative && this.from.blockchain === BLOCKCHAIN_NAME.METIS + ? '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000' + : this.from.address; + + return this.web3Private.approveTokens( + fromTokenAddress, + this.fromContractAddress, + approveAmount, + options + ); + } + + protected async checkAllowanceAndApprove( + options?: Omit + ): Promise { + const needApprove = await this.needApprove(); + if (!needApprove) { + return; + } + + const approveOptions: EvmBasicTransactionOptions = { + onTransactionHash: options?.onApprove, + gas: options?.approveGasLimit, + gasPriceOptions: options?.gasPriceOptions + }; + + await this.approve(approveOptions, false); + } + + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + await this.checkTradeErrors(); + await this.checkAllowanceAndApprove(options); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + try { + const config = await this.setTransactionConfig( + false, + options.useCacheData || false, + options?.receiverAddress + ); + if ('data' in config) { + const { data, value, to } = config; + await this.web3Private.trySendTransaction(to, { + data, + value, + onTransactionHash, + gas: gasLimit, + gasPriceOptions + }); + return transactionHash!; + } + + throw new Error('Invalid transaction config'); + } catch (err) { + throw err; + } + } + + /** + * + * @returns txHash(srcTxHash) | never + */ + public async swap(options: SwapTransactionOptions = {}): Promise { + if (!options?.testMode) { + await this.checkTradeErrors(); + } + await this.checkReceiverAddress( + options.receiverAddress, + !BlockchainsInfo.isEvmBlockchainName(this.to.blockchain) + ); + const method = options?.testMode ? 'sendTransaction' : 'trySendTransaction'; + + const fromAddress = this.walletAddress; + + const { data, value, to } = await this.encode({ ...options, fromAddress }); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + try { + await this.web3Private[method](to, { + data, + value, + onTransactionHash, + gas: gasLimit, + gasPriceOptions, + ...(options?.useEip155 && { + chainId: `0x${blockchainId[this.from.blockchain].toString(16)}` + }) + }); + + return transactionHash!; + } catch (err) { + if (err instanceof FailedToCheckForTransactionReceiptError) { + return transactionHash!; + } + throw err; + } + } + + private async swapWithParams(options: SwapTransactionOptions = {}): Promise { + await this.checkTradeErrors(); + await this.checkReceiverAddress( + options.receiverAddress, + !BlockchainsInfo.isEvmBlockchainName(this.to.blockchain) + ); + + await this.checkAllowanceAndApprove(options); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + const { contractAddress, contractAbi, methodName, methodArguments, value } = + await this.getContractParams(options, false); + + try { + let method: 'tryExecuteContractMethod' | 'executeContractMethod' = + 'tryExecuteContractMethod'; + if (options?.testMode) { + console.info( + contractAddress, + contractAbi, + methodName, + methodName, + value, + gasLimit, + gasPriceOptions + ); + method = 'executeContractMethod'; + } + + await this.web3Private[method]( + contractAddress, + contractAbi, + methodName, + methodArguments, + { + value, + onTransactionHash, + gas: gasLimit, + gasPriceOptions + } + ); + + return transactionHash!; + } catch (err) { + if (err instanceof FailedToCheckForTransactionReceiptError) { + return transactionHash!; + } + throw err; + } + } + + public async encode(options: EncodeTransactionOptions): Promise { + await this.checkFromAddress(options.fromAddress, true); + await this.checkReceiverAddress( + options.receiverAddress, + !BlockchainsInfo.isEvmBlockchainName(this.to.blockchain) + ); + + if (this.feeInfo?.rubicProxy?.fixedFee?.amount.gt(0)) { + return this.encodeProxy(options); + } + return this.setTransactionConfig( + options?.skipAmountCheck || false, + options?.useCacheData || false, + options?.receiverAddress || this.walletAddress + ); + } + + public async encodeApprove( + tokenAddress: string, + spenderAddress: string, + value: BigNumber | 'infinity', + options: EvmTransactionOptions = {} + ): Promise { + return this.web3Private.encodeApprove(tokenAddress, spenderAddress, value, options); + } + + protected abstract getContractParams( + options: GetContractParamsOptions, + skipAmountChangeCheck?: boolean + ): Promise; + + public getUsdPrice(providerFeeToken?: BigNumber): BigNumber { + let feeSum = new BigNumber(0); + const providerFee = this.feeInfo.provider?.cryptoFee; + if (providerFee) { + feeSum = feeSum.plus( + providerFee.amount.multipliedBy(providerFeeToken || providerFee.token.price) + ); + } + + return this.to.price.multipliedBy(this.to.tokenAmount).minus(feeSum); + } + + private async encodeProxy(options: EncodeTransactionOptions): Promise { + const { gasLimit } = options; + const { contractAddress, contractAbi, methodName, methodArguments, value } = + await this.getContractParams({ + fromAddress: options.fromAddress, + receiverAddress: options.receiverAddress || options.fromAddress, + useCacheData: options?.useCacheData || false + }); + + return EvmWeb3Pure.encodeMethodCall( + contractAddress, + contractAbi, + methodName, + methodArguments, + value, + { + gas: gasLimit || this.gasData?.gasLimit.toFixed(0), + ...getGasOptions(options) + } + ); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data.ts new file mode 100644 index 0000000..2aac06f --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data.ts @@ -0,0 +1,9 @@ +import BigNumber from 'bignumber.js'; + +export type GasData = { + readonly gasLimit: BigNumber; + readonly gasPrice?: BigNumber; + readonly baseFee?: BigNumber; + readonly maxFeePerGas?: BigNumber; + readonly maxPriorityFeePerGas?: BigNumber; +} | null; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/bridge-type.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/bridge-type.ts new file mode 100644 index 0000000..27978cf --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/bridge-type.ts @@ -0,0 +1,11 @@ +import { DEFAULT_BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/default-bridge-type'; +import { UNIQ_LIFI_BRIDGE_TYPES } from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/models/uniq-lifi-bridge-types'; + +export const BRIDGE_TYPE = { + ...DEFAULT_BRIDGE_TYPE, + ...UNIQ_LIFI_BRIDGE_TYPES +} as const; + +export type BridgeType = (typeof BRIDGE_TYPE)[keyof typeof BRIDGE_TYPE]; + +export const bridges = Object.values(BRIDGE_TYPE); diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/calculation-result.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/calculation-result.ts new file mode 100644 index 0000000..9d01758 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/calculation-result.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { TronTransactionConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-transaction-config'; +import { CrossChainTradeType } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-trade'; + +export type CalculationResult< + T = EvmEncodeConfig | null | TronTransactionConfig | { data: string } +> = + | { trade: CrossChainTrade; error?: RubicSdkError; tradeType: CrossChainTradeType } + | { trade: null; error: RubicSdkError; tradeType: CrossChainTradeType }; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/default-bridge-type.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/default-bridge-type.ts new file mode 100644 index 0000000..7cc5114 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/default-bridge-type.ts @@ -0,0 +1,49 @@ +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; + +export const DEFAULT_BRIDGE_TYPE = { + ...CROSS_CHAIN_TRADE_TYPE, + + ACROSS: 'across', + AMAROK: 'connext', + ANY_SWAP: 'anyswap', + ARBITRUM_BRIDGE: 'arbitrum', + AVALANCHE_BRIDGE: 'avalanche', + + CONNEXT: 'connext', + CELERIM: 'celerim', + + HOP: 'hop', + HYPHEN: 'hyphen', + + IBC: 'ibc', + + LI_FUEL: 'lifuel', + + MAKERS_WORMHOLE: 'maker', + MAYA_PROTOCOL: 'mayaprotocol', + MULTICHAIN: 'multichain', + + OPEN_OCEAN: 'openocean', + OPTIMISM_GATEWAY: 'optimism', + OSMOSIS_BRIDGE: 'osmosis', + + POLYGON: 'polygon', + + RAINBOW: 'rainbow', + REFUEL: 'refuel', + + SATELLITE: 'satellite', + STARGATE: 'stargate', + SYMBIOSIS: 'symbiosis', + SYNAPSE: 'synapse', + + THORCHAIN: 'thorchain', + + VOYAGER: 'voyager', + + WORMHOLE: 'wormhole', + + YPOOL: 'ypool' +} as const; + +export type DefaultBridgeType = (typeof DEFAULT_BRIDGE_TYPE)[keyof typeof DEFAULT_BRIDGE_TYPE]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/fee-info.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/fee-info.ts new file mode 100644 index 0000000..7ba29a3 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/fee-info.ts @@ -0,0 +1,51 @@ +import BigNumber from 'bignumber.js'; +import { PriceToken } from 'src/common/tokens'; + +/** + * Transaction fee information. + */ +export interface FeeInfo { + /** + * Fees, taken by cross-chain proxy or celer contract . + * Attached as additional amounts. + */ + rubicProxy?: { + /** + * Fixed crypto fee attached as additional value. + */ + fixedFee?: { + amount: BigNumber; + token: PriceToken; + }; + + /** + * Platform fee which is percent from token in amount. + */ + platformFee?: { + percent: number; + token: PriceToken; + }; + }; + + /** + * Fees, taken by provider. + * Already included in amounts. + */ + provider?: { + /** + * Crypto fee to pay swap in target network. + */ + cryptoFee?: { + amount: BigNumber; + token: PriceToken; + }; + + /** + * Platform fee which is percent from token in amount. + */ + platformFee?: { + percent: number; + token: PriceToken; + }; + }; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options.ts new file mode 100644 index 0000000..dcf814c --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options.ts @@ -0,0 +1,6 @@ +export interface GetContractParamsOptions { + fromAddress?: string; + receiverAddress?: string; + referrer?: string; + useCacheData?: boolean; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/on-chain-subtype.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/on-chain-subtype.ts new file mode 100644 index 0000000..6ddfc97 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/on-chain-subtype.ts @@ -0,0 +1,6 @@ +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +export interface OnChainSubtype { + from: OnChainTradeType | undefined; + to: OnChainTradeType | undefined; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/proxy-bridge-params.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/proxy-bridge-params.ts new file mode 100644 index 0000000..5a49678 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/proxy-bridge-params.ts @@ -0,0 +1,14 @@ +import { PriceTokenAmount } from 'src/common/tokens'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +export interface ProxyBridgeParams { + walletAddress: string; + fromTokenAmount: PriceTokenAmount; + toTokenAmount: PriceTokenAmount; + toAddress?: string; + srcChainTrade: EvmOnChainTrade | null; + providerAddress: string; + type: string; + fromAddress: string; + dstChainTrade?: EvmOnChainTrade; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/proxy-swap-params.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/proxy-swap-params.ts new file mode 100644 index 0000000..c3a5373 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/proxy-swap-params.ts @@ -0,0 +1,12 @@ +import { PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; + +export interface ProxySwapParams { + walletAddress: string; + contractAddress: string; + fromTokenAmount: PriceTokenAmount; + toTokenAmount: PriceTokenAmount; + onChainEncodeFn: (options: EncodeTransactionOptions) => Promise; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/rubicStep.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/rubicStep.ts new file mode 100644 index 0000000..d6b75ca --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/rubicStep.ts @@ -0,0 +1,18 @@ +import { Token, TokenAmount } from 'src/common/tokens'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +import { BridgeType } from './bridge-type'; + +export interface CrossChainStep { + provider: BridgeType; + type: 'cross-chain'; + path: (TokenAmount | Token)[]; +} + +interface OnChainStep { + path: Token[]; + provider: OnChainTradeType; + type: 'on-chain'; +} + +export type RubicStep = CrossChainStep | OnChainStep; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/trade-info.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/trade-info.ts new file mode 100644 index 0000000..e279cfd --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/trade-info.ts @@ -0,0 +1,11 @@ +import BigNumber from 'bignumber.js'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; + +export interface TradeInfo { + estimatedGas: BigNumber | null; + feeInfo: FeeInfo; + priceImpact: number | null; + slippage: number; + routePath: RubicStep[]; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/universal-contract.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/universal-contract.ts new file mode 100644 index 0000000..986b48a --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/universal-contract.ts @@ -0,0 +1,19 @@ +/** + * Describes universal contracts addresses. + */ +export interface UniversalContract { + /** + * Provider router contract address. + */ + readonly providerRouter: string; + + /** + * Provider gateway contract address. + */ + readonly providerGateway: string; + + /** + * Rubic router contract address. + */ + readonly rubicRouter: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/wrapped-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/wrapped-cross-chain-trade.ts new file mode 100644 index 0000000..b19f125 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/models/wrapped-cross-chain-trade.ts @@ -0,0 +1,22 @@ +import { RubicSdkError } from 'src/common/errors'; +import { CrossChainTradeType } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-trade'; + +export interface WrappedCrossChainTrade { + /** + * Calculated cross-chain trade. + * Sometimes trade can be calculated even if error was thrown. + * Equals `null` in case error is critical and trade cannot be calculated. + */ + trade: CrossChainTrade | null; + + /** + * Type of calculated trade. + */ + tradeType: CrossChainTradeType; + + /** + * Error, thrown during calculation. + */ + error?: RubicSdkError; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade.ts new file mode 100644 index 0000000..fd27512 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade.ts @@ -0,0 +1,392 @@ +import BigNumber from 'bignumber.js'; +import { UnapprovedContractError } from 'src/common/errors/proxy/unapproved-contract-error'; +import { UnapprovedMethodError } from 'src/common/errors/proxy/unapproved-method-error'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { TokenBaseStruct } from 'src/common/tokens/models/token-base-struct'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Web3PublicSupportedBlockchain } from 'src/core/blockchain/web3-public-service/models/web3-public-storage'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { ProxyBridgeParams } from 'src/features/cross-chain/calculation-manager/providers/common/models/proxy-bridge-params'; +import { ProxySwapParams } from 'src/features/cross-chain/calculation-manager/providers/common/models/proxy-swap-params'; +import { typedTradeProviders } from 'src/features/on-chain/calculation-manager/constants/trade-providers/typed-trade-providers'; +import { OnChainManager } from 'src/features/on-chain/calculation-manager/on-chain-manager'; +import { oneinchApiParams } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/constants'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { AbiItem, utf8ToHex } from 'web3-utils'; + +type BridgeParams = [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + number, + boolean, + boolean +]; + +export class ProxyCrossChainEvmTrade { + public static async getFeeInfo( + fromBlockchain: Web3PublicSupportedBlockchain, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + const fixedFeeAmount = useProxy + ? await ProxyCrossChainEvmTrade.getFixedFee( + fromBlockchain, + providerAddress, + rubicProxyContractAddress[fromBlockchain].router, + evmCommonCrossChainAbi + ) + : new BigNumber(0); + + const feePercent = useProxy + ? await ProxyCrossChainEvmTrade.getFeePercent( + fromBlockchain, + providerAddress, + rubicProxyContractAddress[fromBlockchain].router, + evmCommonCrossChainAbi + ) + : 0; + const nativeToken = await PriceToken.createFromToken(nativeTokensList[fromBlockchain]); + + return { + rubicProxy: { + fixedFee: { + amount: fixedFeeAmount, + token: nativeToken + }, + platformFee: { + percent: feePercent, + token: percentFeeToken + } + } + }; + } + + /** + * Gets fixed fee information. + * @param fromBlockchain Source network blockchain. + * @param providerAddress Integrator address. + * @param contractAddress Contract address. + * @param contractAbi Contract ABI. + * @protected + * @internal + */ + private static async getFixedFee( + fromBlockchain: Web3PublicSupportedBlockchain, + providerAddress: string, + contractAddress: string, + contractAbi: AbiItem[] + ): Promise { + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const fromChainType = BlockchainsInfo.getChainType(fromBlockchain); + const nativeToken = nativeTokensList[fromBlockchain]; + + if (!Web3Pure[fromChainType].isEmptyAddress(providerAddress)) { + const integratorInfo = await web3Public.callContractMethod<{ + isIntegrator: boolean; + fixedFeeAmount: string; + }>(contractAddress, contractAbi, 'integratorToFeeInfo', [providerAddress]); + if (integratorInfo.isIntegrator) { + return Web3Pure.fromWei(integratorInfo.fixedFeeAmount, nativeToken.decimals); + } + } + + return Web3Pure.fromWei( + await web3Public.callContractMethod( + contractAddress, + contractAbi, + 'fixedNativeFee' + ), + nativeToken.decimals + ); + } + + /** + * Gets percent fee. + * @param fromBlockchain Source network blockchain. + * @param providerAddress Integrator address. + * @param contractAddress Contract address. + * @param contractAbi Contract ABI. + * @protected + * @internal + */ + private static async getFeePercent( + fromBlockchain: Web3PublicSupportedBlockchain, + providerAddress: string, + contractAddress: string, + contractAbi: AbiItem[] + ): Promise { + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const fromChainType = BlockchainsInfo.getChainType(fromBlockchain); + + if (!Web3Pure[fromChainType].isEmptyAddress(providerAddress)) { + const integratorInfo = await web3Public.callContractMethod<{ + isIntegrator: boolean; + tokenFee: string; + }>(contractAddress, contractAbi, 'integratorToFeeInfo', [providerAddress]); + if (integratorInfo.isIntegrator) { + return new BigNumber(integratorInfo.tokenFee).toNumber() / 10_000; + } + } + + return ( + new BigNumber( + await web3Public.callContractMethod( + contractAddress, + contractAbi, + 'RubicPlatformFee' + ) + ).toNumber() / 10_000 + ); + } + + public static async getOnChainTrade( + from: PriceTokenAmount, + transitToken: TokenBaseStruct, + slippageTolerance: number, + isCustomWeth = false + ): Promise { + const to = await PriceToken.createToken(transitToken); + + if (compareAddresses(from.address, transitToken.address) && !from.isNative) { + return null; + } + + const fromBlockchain = from.blockchain as EvmBlockchainName; + + if (from.isNative) { + try { + const wrapToken = isCustomWeth + ? to.asStruct + : wrappedNativeTokensList[fromBlockchain]!; + + const toWrap = new PriceToken({ + ...wrapToken, + price: from.price + }); + + const trade = OnChainManager.getWrapTrade(from, toWrap, { + slippageTolerance + }); + + if (trade) { + return trade; + } + } catch {} + } + + const availableDexes = await ProxyCrossChainEvmTrade.getWhitelistedDexes(fromBlockchain); + + const dexes = Object.values(typedTradeProviders[fromBlockchain]); + + const allOnChainTrades = await Promise.allSettled( + dexes.map(dex => + dex.calculate(from, to, { + slippageTolerance, + gasCalculation: 'disabled', + useProxy: false, + usedForCrossChain: true + }) + ) + ); + const successSortedTrades = allOnChainTrades + .filter(value => value.status === 'fulfilled') + .map(value => (value as PromiseFulfilledResult).value) + .filter(onChainTrade => + availableDexes.some(availableDex => + compareAddresses(availableDex, onChainTrade.dexContractAddress) + ) + ) + .sort((a, b) => b.to.tokenAmount.comparedTo(a.to.tokenAmount)); + + if (!successSortedTrades.length) { + return null; + } + return successSortedTrades[0]!; + } + + public static async getWhitelistedDexes(fromBlockchain: EvmBlockchainName): Promise { + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + return web3Public.callContractMethod( + rubicProxyContractAddress[fromBlockchain].router, + evmCommonCrossChainAbi, + 'approvedDexs' + ); + } + + public static getBridgeData( + swapOptions: GetContractParamsOptions, + tradeParams: ProxyBridgeParams + ): BridgeParams { + const toChainId = blockchainId[tradeParams.toTokenAmount.blockchain] || 9999; + const fromToken = tradeParams.srcChainTrade + ? tradeParams.srcChainTrade.toTokenAmountMin + : tradeParams.fromTokenAmount; + const hasSwapBeforeBridge = tradeParams.srcChainTrade !== null; + const toAddress = tradeParams.toAddress || tradeParams.toTokenAmount.address; + const receiverAddress = ProxyCrossChainEvmTrade.getReceiverAddress( + swapOptions?.receiverAddress, + tradeParams.walletAddress, + toChainId + ); + + return [ + EvmWeb3Pure.randomHex(32), + tradeParams.type.toLowerCase(), + tradeParams.providerAddress, + ProxyCrossChainEvmTrade.getReferrerAddress(swapOptions?.referrer), + fromToken.isNative && fromToken.blockchain === BLOCKCHAIN_NAME.METIS + ? toAddress + : fromToken.address, + toAddress, + receiverAddress, + tradeParams.fromAddress, + fromToken.stringWeiAmount, + toChainId, + hasSwapBeforeBridge, + Boolean(tradeParams?.dstChainTrade) + ]; + } + + private static getReferrerAddress(referrer: string | undefined): string { + if (referrer) { + return '0x' + utf8ToHex(referrer).slice(2, 42).padStart(40, '0'); + } + + return '0x0000000000000000000000000000000000000000'; + } + + public static async getSwapData( + swapOptions: GetContractParamsOptions, + tradeParams: ProxySwapParams + ): Promise<[[string, string, string, string, string, string, boolean]]> { + const fromAddress = + swapOptions.fromAddress || tradeParams.walletAddress || oneinchApiParams.nativeAddress; + const swapData = await tradeParams.onChainEncodeFn({ + fromAddress, + receiverAddress: tradeParams.contractAddress, + supportFee: false + }); + + const routerAddress = swapData.to; + const signature = swapData.data.slice(0, 10); + + await ProxyCrossChainEvmTrade.checkDexWhiteList( + tradeParams.fromTokenAmount.blockchain, + routerAddress, + signature + ); + + return [ + [ + routerAddress, + routerAddress, + tradeParams.fromTokenAmount.address, + tradeParams.toTokenAmount.address, + tradeParams.fromTokenAmount.stringWeiAmount, + swapData.data, + true + ] + ]; + } + + public static async getGenericProviderData( + providerAddress: string, + providerData: string, + fromBlockchain: EvmBlockchainName, + gatewayAddress: string, + extraNative: string + ): Promise<[string, string, string, string]> { + await ProxyCrossChainEvmTrade.checkCrossChainWhiteList( + fromBlockchain, + providerAddress, + providerData.slice(0, 10) + ); + + return [providerAddress, gatewayAddress, extraNative, providerData]; + } + + public static async checkCrossChainWhiteList( + fromBlockchain: EvmBlockchainName, + routerAddress: string, + offset: string + ): Promise { + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const result = await web3Public.callContractMethod<{ isAvailable: boolean }>( + rubicProxyContractAddress[fromBlockchain].router, + evmCommonCrossChainAbi, + 'getSelectorInfo', + [routerAddress, offset] + ); + if (!result.isAvailable) { + throw new UnapprovedContractError(offset, routerAddress); + } + } + + public static async checkDexWhiteList( + fromBlockchain: EvmBlockchainName, + routerAddress: string, + method: string + ): Promise { + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + + let isRouterApproved = false; + try { + isRouterApproved = await web3Public.callContractMethod( + rubicProxyContractAddress[fromBlockchain].router, + evmCommonCrossChainAbi, + 'isContractApproved', + [routerAddress] + ); + } catch {} + if (!isRouterApproved) { + throw new UnapprovedContractError(method, routerAddress); + } + + let isMethodApproved = false; + try { + isMethodApproved = await web3Public.callContractMethod( + rubicProxyContractAddress[fromBlockchain].router, + evmCommonCrossChainAbi, + 'isFunctionApproved', + [method] + ); + } catch {} + if (!isMethodApproved) { + throw new UnapprovedMethodError(method, routerAddress); + } + } + + private static getReceiverAddress( + receiverAddress: string | undefined, + walletAddress: string, + toChainId: number + ): string { + if ( + toChainId === blockchainId[BLOCKCHAIN_NAME.BITCOIN] || + toChainId === blockchainId[BLOCKCHAIN_NAME.SOLANA] + ) { + return walletAddress; + } + + return receiverAddress || walletAddress; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/solana-cross-chain-trade/solana-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/solana-cross-chain-trade/solana-cross-chain-trade.ts new file mode 100644 index 0000000..ae66e47 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/solana-cross-chain-trade/solana-cross-chain-trade.ts @@ -0,0 +1,134 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { getGasOptions } from 'src/common/utils/options'; +import { BLOCKCHAIN_NAME, SolanaBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { EvmBasicTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-basic-transaction-options'; +import { EvmTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-transaction-options'; +import { SolanaWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/solana-web3-private/solana-web3-private'; +import { SolanaWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/solana-web3-public/solana-web3-public'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { CrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-trade'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { TransactionConfig } from 'web3-core'; +import { TransactionReceipt } from 'web3-eth'; + +export abstract class SolanaCrossChainTrade extends CrossChainTrade<{ data: string }> { + public abstract readonly from: PriceTokenAmount; + + protected get fromWeb3Public(): SolanaWeb3Public { + return Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.SOLANA); + } + + protected get web3Private(): SolanaWeb3Private { + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(BLOCKCHAIN_NAME.SOLANA); + } + + /** + * Gets gas fee in source blockchain. + */ + public get estimatedGas(): BigNumber | null { + return null; + } + + public async approve( + _options: EvmBasicTransactionOptions, + _checkNeedApprove = true, + _amount: BigNumber | 'infinity' = 'infinity' + ): Promise { + throw new Error('Method is not supported'); + } + + protected async checkAllowanceAndApprove( + options?: Omit + ): Promise { + const needApprove = await this.needApprove(); + if (!needApprove) { + return; + } + + const approveOptions: EvmBasicTransactionOptions = { + onTransactionHash: options?.onApprove, + gas: options?.approveGasLimit, + gasPriceOptions: options?.gasPriceOptions + }; + + await this.approve(approveOptions, false); + } + + protected abstract swapDirect(options?: SwapTransactionOptions): Promise; + + /** + * + * @returns txHash(srcTxHash) | never + */ + public async swap(options: SwapTransactionOptions = {}): Promise { + return this.swapDirect(options); + // There is no Rubic proxy contracts in Solana for now + // if (!this.isProxyTrade) { + // return this.swapDirect(options); + // } + // return this.swapWithParams(options); + } + + private async swapWithParams(_options: SwapTransactionOptions = {}): Promise { + throw new Error("Method is not supported');"); + } + + public async encode(options: EncodeTransactionOptions): Promise { + await this.checkFromAddress(options.fromAddress, true); + await this.checkReceiverAddress( + options.receiverAddress, + !BlockchainsInfo.isEvmBlockchainName(this.to.blockchain) + ); + + const { gasLimit } = options; + + const { contractAddress, contractAbi, methodName, methodArguments, value } = + await this.getContractParams({ + fromAddress: options.fromAddress, + receiverAddress: options.receiverAddress || options.fromAddress + }); + + return EvmWeb3Pure.encodeMethodCall( + contractAddress, + contractAbi, + methodName, + methodArguments, + value, + { + gas: gasLimit || '0', + ...getGasOptions(options) + } + ); + } + + public async encodeApprove( + _tokenAddress: string, + _spenderAddress: string, + _value: BigNumber | 'infinity', + _options: EvmTransactionOptions = {} + ): Promise { + throw new Error('Method is not supported'); + } + + protected abstract getContractParams( + options: GetContractParamsOptions + ): Promise; + + public getUsdPrice(providerFeeToken?: BigNumber): BigNumber { + let feeSum = new BigNumber(0); + const providerFee = this.feeInfo.provider?.cryptoFee; + if (providerFee) { + feeSum = feeSum.plus( + providerFee.amount.multipliedBy(providerFeeToken || providerFee.token.price) + ); + } + + return this.to.price.multipliedBy(this.to.tokenAmount).minus(feeSum); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/constants/tron-common-cross-chain-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/constants/tron-common-cross-chain-abi.ts new file mode 100644 index 0000000..6b44ee3 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/constants/tron-common-cross-chain-abi.ts @@ -0,0 +1,106 @@ +import { AbiItem } from 'web3-utils'; + +export const tronCommonCrossChainAbi = [ + { + inputs: [], + outputs: [{ type: 'uint256' }], + name: 'RubicPlatformFee', + stateMutability: 'View', + type: 'Function' + }, + { + inputs: [], + outputs: [{ type: 'uint256' }], + name: 'fixedCryptoFee', + stateMutability: 'View', + type: 'Function' + }, + { + inputs: [], + outputs: [{ type: 'address[]' }], + name: 'getAvailableRouters', + stateMutability: 'View', + type: 'Function' + }, + { + outputs: [ + { name: 'isIntegrator', type: 'bool' }, + { name: 'tokenFee', type: 'uint32' }, + { name: 'RubicTokenShare', type: 'uint32' }, + { name: 'RubicFixedCryptoShare', type: 'uint32' }, + { name: 'fixedFeeAmount', type: 'uint128' } + ], + inputs: [{ type: 'address' }], + name: 'integratorToFeeInfo', + stateMutability: 'View', + type: 'Function' + }, + { + outputs: [{ type: 'uint256' }], + inputs: [{ type: 'address' }], + name: 'maxTokenAmount', + stateMutability: 'View', + type: 'Function' + }, + { + outputs: [{ type: 'uint256' }], + inputs: [{ type: 'address' }], + name: 'minTokenAmount', + stateMutability: 'View', + type: 'Function' + }, + { + inputs: [], + outputs: [{ type: 'bool' }], + name: 'paused', + stateMutability: 'View', + type: 'Function' + }, + { + inputs: [ + { name: '_providerInfo', type: 'string' }, + { + name: '_params', + type: 'tuple', + components: [ + { name: 'srcInputToken', type: 'address' }, + { name: 'srcInputAmount', type: 'uint256' }, + { name: 'dstChainID', type: 'uint256' }, + { name: 'dstOutputToken', type: 'address' }, + { name: 'dstMinOutputAmount', type: 'uint256' }, + { name: 'recipient', type: 'address' }, + { name: 'integrator', type: 'address' }, + { name: 'router', type: 'address' } + ] + }, + { name: '_gateway', type: 'address' }, + { name: '_data', type: 'bytes' } + ], + name: 'routerCall', + stateMutability: 'Payable', + type: 'Function' + }, + { + inputs: [ + { name: '_providerInfo', type: 'string' }, + { + name: '_params', + type: 'tuple', + components: [ + { name: 'srcInputToken', type: 'address' }, + { name: 'srcInputAmount', type: 'uint256' }, + { name: 'dstChainID', type: 'uint256' }, + { name: 'dstOutputToken', type: 'address' }, + { name: 'dstMinOutputAmount', type: 'uint256' }, + { name: 'recipient', type: 'address' }, + { name: 'integrator', type: 'address' }, + { name: 'router', type: 'address' } + ] + }, + { name: '_data', type: 'bytes' } + ], + name: 'routerCallNative', + stateMutability: 'Payable', + type: 'Function' + } +] as unknown as AbiItem[]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/constants/tron-native-swap-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/constants/tron-native-swap-abi.ts new file mode 100644 index 0000000..945d6fa --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/constants/tron-native-swap-abi.ts @@ -0,0 +1,28 @@ +import { AbiItem } from 'web3-utils'; + +export const tronNativeSwapAbi: AbiItem[] = [ + { + inputs: [ + { internalType: 'address', name: 'fromToken', type: 'address' }, + { internalType: 'string', name: 'toToken', type: 'string' }, + { internalType: 'string', name: 'destination', type: 'string' }, + { internalType: 'uint256', name: 'fromAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'minReturnAmount', type: 'uint256' } + ], + name: 'swap', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'string', name: 'toToken', type: 'string' }, + { internalType: 'string', name: 'destination', type: 'string' }, + { internalType: 'uint256', name: 'minReturnAmount', type: 'uint256' } + ], + name: 'swapEth', + outputs: [], + stateMutability: 'payable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/models/tron-contract-params.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/models/tron-contract-params.ts new file mode 100644 index 0000000..053d4af --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/models/tron-contract-params.ts @@ -0,0 +1,5 @@ +import { ContractParams } from 'src/features/common/models/contract-params'; + +export interface TronContractParams extends ContractParams { + feeLimit: number; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/models/tron-get-contract-params-options.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/models/tron-get-contract-params-options.ts new file mode 100644 index 0000000..b4d6034 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/models/tron-get-contract-params-options.ts @@ -0,0 +1,4 @@ +export interface TronGetContractParamsOptions { + fromAddress?: string; + receiverAddress: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/tron-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/tron-cross-chain-trade.ts new file mode 100644 index 0000000..b7c9a7b --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/tron-cross-chain-trade.ts @@ -0,0 +1,157 @@ +import BigNumber from 'bignumber.js'; +import { + FailedToCheckForTransactionReceiptError, + UnnecessaryApproveError +} from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { TronBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { TronTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/tron-web3-private/models/tron-transaction-options'; +import { TronWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/tron-web3-private/tron-web3-private'; +import { TronWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/tron-web3-public'; +import { TronTransactionConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-transaction-config'; +import { TronWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/tron-web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { CrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-trade'; +import { TronContractParams } from 'src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/models/tron-contract-params'; +import { TronGetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/tron-cross-chain-trade/models/tron-get-contract-params-options'; +import { MarkRequired } from 'ts-essentials'; + +export abstract class TronCrossChainTrade extends CrossChainTrade { + public abstract readonly from: PriceTokenAmount; + + protected get fromWeb3Public(): TronWeb3Public { + return Injector.web3PublicService.getWeb3Public(this.from.blockchain); + } + + protected get web3Private(): TronWeb3Private { + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(this.from.blockchain); + } + + public async approve( + options: TronTransactionOptions, + checkNeedApprove = true, + amount: BigNumber | 'infinity' = 'infinity' + ): Promise { + if (checkNeedApprove) { + const needApprove = await this.needApprove(); + if (!needApprove) { + throw new UnnecessaryApproveError(); + } + } + + this.checkWalletConnected(); + await this.checkBlockchainCorrect(); + + return this.web3Private.approveTokens( + this.from.address, + this.fromContractAddress, + amount, + options + ); + } + + protected async checkAllowanceAndApprove( + options?: Omit + ): Promise { + const needApprove = await this.needApprove(); + if (!needApprove) { + return; + } + + const approveOptions: TronTransactionOptions = { + onTransactionHash: options?.onApprove, + feeLimit: options?.approveFeeLimit + }; + await this.approve(approveOptions, false); + } + + public async swap( + options: MarkRequired + ): Promise { + await this.checkTradeErrors(); + await this.checkReceiverAddress(options.receiverAddress, true); + + await this.checkAllowanceAndApprove(options); + + const { onConfirm } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + const { contractAddress, contractAbi, methodName, methodArguments, value, feeLimit } = + await this.getContractParams(options); + + try { + await this.web3Private.executeContractMethod( + contractAddress, + contractAbi, + methodName, + methodArguments, + { + onTransactionHash, + callValue: value, + feeLimit: options.feeLimit || feeLimit + } + ); + + return transactionHash!; + } catch (err) { + if (err instanceof FailedToCheckForTransactionReceiptError) { + return transactionHash!; + } + + throw err; + } + } + + public async encode( + options: MarkRequired + ): Promise { + await this.checkFromAddress(options.fromAddress, true); + await this.checkReceiverAddress(options.receiverAddress, true); + + const { contractAddress, contractAbi, methodName, methodArguments, value, feeLimit } = + await this.getContractParams({ + fromAddress: options.fromAddress, + receiverAddress: options.receiverAddress + }); + + return TronWeb3Pure.encodeMethodCall( + contractAddress, + contractAbi, + methodName, + methodArguments, + value, + options.feeLimit || feeLimit + ); + } + + public async encodeApprove( + tokenAddress: string, + spenderAddress: string, + value: BigNumber | 'infinity', + options: TronTransactionOptions = {} + ): Promise { + return this.web3Private.encodeApprove(tokenAddress, spenderAddress, value, options); + } + + protected abstract getContractParams( + options: TronGetContractParamsOptions + ): Promise; + + public getUsdPrice(): BigNumber { + let feeSum = new BigNumber(0); + const providerFee = this.feeInfo.provider?.cryptoFee; + if (providerFee) { + feeSum = feeSum.plus(providerFee.amount.multipliedBy(providerFee.token.price)); + } + + return this.to.price.multipliedBy(this.to.tokenAmount).minus(feeSum); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/chains/debridge-evm-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/chains/debridge-evm-cross-chain-trade.ts new file mode 100644 index 0000000..3f0d1bc --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/chains/debridge-evm-cross-chain-trade.ts @@ -0,0 +1,294 @@ +import BigNumber from 'bignumber.js'; +import { FailedToCheckForTransactionReceiptError, TooLowAmountError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { parseError } from 'src/common/utils/errors'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { DlnApiService } from 'src/features/common/providers/dln/dln-api-service'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { DeBridgeCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/constants/debridge-cross-chain-supported-blockchain'; +import { DebridgeEvmCrossChainTradeConstructor } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/debridge-cross-chain-trade-constructor'; +import { TransactionRequest } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-request'; +import { DlnEvmTransactionResponse } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-response'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +/** + * Calculated DeBridge cross-chain trade. + */ +export class DebridgeEvmCrossChainTrade extends EvmCrossChainTrade { + /** @internal */ + public readonly transitAmount: BigNumber; + + private readonly cryptoFeeToken: PriceTokenAmount; + + private readonly transactionRequest: TransactionRequest; + + private readonly slippage: number; + + private readonly onChainTrade: EvmOnChainTrade | null; + + /** @internal */ + public static async getGasData( + from: PriceTokenAmount, + toToken: PriceTokenAmount, + transactionRequest: TransactionRequest, + feeInfo: FeeInfo, + providerAddress: string, + receiverAddress?: string + ): Promise { + try { + const trade = new DebridgeEvmCrossChainTrade( + { + from, + to: toToken, + transactionRequest, + gasData: null, + priceImpact: 0, + allowanceTarget: '', + slippage: 0, + feeInfo, + transitAmount: new BigNumber(NaN), + toTokenAmountMin: new BigNumber(NaN), + cryptoFeeToken: from, + onChainTrade: null + }, + providerAddress || EvmWeb3Pure.EMPTY_ADDRESS, + [] + ); + return getCrossChainGasData(trade, receiverAddress); + } catch (_err) { + return null; + } + } + + public readonly type = CROSS_CHAIN_TRADE_TYPE.DEBRIDGE; + + public readonly isAggregator = false; + + public readonly onChainSubtype = { + from: ON_CHAIN_TRADE_TYPE.DLN, + to: ON_CHAIN_TRADE_TYPE.DLN + }; + + public readonly bridgeType = BRIDGE_TYPE.DEBRIDGE; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly priceImpact: number | null; + + public readonly allowanceTarget: string; + + public readonly gasData: GasData | null; + + private get fromBlockchain(): DeBridgeCrossChainSupportedBlockchain & EvmBlockchainName { + return this.from.blockchain as DeBridgeCrossChainSupportedBlockchain & EvmBlockchainName; + } + + protected get fromContractAddress(): string { + return this.isProxyTrade + ? rubicProxyContractAddress[this.fromBlockchain].gateway + : this.allowanceTarget; + } + + public readonly feeInfo: FeeInfo; + + protected get methodName(): string { + return 'startBridgeTokensViaGenericCrossChain'; + } + + constructor( + crossChainTrade: DebridgeEvmCrossChainTradeConstructor, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.transactionRequest = crossChainTrade.transactionRequest; + this.gasData = crossChainTrade.gasData; + this.priceImpact = crossChainTrade.priceImpact; + this.allowanceTarget = crossChainTrade.allowanceTarget || ''; + this.slippage = crossChainTrade.slippage; + this.onChainTrade = crossChainTrade.onChainTrade; + this.toTokenAmountMin = crossChainTrade.toTokenAmountMin; + this.feeInfo = crossChainTrade.feeInfo; + this.cryptoFeeToken = crossChainTrade.cryptoFeeToken; + + this.transitAmount = crossChainTrade.transitAmount; + } + + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + this.checkWalletConnected(); + await this.checkAllowanceAndApprove(options); + let transactionHash: string; + + try { + const { data, value, to } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress + ); + const { onConfirm } = options; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + await this.web3Private.trySendTransaction(to, { + onTransactionHash, + data, + value, + gas: options.gasLimit, + gasPriceOptions: options.gasPriceOptions + }); + + return transactionHash!; + } catch (err) { + if (err?.error?.errorId === 'ERROR_LOW_GIVE_AMOUNT') { + throw new TooLowAmountError(); + } + if (err instanceof FailedToCheckForTransactionReceiptError) { + return transactionHash!; + } + throw parseError(err); + } + } + + public async getContractParams(options: GetContractParamsOptions): Promise { + const { + data, + value: providerValue, + to + } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress + ); + + const isEvmDestination = BlockchainsInfo.isEvmBlockchainName(this.to.blockchain); + const receivingAsset = isEvmDestination ? this.to.address : this.from.address; + + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData(options, { + walletAddress: this.walletAddress, + fromTokenAmount: this.from, + toTokenAmount: this.to, + srcChainTrade: null, + providerAddress: this.providerAddress, + type: `native:${this.type}`, + fromAddress: this.walletAddress, + toAddress: receivingAsset + }); + const swapData = + this.onChainTrade && + (await ProxyCrossChainEvmTrade.getSwapData(options, { + walletAddress: this.walletAddress, + contractAddress: rubicProxyContractAddress[this.from.blockchain].router, + fromTokenAmount: this.from, + toTokenAmount: this.onChainTrade.to, + onChainEncodeFn: this.onChainTrade.encode.bind(this.onChainTrade) + })); + const fixFee = this.from.isNative + ? new BigNumber(providerValue).minus(this.from.stringWeiAmount).toFixed() + : new BigNumber(providerValue).toFixed(); + const providerData = await ProxyCrossChainEvmTrade.getGenericProviderData( + to, + data! as string, + this.fromBlockchain, + this.allowanceTarget, + fixFee + ); + + const methodArguments = swapData + ? [bridgeData, swapData, providerData] + : [bridgeData, providerData]; + + const value = this.getSwapValue(providerValue); + + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + const sameChain = + BlockchainsInfo.getChainType(this.from.blockchain) === + BlockchainsInfo.getChainType(this.to.blockchain); + const walletAddress = this.web3Private.address; + const params = { + ...this.transactionRequest, + ...(receiverAddress && { dstChainTokenOutRecipient: receiverAddress }), + // @TODO Check proxy when deBridge proxy returned + senderAddress: walletAddress, + srcChainRefundAddress: walletAddress, + dstChainOrderAuthorityAddress: sameChain + ? receiverAddress || walletAddress + : receiverAddress!, + srcChainOrderAuthorityAddress: sameChain + ? receiverAddress || walletAddress + : walletAddress, + referralCode: '4350' + }; + + const { tx, estimation } = + await DlnApiService.fetchCrossChainSwapData(params); + + return { config: tx, amount: estimation.dstChainTokenOut.maxTheoreticalAmount }; + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact ?? null, + slippage: this.slippage, + routePath: this.routePath + }; + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + const usdCryptoFee = this.cryptoFeeToken.price.multipliedBy( + this.cryptoFeeToken.tokenAmount + ); + + return fromUsd.plus(usdCryptoFee.isNaN() ? 0 : usdCryptoFee).dividedBy(this.to.tokenAmount); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/chains/debridge-solana-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/chains/debridge-solana-cross-chain-trade.ts new file mode 100644 index 0000000..502466a --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/chains/debridge-solana-cross-chain-trade.ts @@ -0,0 +1,184 @@ +import BigNumber from 'bignumber.js'; +import { FailedToCheckForTransactionReceiptError, TooLowAmountError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { parseError } from 'src/common/utils/errors'; +import { BlockchainName, SolanaBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { DlnApiService } from 'src/features/common/providers/dln/dln-api-service'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { SolanaCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/solana-cross-chain-trade/solana-cross-chain-trade'; +import { DeBridgeCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/constants/debridge-cross-chain-supported-blockchain'; +import { DebridgeSolanaCrossChainTradeConstructor } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/debridge-cross-chain-trade-constructor'; +import { TransactionRequest } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-request'; +import { DlnSolanaTransactionResponse } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-response'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +/** + * Calculated DeBridge cross-chain trade. + */ +export class DebridgeSolanaCrossChainTrade extends SolanaCrossChainTrade { + /** @internal */ + public readonly transitAmount: BigNumber; + + private readonly cryptoFeeToken: PriceTokenAmount; + + private readonly transactionRequest: TransactionRequest; + + private readonly slippage: number; + + public readonly type = CROSS_CHAIN_TRADE_TYPE.DEBRIDGE; + + public readonly isAggregator = false; + + public readonly onChainSubtype = { + from: ON_CHAIN_TRADE_TYPE.DLN, + to: ON_CHAIN_TRADE_TYPE.DLN + }; + + public readonly bridgeType = BRIDGE_TYPE.DEBRIDGE; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly priceImpact: number | null; + + private latestFixedFee: string | null = null; + + private get fromBlockchain(): DeBridgeCrossChainSupportedBlockchain & SolanaBlockchainName { + return this.from.blockchain as DeBridgeCrossChainSupportedBlockchain & SolanaBlockchainName; + } + + protected get fromContractAddress(): string { + if (this.isProxyTrade) { + throw new Error('Solana contract is not implemented yet'); + } + return rubicProxyContractAddress[this.fromBlockchain].gateway; + } + + public readonly feeInfo: FeeInfo; + + protected get methodName(): string { + return 'startBridgeTokensViaGenericCrossChain'; + } + + constructor( + crossChainTrade: DebridgeSolanaCrossChainTradeConstructor, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.transactionRequest = crossChainTrade.transactionRequest; + this.priceImpact = crossChainTrade.priceImpact; + this.slippage = crossChainTrade.slippage; + this.toTokenAmountMin = crossChainTrade.toTokenAmountMin; + this.feeInfo = crossChainTrade.feeInfo; + this.cryptoFeeToken = crossChainTrade.cryptoFeeToken; + + this.transitAmount = crossChainTrade.transitAmount; + } + + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + this.checkWalletConnected(); + await this.checkAllowanceAndApprove(options); + let transactionHash: string; + + try { + const { data } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress + ); + + const { onConfirm } = options; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + await this.web3Private.sendTransaction({ data, onTransactionHash }); + + return transactionHash!; + } catch (err) { + if (err?.error?.errorId === 'ERROR_LOW_GIVE_AMOUNT') { + throw new TooLowAmountError(); + } + if (err instanceof FailedToCheckForTransactionReceiptError) { + return transactionHash!; + } + throw parseError(err); + } + } + + public async getContractParams( + _options: GetContractParamsOptions, + _skipAmountChangeCheck: boolean = false + ): Promise { + throw new Error('Solana contracts is not implemented yet'); + } + + protected async getTransactionConfigAndAmount(receiverAddress?: string): Promise<{ + config: { data: string }; + amount: string; + }> { + const sameChain = + BlockchainsInfo.getChainType(this.from.blockchain) === + BlockchainsInfo.getChainType(this.to.blockchain); + const walletAddress = this.web3Private.address; + const params = { + ...this.transactionRequest, + ...(receiverAddress && { dstChainTokenOutRecipient: receiverAddress }), + senderAddress: walletAddress, + srcChainRefundAddress: walletAddress, + dstChainOrderAuthorityAddress: sameChain + ? receiverAddress || walletAddress + : receiverAddress!, + srcChainOrderAuthorityAddress: sameChain + ? receiverAddress || walletAddress + : walletAddress, + referralCode: '4350' + }; + + const { + tx: config, + fixFee, + estimation + } = await DlnApiService.fetchCrossChainSwapData(params); + this.latestFixedFee = Boolean(fixFee) ? fixFee : '0'; + + return { config, amount: estimation.dstChainTokenOut.maxTheoreticalAmount }; + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact ?? null, + slippage: this.slippage, + routePath: this.routePath + }; + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + const usdCryptoFee = this.cryptoFeeToken.price.multipliedBy( + this.cryptoFeeToken.tokenAmount + ); + + return fromUsd.plus(usdCryptoFee.isNaN() ? 0 : usdCryptoFee).dividedBy(this.to.tokenAmount); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/constants/debridge-cross-chain-supported-blockchain.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/constants/debridge-cross-chain-supported-blockchain.ts new file mode 100644 index 0000000..1d50dc1 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/constants/debridge-cross-chain-supported-blockchain.ts @@ -0,0 +1,16 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const deBridgeCrossChainSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.SOLANA +] as const; + +export type DeBridgeCrossChainSupportedBlockchain = + (typeof deBridgeCrossChainSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/debridge-cross-chain-factory.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/debridge-cross-chain-factory.ts new file mode 100644 index 0000000..324382d --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/debridge-cross-chain-factory.ts @@ -0,0 +1,38 @@ +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { CrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-trade'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { DebridgeEvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/chains/debridge-evm-cross-chain-trade'; +import { DebridgeSolanaCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/chains/debridge-solana-cross-chain-trade'; +import { + DebridgeCrossChainTradeConstructor, + DebridgeEvmCrossChainTradeConstructor, + DebridgeSolanaCrossChainTradeConstructor +} from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/debridge-cross-chain-trade-constructor'; + +export class DebridgeCrossChainFactory { + public static createTrade( + fromBlockchain: BlockchainName, + constructorParams: DebridgeCrossChainTradeConstructor, + providerAddress: string, + routePath: RubicStep[] + ): CrossChainTrade { + if (BlockchainsInfo.isSolanaBlockchainName(fromBlockchain)) { + return new DebridgeSolanaCrossChainTrade( + constructorParams as DebridgeSolanaCrossChainTradeConstructor, + providerAddress, + routePath + ); + } + + if (BlockchainsInfo.isEvmBlockchainName(fromBlockchain)) { + return new DebridgeEvmCrossChainTrade( + constructorParams as DebridgeEvmCrossChainTradeConstructor, + providerAddress, + routePath + ); + } + throw new Error('Can not create trade instance'); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/debridge-cross-chain-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/debridge-cross-chain-provider.ts new file mode 100644 index 0000000..11f939d --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/debridge-cross-chain-provider.ts @@ -0,0 +1,320 @@ +import BigNumber from 'bignumber.js'; +import { NotSupportedTokensError, RubicSdkError, TooLowAmountError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, TokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { DlnApiService } from 'src/features/common/providers/dln/dln-api-service'; +import { DlnUtils } from 'src/features/common/providers/dln/dln-utils'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { DebridgeEvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/chains/debridge-evm-cross-chain-trade'; +import { + DeBridgeCrossChainSupportedBlockchain, + deBridgeCrossChainSupportedBlockchains +} from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/constants/debridge-cross-chain-supported-blockchain'; +import { DebridgeCrossChainFactory } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/debridge-cross-chain-factory'; +import { Estimation } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/estimation-response'; +import { TransactionRequest } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-request'; +import { + DlnEvmTransactionResponse, + DlnSolanaTransactionResponse, + TransactionErrorResponse +} from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-response'; +import { DeflationTokenManager } from 'src/features/deflation-token-manager/deflation-token-manager'; +import { DlnOnChainSupportedBlockchain } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/constants/dln-on-chain-supported-blockchains'; +import { DlnOnChainSwapRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-request'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +export class DebridgeCrossChainProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.DEBRIDGE; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is DeBridgeCrossChainSupportedBlockchain { + return deBridgeCrossChainSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise> { + const fromBlockchain = from.blockchain; + const toBlockchain = toToken.blockchain; + const useProxy = options?.useProxy?.[this.type] ?? true; + + await this.checkDeflationTokens(from, toToken); + + if (!this.areSupportedBlockchains(fromBlockchain, toBlockchain)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + try { + const fakeAddress = DlnUtils.getFakeReceiver(toBlockchain); + + const feeInfo = await this.getFeeInfo( + fromBlockchain, + options.providerAddress, + from, + useProxy + ); + const fromWithoutFee = getFromWithoutFee( + from, + feeInfo.rubicProxy?.platformFee?.percent + ); + + const requestParams: TransactionRequest = { + ...this.getAffiliateFee(fromBlockchain), + srcChainId: blockchainId[fromBlockchain], + srcChainTokenIn: DlnUtils.getSupportedAddress(from), + srcChainTokenInAmount: fromWithoutFee.stringWeiAmount, + dstChainId: blockchainId[toBlockchain], + dstChainTokenOut: DlnUtils.getSupportedAddress(toToken), + dstChainTokenOutRecipient: this.getWalletAddress(fromBlockchain) || fakeAddress, + prependOperatingExpenses: false + }; + + const debridgeResponse = await DlnApiService.fetchCrossChainQuote< + DlnEvmTransactionResponse | DlnSolanaTransactionResponse + >(requestParams); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: Web3Pure.fromWei( + debridgeResponse.estimation.dstChainTokenOut.maxTheoreticalAmount, + debridgeResponse.estimation.dstChainTokenOut.decimals + ) + }); + + const toTokenAmountMin = Web3Pure.fromWei( + debridgeResponse.estimation.dstChainTokenOut.amount, + debridgeResponse.estimation.dstChainTokenOut.decimals + ); + const slippage = Number( + to.tokenAmount + .minus(toTokenAmountMin) + .dividedBy(toTokenAmountMin) + .multipliedBy(100) + .toFixed(2) + ); + + const transitToken = + debridgeResponse.estimation.srcChainTokenOut || + debridgeResponse.estimation.srcChainTokenIn; + + const nativeToken = nativeTokensList[fromBlockchain]; + const cryptoFeeToken = await PriceTokenAmount.createFromToken({ + ...nativeToken, + weiAmount: new BigNumber(debridgeResponse.fixFee) + }); + + const gasData = await this.getGasData( + { ...options, receiverAddress: fakeAddress }, + from, + to, + requestParams, + feeInfo + ); + + return { + trade: DebridgeCrossChainFactory.createTrade( + fromBlockchain, + { + from, + to, + transactionRequest: requestParams, + gasData, + priceImpact: from.calculatePriceImpactPercent(to), + allowanceTarget: ( + debridgeResponse?.tx as { allowanceTarget: string | undefined } + )?.allowanceTarget, + slippage, + feeInfo: { + ...feeInfo, + provider: { + cryptoFee: { + amount: Web3Pure.fromWei( + cryptoFeeToken.stringWeiAmount, + cryptoFeeToken.decimals + ), + token: cryptoFeeToken + } + } + }, + transitAmount: Web3Pure.fromWei(transitToken.amount, transitToken.decimals), + toTokenAmountMin, + cryptoFeeToken, + onChainTrade: null + }, + options.providerAddress, + await this.getRoutePath(debridgeResponse.estimation, from, to) + ), + tradeType: this.type + }; + } catch (err) { + const rubicSdkError = CrossChainProvider.parseError(err); + const debridgeApiError = this.parseDebridgeApiError(err); + + return { + trade: null, + error: debridgeApiError || rubicSdkError, + tradeType: this.type + }; + } + } + + protected async getFeeInfo( + fromBlockchain: DeBridgeCrossChainSupportedBlockchain, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } + + private parseDebridgeApiError(httpErrorResponse: { + error: TransactionErrorResponse; + }): RubicSdkError | null { + if ( + httpErrorResponse?.error?.errorId === 'INCLUDED_GAS_FEE_NOT_COVERED_BY_INPUT_AMOUNT' || + httpErrorResponse?.error?.errorId === 'ERROR_LOW_GIVE_AMOUNT' + ) { + return new TooLowAmountError(); + } + + // @TODO handle other debridge API error codes: + // CONNECTOR_1INCH_RETURNED_ERROR + // INCLUDED_GAS_FEE_CANNOT_BE_ESTIMATED_FOR_TRANSACTION_BUNDLE + + return null; + } + + protected async getRoutePath( + estimation: Estimation, + from: PriceTokenAmount, + to: PriceTokenAmount + ): Promise { + const fromChainId = String(blockchainId[from.blockchain]); + const toChainId = String(blockchainId[to.blockchain]); + + const transitFrom = [...estimation.costsDetails] + .reverse() + .find(el => el.chain === fromChainId); + const transitTo = estimation.costsDetails.find(el => el.chain === toChainId); + + try { + const fromTokenAmount = transitFrom + ? await TokenAmount.createToken({ + blockchain: from.blockchain, + address: transitFrom!.tokenOut, + weiAmount: new BigNumber(transitFrom!.amountOut) + }) + : from; + + const toTokenAmount = transitTo + ? await TokenAmount.createToken({ + blockchain: to.blockchain, + address: transitTo!.tokenIn, + weiAmount: new BigNumber(transitTo!.amountIn) + }) + : to; + + return [ + { + type: 'on-chain', + path: [from, fromTokenAmount], + provider: ON_CHAIN_TRADE_TYPE.DLN + }, + { + type: 'on-chain', + path: [toTokenAmount, to], + provider: ON_CHAIN_TRADE_TYPE.DLN + } + ]; + } catch { + return [ + { + type: 'cross-chain', + path: [from, to], + provider: CROSS_CHAIN_TRADE_TYPE.DEBRIDGE + } + ]; + } + } + + private async getGasData( + options: RequiredCrossChainOptions, + from: PriceTokenAmount, + to: PriceTokenAmount, + requestParams: TransactionRequest, + feeInfo: FeeInfo + ): Promise { + if (options.gasCalculation !== 'enabled') { + return null; + } + + const blockchain = from.blockchain; + const chainType = BlockchainsInfo.getChainType(blockchain); + + if (chainType === CHAIN_TYPE.EVM) { + return DebridgeEvmCrossChainTrade.getGasData( + from as PriceTokenAmount, + to as PriceTokenAmount, + requestParams, + feeInfo, + options.providerAddress, + options.receiverAddress + ); + } + // Chain is not supported + return null; + } + + private async checkDeflationTokens( + from: PriceTokenAmount, + toToken: PriceToken + ): Promise { + const deflationTokenManager = new DeflationTokenManager(); + await deflationTokenManager.checkToken(from); + await deflationTokenManager.checkToken(toToken); + } + + private getAffiliateFee( + fromBlockchain: DlnOnChainSupportedBlockchain + ): Partial> { + if (fromBlockchain === BLOCKCHAIN_NAME.SOLANA) { + return { + affiliateFeeRecipient: '4juPxgyQapaKdgxuCS7N8pRxjttXGRZsS5WTVZ42rNjn', + affiliateFeePercent: 0.1 + }; + } + return {}; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/debridge-cross-chain-trade-constructor.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/debridge-cross-chain-trade-constructor.ts new file mode 100644 index 0000000..c3cd9e2 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/debridge-cross-chain-trade-constructor.ts @@ -0,0 +1,36 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { + BlockchainName, + EvmBlockchainName, + SolanaBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { TransactionRequest } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-request'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +export interface DebridgeCrossChainTradeConstructor { + from: PriceTokenAmount; + to: PriceTokenAmount; + priceImpact: number | null; + slippage: number; + feeInfo: FeeInfo; + transitAmount: BigNumber; + toTokenAmountMin: BigNumber; + cryptoFeeToken: PriceTokenAmount; + + transactionRequest?: TransactionRequest; + gasData?: GasData | null; + allowanceTarget?: string; + onChainTrade?: EvmOnChainTrade | null; +} + +export type DebridgeEvmCrossChainTradeConstructor = Required< + DebridgeCrossChainTradeConstructor +>; + +export interface DebridgeSolanaCrossChainTradeConstructor + extends DebridgeCrossChainTradeConstructor { + transactionRequest: TransactionRequest; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/estimation-request.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/estimation-request.ts new file mode 100644 index 0000000..6c8bb90 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/estimation-request.ts @@ -0,0 +1,59 @@ +/** + * DeBridge api request params. + */ +export interface EstimationRequest { + /** + * Source chain ID. + */ + readonly srcChainId: number; + + /** + * Source token in address. + */ + readonly srcChainTokenIn: string; + + /** + * Source token in amount. + */ + readonly srcChainTokenInAmount: string; + + /** + * Swap slippage. + */ + readonly slippage?: number; + + /** + * Destination chain ID. + */ + readonly dstChainId: number; + + /** + * Destination token out amount. + */ + readonly dstChainTokenOut: string; + + /** + * Execution fee amount. + */ + readonly executionFeeAmount?: string; + + /** + * Token address of the execution fee. + */ + readonly executionFeeTokenAddress?: string; + + /** + * The estimated operator smart contract gas consumption. + */ + readonly dstBaseGasAmount?: number; + + /** + * Affiliate fee percent. + */ + readonly affiliateFeePercent?: number; + + /** + * Affiliate fee recipient address. + */ + readonly affiliateFeeRecipient?: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/estimation-response.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/estimation-response.ts new file mode 100644 index 0000000..5903885 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/estimation-response.ts @@ -0,0 +1,67 @@ +import { + DlnMaxTheoreticalAmountToken, + DlnTokenMinAmount +} from 'src/features/common/providers/dln/models/dln-estimation'; + +import { BaseToken } from '../../lifi-provider/models/lifi-cross-chain-token'; + +/** + * Estimation object. + */ +export interface Estimation { + readonly costsDetails: { + payload: { + feeAmount: string; + }; + type: string; + amountIn: string; + amountOut: string; + tokenIn: string; + tokenOut: string; + chain: string; + }[]; + /** + * Source chain token in. + */ + readonly srcChainTokenIn: DlnTokenMinAmount; + + /** + * Source chain token out. + */ + readonly srcChainTokenOut: DlnTokenMinAmount; + + /** + * Destination chain token out. + */ + readonly dstChainTokenOut: DlnMaxTheoreticalAmountToken; + + /** + * Details of the token representing execution fee currency, a recommended amount + * calculated by the planner, and an actual amount used during route construction. + */ + readonly executionFee: { + token: BaseToken; + recommendedAmount: string; + actualAmount: string; + }; +} + +/** + * Swap estimates response. + */ +export interface EstimationResponse { + /** + * Trade estimation object. + */ + estimation: Estimation; + + /** + * Provider fee. + */ + fixFee: string; + + /** + * Transaction data. + */ + tx: T; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-request.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-request.ts new file mode 100644 index 0000000..731baf9 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-request.ts @@ -0,0 +1,27 @@ +import { EstimationRequest } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/estimation-request'; + +/** + * Transaction request params. + */ +export interface TransactionRequest extends EstimationRequest { + /** + * The address target tokens should be transferred to after the swap. + */ + readonly dstChainTokenOutRecipient: string; + + /** + * The address target or intermediary tokens should be transferred in case of a failed swap + * (e.g., a swap may fail due to slippage constraints). + */ + readonly dstChainFallbackAddress?: string; + + /** + * Invitation code. + */ + readonly referralCode?: string; + + /** + * Tells API server to prepend operating expenses to the input amount. + */ + readonly prependOperatingExpenses?: boolean; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-response.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-response.ts new file mode 100644 index 0000000..03cbfa1 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-response.ts @@ -0,0 +1,62 @@ +import { Estimation } from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/estimation-response'; + +interface DlnEvmTransaction { + to: string; + data: string; + value: string; + allowanceTarget: string; + allowanceValue: string; +} + +interface DlnSolanaTransaction { + data: string; +} + +/** + * Swap transaction response. + */ +export interface TransactionResponse { + /** + * Trade estimation response. + */ + estimation: Estimation; + + /** + * Tells API server to prepend operating expenses to the input amount. + */ + prependedOperatingExpenseCost: string; + + /** + * Provider fee. + */ + fixFee: string; + + /** + * Transaction data. + */ + tx: T; +} + +export interface DlnEvmTransactionResponse extends TransactionResponse {} + +export interface DlnSolanaTransactionResponse extends TransactionResponse {} + +/** + * Swap transaction error response. + */ +export interface TransactionErrorResponse { + /** + * Error code. + */ + errorCode: number; + + /** + * Error ID. + */ + errorId: string; + + /** + * Error message. + */ + errorMessage: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/constants/algb-token-addresses.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/constants/algb-token-addresses.ts new file mode 100644 index 0000000..13f65ab --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/constants/algb-token-addresses.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { LayerZeroBridgeSupportedBlockchain } from '../models/layerzero-bridge-supported-blockchains'; + +export const ALGB_TOKEN: Record = { + [BLOCKCHAIN_NAME.ARBITRUM]: '0x9f018bda8f6b507a0c9e6f290b2f7c49c2f8daf8', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: '0xe374116f490b461764e2438f98eab3fff383367b', + [BLOCKCHAIN_NAME.POLYGON]: '0x0169ec1f8f639b32eec6d923e24c2a2ff45b9dd6' +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/constants/layerzero-bridge-address.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/constants/layerzero-bridge-address.ts new file mode 100644 index 0000000..cca3479 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/constants/layerzero-bridge-address.ts @@ -0,0 +1,10 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { LayerZeroBridgeSupportedBlockchain } from '../models/layerzero-bridge-supported-blockchains'; +import { ALGB_TOKEN } from './algb-token-addresses'; + +export const layerZeroProxyOFT: Record = { + [BLOCKCHAIN_NAME.ARBITRUM]: ALGB_TOKEN.ARBITRUM, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: ALGB_TOKEN.BSC, + [BLOCKCHAIN_NAME.POLYGON]: '0xDef87c507ef911Fd99c118c53171510Eb7967738' +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/constants/layzerzero-chain-ids.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/constants/layzerzero-chain-ids.ts new file mode 100644 index 0000000..641219a --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/constants/layzerzero-chain-ids.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { LayerZeroBridgeSupportedBlockchain } from '../models/layerzero-bridge-supported-blockchains'; + +export const layerZeroChainIds: Record = { + [BLOCKCHAIN_NAME.ARBITRUM]: '110', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: '102', + [BLOCKCHAIN_NAME.POLYGON]: '109' +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/layerzero-bridge-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/layerzero-bridge-provider.ts new file mode 100644 index 0000000..22229c5 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/layerzero-bridge-provider.ts @@ -0,0 +1,94 @@ +import { NotSupportedTokensError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CbridgeCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-supported-blockchains'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; + +import { LayerZeroBridgeTrade } from './layerzero-bridge-trade'; +import { + LayerZeroBridgeSupportedBlockchain, + layerZeroBridgeSupportedBlockchains +} from './models/layerzero-bridge-supported-blockchains'; + +export class LayerZeroBridgeProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.LAYERZERO; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is LayerZeroBridgeSupportedBlockchain { + return layerZeroBridgeSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public async calculate( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + const fromBlockchain = fromToken.blockchain as LayerZeroBridgeSupportedBlockchain; + const toBlockchain = toToken.blockchain as LayerZeroBridgeSupportedBlockchain; + + if (!this.areSupportedBlockchains(fromBlockchain, toBlockchain)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + try { + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: fromToken.tokenAmount + }); + + const gasData = + options.gasCalculation === 'enabled' + ? await LayerZeroBridgeTrade.getGasData(fromToken, to, options) + : null; + + return { + trade: new LayerZeroBridgeTrade( + { + from: fromToken, + to, + gasData + }, + options.providerAddress, + await this.getRoutePath(fromToken, to) + ), + tradeType: this.type + }; + } catch (err) { + const rubicSdkError = CrossChainProvider.parseError(err); + + return { + trade: null, + error: rubicSdkError, + tradeType: this.type + }; + } + } + + protected async getFeeInfo( + _fromBlockchain: CbridgeCrossChainSupportedBlockchain, + _providerAddress: string, + _percentFeeToken: PriceTokenAmount, + _useProxy: boolean + ): Promise { + return {}; + } + + protected async getRoutePath( + fromToken: PriceTokenAmount, + toToken: PriceTokenAmount + ): Promise { + return [{ type: 'cross-chain', provider: this.type, path: [fromToken, toToken] }]; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/layerzero-bridge-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/layerzero-bridge-trade.ts new file mode 100644 index 0000000..2dd143d --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/layerzero-bridge-trade.ts @@ -0,0 +1,222 @@ +import BigNumber from 'bignumber.js'; +import { solidityPack } from 'ethers/lib/utils'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; + +import { ALGB_TOKEN } from './constants/algb-token-addresses'; +import { layerZeroProxyOFT } from './constants/layerzero-bridge-address'; +import { layerZeroChainIds } from './constants/layzerzero-chain-ids'; +import { LayerZeroBridgeSupportedBlockchain } from './models/layerzero-bridge-supported-blockchains'; +import { layerZeroOFTABI } from './models/layerzero-oft-abi'; + +export class LayerZeroBridgeTrade extends EvmCrossChainTrade { + /** @internal */ + public static async getGasData( + from: PriceTokenAmount, + to: PriceTokenAmount, + options: SwapTransactionOptions + ): Promise { + const trade = new LayerZeroBridgeTrade( + { + from, + to, + gasData: { + gasLimit: new BigNumber(0), + gasPrice: new BigNumber(0) + } + }, + EvmWeb3Pure.EMPTY_ADDRESS, + [] + ); + return getCrossChainGasData(trade, options?.receiverAddress); + } + + public readonly onChainSubtype = { from: undefined, to: undefined }; + + public readonly type = CROSS_CHAIN_TRADE_TYPE.LAYERZERO; + + public readonly isAggregator = false; + + public readonly bridgeType = BRIDGE_TYPE.LAYERZERO; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly gasData: GasData | null; + + private get fromBlockchain(): LayerZeroBridgeSupportedBlockchain { + return this.from.blockchain as LayerZeroBridgeSupportedBlockchain; + } + + private get toBlockchain(): LayerZeroBridgeSupportedBlockchain { + return this.to.blockchain as LayerZeroBridgeSupportedBlockchain; + } + + protected get fromContractAddress(): string { + return layerZeroProxyOFT[this.fromBlockchain]; + } + + public readonly feeInfo: FeeInfo = {}; + + public readonly onChainTrade = null; + + protected get methodName(): string { + return 'sendFrom'; + } + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + gasData: GasData | null; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.gasData = crossChainTrade.gasData; + this.toTokenAmountMin = crossChainTrade.to.tokenAmount; + } + + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + await this.checkTradeErrors(); + await this.checkAllowanceAndApprove(options); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + // eslint-disable-next-line no-useless-catch + try { + const { data, value, to } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options.receiverAddress + ); + + const tx = await this.web3Private.trySendTransaction(to, { + data, + value, + gas: gasLimit, + gasPriceOptions + }); + + onTransactionHash(tx.transactionHash); + + return transactionHash!; + } catch (err) { + throw err; + } + } + + public async getContractParams(options: SwapTransactionOptions): Promise { + const account = this.web3Private.address; + + const fee = await this.estimateSendFee(options); + + const methodArguments = [ + account, + layerZeroChainIds[this.toBlockchain], + options.receiverAddress || account, + this.from.stringWeiAmount, + options.receiverAddress || account, + '0x0000000000000000000000000000000000000000', + '0x' + ]; + + return { + contractAddress: + this.fromBlockchain === BLOCKCHAIN_NAME.POLYGON + ? layerZeroProxyOFT[BLOCKCHAIN_NAME.POLYGON] + : ALGB_TOKEN[this.fromBlockchain], + contractAbi: layerZeroOFTABI, + methodName: this.methodName, + methodArguments, + value: fee || '0x' + }; + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + const params = await this.getContractParams({ receiverAddress }); + const config = EvmWeb3Pure.encodeMethodCall( + params.contractAddress, + params.contractAbi, + params.methodName, + params.methodArguments, + params.value + ); + return { config, amount: this.to.stringWeiAmount }; + } + + private async estimateSendFee(options: SwapTransactionOptions) { + const adapterParams = solidityPack( + ['uint16', 'uint256'], + [1, this.toBlockchain === BLOCKCHAIN_NAME.ARBITRUM ? 2_000_000 : 200_000] + ); + + const params = { + contractAddress: + this.fromBlockchain === BLOCKCHAIN_NAME.POLYGON + ? layerZeroProxyOFT[BLOCKCHAIN_NAME.POLYGON] + : ALGB_TOKEN[this.fromBlockchain], + contractAbi: layerZeroOFTABI, + methodName: 'estimateSendFee', + methodArguments: [ + layerZeroChainIds[this.toBlockchain], + options.receiverAddress || this.web3Private.address, + this.from.stringWeiAmount, + false, + adapterParams + ], + value: '0' + }; + + const gasFee = await this.fromWeb3Public.callContractMethod( + params.contractAddress, + params.contractAbi, + params.methodName, + params.methodArguments + ); + + return gasFee[0]; + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + return fromUsd.dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: null, + slippage: 0, + routePath: this.routePath + }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/models/layerzero-bridge-supported-blockchains.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/models/layerzero-bridge-supported-blockchains.ts new file mode 100644 index 0000000..31a7902 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/models/layerzero-bridge-supported-blockchains.ts @@ -0,0 +1,10 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const layerZeroBridgeSupportedBlockchains = [ + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.ARBITRUM +] as const; + +export type LayerZeroBridgeSupportedBlockchain = + (typeof layerZeroBridgeSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/models/layerzero-oft-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/models/layerzero-oft-abi.ts new file mode 100644 index 0000000..7a947a7 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/layerzero-bridge/models/layerzero-oft-abi.ts @@ -0,0 +1,91 @@ +import { AbiItem } from 'web3-utils'; + +export const layerZeroOFTABI: AbiItem[] = [ + { + inputs: [ + { + internalType: 'uint16', + name: '_dstChainId', + type: 'uint16' + }, + { + internalType: 'bytes', + name: '_toAddress', + type: 'bytes' + }, + { + internalType: 'uint256', + name: '_amount', + type: 'uint256' + }, + { + internalType: 'bool', + name: '_useZro', + type: 'bool' + }, + { + internalType: 'bytes', + name: '_adapterParams', + type: 'bytes' + } + ], + name: 'estimateSendFee', + outputs: [ + { + internalType: 'uint256', + name: 'nativeFee', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'zroFee', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_from', + type: 'address' + }, + { + internalType: 'uint16', + name: '_dstChainId', + type: 'uint16' + }, + { + internalType: 'bytes', + name: '_toAddress', + type: 'bytes' + }, + { + internalType: 'uint256', + name: '_amount', + type: 'uint256' + }, + { + internalType: 'address payable', + name: '_refundAddress', + type: 'address' + }, + { + internalType: 'address', + name: '_zroPaymentAddress', + type: 'address' + }, + { + internalType: 'bytes', + name: '_adapterParams', + type: 'bytes' + } + ], + name: 'sendFrom', + outputs: [], + stateMutability: 'payable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/constants/lifi-cross-chain-supported-blockchain.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/constants/lifi-cross-chain-supported-blockchain.ts new file mode 100644 index 0000000..13db6cb --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/constants/lifi-cross-chain-supported-blockchain.ts @@ -0,0 +1,24 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const lifiCrossChainSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.MOONRIVER, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.GNOSIS, + BLOCKCHAIN_NAME.FUSE, + BLOCKCHAIN_NAME.MOONBEAM, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.POLYGON_ZKEVM, + BLOCKCHAIN_NAME.ZK_SYNC, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.MODE, + BLOCKCHAIN_NAME.SCROLL, + BLOCKCHAIN_NAME.MANTLE +] as const; + +export type LifiCrossChainSupportedBlockchain = (typeof lifiCrossChainSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/lifi-cross-chain-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/lifi-cross-chain-provider.ts new file mode 100644 index 0000000..a66fe31 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/lifi-cross-chain-provider.ts @@ -0,0 +1,346 @@ +import BigNumber from 'bignumber.js'; +import { MinAmountError, NotSupportedTokensError, RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, TokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { + BRIDGE_TYPE, + bridges, + BridgeType +} from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { + LifiCrossChainSupportedBlockchain, + lifiCrossChainSupportedBlockchains +} from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/constants/lifi-cross-chain-supported-blockchain'; +import { LifiCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/lifi-cross-chain-trade'; +import { + LIFI_API_CROSS_CHAIN_PROVIDERS, + LifiSubProvider +} from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-bridge-types'; +import { + LIFI_API_ON_CHAIN_PROVIDERS, + LifiApiOnChainTrade +} from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/constants/lifi-providers'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +import { FeeCost, LifiStep } from './models/lifi-fee-cost'; +import { Route, RouteOptions, RoutesRequest } from './models/lifi-route'; +import { LifiApiService } from './services/lifi-api-service'; +export class LifiCrossChainProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.LIFI; + + private readonly MIN_AMOUNT_USD = new BigNumber(30); + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is LifiCrossChainSupportedBlockchain { + return lifiCrossChainSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + const fromBlockchain = from.blockchain as LifiCrossChainSupportedBlockchain; + const toBlockchain = toToken.blockchain as LifiCrossChainSupportedBlockchain; + if (!this.areSupportedBlockchains(fromBlockchain, toBlockchain)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + const { disabledBridges, disabledDexes } = this.mapDisabledProviders( + options.lifiDisabledBridgeTypes || [] + ); + const routeOptions: RouteOptions = { + slippage: options.slippageTolerance, + order: 'RECOMMENDED', + allowSwitchChain: false, + bridges: { deny: disabledBridges }, + exchanges: { deny: disabledDexes }, + integrator: 'rubic' + }; + + const fromChainId = blockchainId[fromBlockchain]; + const toChainId = blockchainId[toBlockchain]; + + const feeInfo = await this.getFeeInfo( + fromBlockchain, + options.providerAddress, + from, + options?.useProxy?.[this.type] ?? true + ); + const fromWithoutFee = getFromWithoutFee(from, feeInfo.rubicProxy?.platformFee?.percent); + + const fromAddress = this.getWalletAddress(fromBlockchain); + const toAddress = options.receiverAddress || fromAddress; + const routesRequest: RoutesRequest = { + fromChainId, + fromAmount: fromWithoutFee.stringWeiAmount, + fromTokenAddress: from.address, + toChainId, + toTokenAddress: toToken.address, + options: routeOptions, + ...(fromAddress && { fromAddress }), + ...(toAddress && { toAddress }) + }; + + const result = await LifiApiService.getRoutes(routesRequest); + + const { routes } = result; + + const bestRoute = routes.find( + route => route.steps.length === 1 && !route.containsSwitchChain + ); + + if (!bestRoute) { + throw new RubicSdkError('No available routes'); + } + + const providerFee = bestRoute.steps[0]!.estimate.feeCosts?.find( + (el: FeeCost & { included?: boolean }) => el?.included === false + ); + const nativeToken = await PriceToken.createFromToken(nativeTokensList[from.blockchain]); + if (providerFee && providerFee.amount !== '0') { + feeInfo.provider = { + cryptoFee: { + amount: Web3Pure.fromWei( + new BigNumber(providerFee.amount), + providerFee.token.decimals + ), + token: nativeToken + } + }; + } + + from = new PriceTokenAmount({ + ...from.asStructWithAmount, + price: new BigNumber(bestRoute.fromAmountUSD).dividedBy(from.tokenAmount) + }); + const to = new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: new BigNumber(bestRoute.toAmount) + }); + + const priceImpact = from.calculatePriceImpactPercent(to); + + const gasData = + options.gasCalculation === 'enabled' + ? await LifiCrossChainTrade.getGasData( + from, + to, + bestRoute, + feeInfo, + options.slippageTolerance, + options.providerAddress, + options.receiverAddress + ) + : null; + + const { onChainType, bridgeType } = this.parseTradeTypes(bestRoute.steps); + + const trade = new LifiCrossChainTrade( + { + from, + to, + route: bestRoute, + gasData, + toTokenAmountMin: Web3Pure.fromWei(bestRoute.toAmountMin, to.decimals), + feeInfo, + priceImpact, + onChainSubtype: onChainType, + bridgeType: bridgeType || BRIDGE_TYPE.LIFI, + slippage: options.slippageTolerance + }, + options.providerAddress, + await this.getRoutePath(from, to, bestRoute) + ); + + try { + this.checkMinError(from); + } catch (err) { + return { + trade, + error: err, + tradeType: this.type + }; + } + + return { + trade, + tradeType: this.type + }; + } + + private checkMinError(from: PriceTokenAmount): void | never { + const fromUsdAmount = from.price.multipliedBy(from.tokenAmount); + if (fromUsdAmount.lt(this.MIN_AMOUNT_USD)) { + if (from.price.gt(0)) { + const minTokenAmount = this.MIN_AMOUNT_USD.multipliedBy(from.tokenAmount).dividedBy( + fromUsdAmount + ); + throw new MinAmountError(minTokenAmount, from.symbol); + } + throw new MinAmountError(this.MIN_AMOUNT_USD, 'USDC'); + } + } + + protected async getFeeInfo( + fromBlockchain: LifiCrossChainSupportedBlockchain, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } + + private parseTradeTypes(bestRouteSteps: LifiStep[]): { + onChainType: { from: OnChainTradeType | undefined; to: OnChainTradeType | undefined }; + bridgeType: BridgeType | undefined; + } { + if (!bestRouteSteps[0]) { + return { + onChainType: { from: undefined, to: undefined }, + bridgeType: undefined + }; + } + + const steps = bestRouteSteps[0].includedSteps; + + if (!steps[0]) { + return { + onChainType: { from: undefined, to: undefined }, + bridgeType: undefined + }; + } + + const sourceDex = + steps[0].action.fromChainId === steps[0].action.toChainId + ? steps?.[0].toolDetails.name.toLowerCase() + : undefined; + + const targetDex = steps + ?.slice(1) + ?.find(provider => provider.action.fromChainId === provider.action.toChainId) + ?.toolDetails.name.toLowerCase(); + + let subType = bestRouteSteps + ?.find(provider => provider.action.fromChainId !== provider.action.toChainId) + ?.tool.toLowerCase(); + + subType = subType === 'amarok' ? BRIDGE_TYPE.AMAROK : subType; + + const onChainType = { + from: sourceDex + ? LIFI_API_ON_CHAIN_PROVIDERS[sourceDex as LifiApiOnChainTrade] + : undefined, + to: targetDex + ? LIFI_API_ON_CHAIN_PROVIDERS[targetDex as LifiApiOnChainTrade] + : undefined + }; + const bridgeType = bridges.find(bridge => bridge.toLowerCase() === subType); + + return { + onChainType, + bridgeType + }; + } + + private mapDisabledProviders(disabledProviders: LifiSubProvider[]): { + disabledBridges: LifiSubProvider[]; + disabledDexes: LifiSubProvider[]; + } { + const disabledBridges = [] as LifiSubProvider[]; + const disabledDexes = [] as LifiSubProvider[]; + + for (let i = 0; i < disabledProviders.length; i++) { + const provider = disabledProviders[i] as LifiSubProvider; + const isBridge = Object.values(LIFI_API_CROSS_CHAIN_PROVIDERS).includes(provider); + if (isBridge) { + disabledBridges.push(provider); + continue; + } + const isDex = Object.keys(LIFI_API_ON_CHAIN_PROVIDERS).includes(provider); + if (isDex) { + disabledDexes.push(provider); + } + } + + return { disabledBridges, disabledDexes }; + } + + protected async getRoutePath( + from: PriceTokenAmount, + to: PriceTokenAmount, + route: Route + ): Promise { + const lifiSteps = (route.steps[0] as LifiStep).includedSteps; + const crossChainStep = lifiSteps.find(el => el.type === 'cross')!; + + const fromTransit = + crossChainStep.action?.fromAddress || crossChainStep.action.fromToken.address; + const toTransit = crossChainStep.action?.toAddress || crossChainStep.action.toToken.address; + + const fromTokenAmount = await TokenAmount.createToken({ + address: fromTransit, + blockchain: from.blockchain, + weiAmount: new BigNumber(crossChainStep.action.fromAmount) + }); + + const toTokenAmount = await TokenAmount.createToken({ + address: toTransit, + blockchain: to.blockchain, + weiAmount: new BigNumber(crossChainStep.estimate.toAmount) + }); + + // @TODO Add dex true provider and path + const routePath: RubicStep[] = []; + + if (lifiSteps?.[0]?.type === 'swap') { + routePath.push({ + type: 'on-chain', + path: [from, fromTokenAmount], + provider: ON_CHAIN_TRADE_TYPE.LIFI + }); + } + + routePath.push({ + type: 'cross-chain', + path: [fromTokenAmount, toTokenAmount], + provider: CROSS_CHAIN_TRADE_TYPE.LIFI + }); + + if (lifiSteps?.[2]?.type === 'swap') { + routePath.push({ + type: 'on-chain', + path: [toTokenAmount, to], + provider: ON_CHAIN_TRADE_TYPE.LIFI + }); + } + + return routePath; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/lifi-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/lifi-cross-chain-trade.ts new file mode 100644 index 0000000..763189e --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/lifi-cross-chain-trade.ts @@ -0,0 +1,311 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError, SwapRequestError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { Cache } from 'src/common/utils/decorators'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { + BRIDGE_TYPE, + BridgeType +} from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { OnChainSubtype } from 'src/features/cross-chain/calculation-manager/providers/common/models/on-chain-subtype'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { LifiCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/constants/lifi-cross-chain-supported-blockchain'; +import { LifiTransactionRequest } from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-transaction-request'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; + +import { Estimate } from './models/lifi-fee-cost'; +import { Route } from './models/lifi-route'; +import { LifiApiService } from './services/lifi-api-service'; + +/** + * Calculated Celer cross-chain trade. + */ +export class LifiCrossChainTrade extends EvmCrossChainTrade { + /** @internal */ + public static async getGasData( + from: PriceTokenAmount, + toToken: PriceTokenAmount, + route: Route, + feeInfo: FeeInfo, + slippage: number, + providerAddress: string, + receiverAddress?: string + ): Promise { + const trade = new LifiCrossChainTrade( + { + from, + to: toToken, + route, + gasData: null, + toTokenAmountMin: new BigNumber(0), + feeInfo, + priceImpact: from.calculatePriceImpactPercent(toToken) || 0, + onChainSubtype: { + from: undefined, + to: undefined + }, + bridgeType: BRIDGE_TYPE.LIFI, + slippage + }, + providerAddress || EvmWeb3Pure.EMPTY_ADDRESS, + [] + ); + + return getCrossChainGasData(trade, receiverAddress); + } + + public readonly type = CROSS_CHAIN_TRADE_TYPE.LIFI; + + public readonly isAggregator = true; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly gasData: GasData | null; + + private readonly route: Route; + + private readonly providerGateway: string; + + public readonly onChainSubtype: OnChainSubtype; + + public readonly bridgeType: BridgeType; + + public readonly priceImpact: number | null; + + public readonly feeInfo: FeeInfo; + + private readonly slippage: number; + + private get fromBlockchain(): LifiCrossChainSupportedBlockchain { + return this.from.blockchain as LifiCrossChainSupportedBlockchain; + } + + public get fromContractAddress(): string { + return this.isProxyTrade + ? rubicProxyContractAddress[this.fromBlockchain].gateway + : this.providerGateway; + } + + protected get methodName(): string { + return 'startBridgeTokensViaGenericCrossChain'; + } + + protected override get amountToCheck(): string { + return Web3Pure.toWei(this.toTokenAmountMin, this.to.decimals); + } + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + route: Route; + gasData: GasData | null; + toTokenAmountMin: BigNumber; + feeInfo: FeeInfo; + priceImpact: number | null; + onChainSubtype: OnChainSubtype; + bridgeType: BridgeType; + slippage: number; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.route = crossChainTrade.route; + this.providerGateway = this.route.steps[0]!.estimate.approvalAddress; + + this.gasData = crossChainTrade.gasData; + this.toTokenAmountMin = crossChainTrade.toTokenAmountMin; + this.feeInfo = crossChainTrade.feeInfo; + this.slippage = crossChainTrade.slippage; + this.priceImpact = crossChainTrade.priceImpact; + this.onChainSubtype = crossChainTrade.onChainSubtype; + this.bridgeType = crossChainTrade.bridgeType; + } + + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + try { + await this.checkTradeErrors(); + if (options.receiverAddress) { + throw new RubicSdkError('Receiver address not supported'); + } + + await this.checkAllowanceAndApprove(options); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + // eslint-disable-next-line no-useless-catch + try { + const { data, value, to } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress + ); + + await this.web3Private.trySendTransaction(to, { + data, + value, + onTransactionHash, + gas: gasLimit, + gasPriceOptions + }); + + return transactionHash!; + } catch (err) { + throw err; + } + } catch (err) { + if ([400, 500, 503].includes(err.code)) { + throw new SwapRequestError(); + } + throw err; + } + } + + public async getContractParams(options: GetContractParamsOptions): Promise { + const { + data, + value: providerValue, + to: providerRouter + } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress + ); + + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData(options, { + walletAddress: this.walletAddress, + fromTokenAmount: this.from, + toTokenAmount: this.to, + srcChainTrade: null, + providerAddress: this.providerAddress, + type: `lifi:${this.bridgeType}`, + fromAddress: this.walletAddress + }); + const extraNativeFee = this.from.isNative + ? new BigNumber(providerValue).minus(this.from.stringWeiAmount).toFixed() + : new BigNumber(providerValue).toFixed(); + const providerData = await ProxyCrossChainEvmTrade.getGenericProviderData( + providerRouter, + data!, + this.fromBlockchain, + providerRouter, + extraNativeFee + ); + + const methodArguments = [bridgeData, providerData]; + + const value = this.getSwapValue(providerValue); + + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + const firstStep = this.route.steps[0]!; + const step = { + ...firstStep, + action: { + ...firstStep.action, + fromAddress: this.walletAddress, + toAddress: receiverAddress || this.walletAddress + }, + execution: { + status: 'NOT_STARTED', + process: [ + { + message: 'Preparing transaction.', + startedAt: Date.now(), + status: 'STARTED', + type: 'CROSS_CHAIN' + } + ] + } + }; + + try { + const swapResponse: { transactionRequest: LifiTransactionRequest; estimate: Estimate } = + await LifiApiService.getQuote( + step.action.fromChainId, + step.action.toChainId, + step.action.fromToken.symbol, + step.action.toToken.symbol, + step.action.fromAmount, + step.action.fromAddress, + step.action.slippage + ); + return { + config: swapResponse.transactionRequest, + amount: swapResponse.estimate.toAmountMin + }; + } catch (err) { + if ('statusCode' in err && 'message' in err) { + throw new RubicSdkError(err.message); + } + throw err; + } + } + + @Cache({ + maxAge: 15_000 + }) + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + return fromUsd.dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact || null, + slippage: this.slippage * 100, + routePath: this.routePath + }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-bridge-types.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-bridge-types.ts new file mode 100644 index 0000000..e40cc6f --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-bridge-types.ts @@ -0,0 +1,33 @@ +export const LIFI_API_CROSS_CHAIN_PROVIDERS = { + ACROSS: 'across', + ARBITRUM: 'arbitrum', + CBRIDGE: 'cbridge', + HOP: 'hop', + HYPHEN: 'hyphen', + STARGATE: 'stargate', + ALLBRIDGE: 'allbridge', + OMNI_BRIDGE: 'omni', + GNOSIS_BRIDGE: 'gnosis', + CONNEXT_AMAROK: 'amarok', + CIRCLE_CELER_BRIDGE: 'celercircle', + CELERIM: 'celerim', + OPTIMISM: 'optimism', + SYMBIOSIS: 'symbiosis', + LI_FUEL: 'lifuel', + THOR_SWAP: 'thorswap' +} as const; + +export type LifiSubProvider = + (typeof LIFI_API_CROSS_CHAIN_PROVIDERS)[keyof typeof LIFI_API_CROSS_CHAIN_PROVIDERS]; + +export interface Bridge { + key: LifiSubProvider; + name: string; + logoURI: string; + bridgeUrl?: string; + discordUrl?: string; + supportUrl?: string; + docsUrl?: string; + explorerUrl?: string; + analyticsUrl?: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-cross-chain-token.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-cross-chain-token.ts new file mode 100644 index 0000000..22fa661 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-cross-chain-token.ts @@ -0,0 +1,15 @@ +export interface BaseToken { + chainId: number; + address: string; +} +export interface StaticToken extends BaseToken { + symbol: string; + decimals: number; + name: string; + coinKey?: string; + logoURI?: string; +} + +export interface LifiToken extends StaticToken { + priceUSD: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-exchange-tools.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-exchange-tools.ts new file mode 100644 index 0000000..84b8450 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-exchange-tools.ts @@ -0,0 +1,54 @@ +import { StaticToken } from './lifi-cross-chain-token'; + +export const exchangeTool = { + ONEINCH: '1inch', + PARASWAP: 'paraswap', + OPENOCEAN: 'openocean', + ZEROX: '0x', + DODO: 'dodo', + UNISWAP: 'uniswap', + SUSHISWAP: 'sushiswap', + QUICKSWAP: 'quickswap', + HONEYSWAP: 'honeyswap', + PANCAKESWAP: 'pancakeswap', + SPIRITSWAP: 'spiritswap', + SPOOKYSWAP: 'spookyswap', + SOULSWAP: 'soulswap', + PANGOLIN: 'pangolin', + SOLARBEAM: 'solarbeam', + STEALLASWAP: 'steallaswap', + BEAMSWAP: 'beamswap', + UBESWAP: 'ubeswap', + CRONASWAP: 'cronaswap', + DIFFUSION: 'diffusion', + CRONUS: 'cronus', + EVMOSWAP: 'evmoswap', + OKCSWAP: 'okcswap', + JSWAP: 'jswap', + SWAPR: 'swapr', + VOLTAGE: 'voltage', + TRISOLARIS: 'trisolaris', + WAGYUSWAP: 'wagyuswap', + KYBERSWAP: 'kyberswap', + ODOS: 'odos' +} as const; +export type ExchangeTool = (typeof exchangeTool)[keyof typeof exchangeTool]; +export interface ExchangeAggregator { + key: ExchangeTool; + name: string; + logoURI: string; + webUrl: string; +} +export interface Exchange { + key: string; + name: string; + chainId: number; + logoURI: string; + webUrl: string; + graph?: string; + tokenlistUrl: string; + routerAddress: string; + factoryAddress: string; + initCodeHash: string; + baseTokens: readonly StaticToken[]; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-fee-cost.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-fee-cost.ts new file mode 100644 index 0000000..7031e2e --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-fee-cost.ts @@ -0,0 +1,161 @@ +import { Bridge } from './lifi-bridge-types'; +import { Exchange, ExchangeAggregator } from './lifi-exchange-tools'; +import { LifiTransactionRequest } from './lifi-transaction-request'; +export type Substatus = + | 'WAIT_SOURCE_CONFIRMATIONS' + | 'WAIT_DESTINATION_TRANSACTION' + | 'BRIDGE_NOT_AVAILABLE' + | 'CHAIN_NOT_AVAILABLE' + | 'REFUND_IN_PROGRESS' + | 'UNKNOWN_ERROR' + | 'COMPLETED' + | 'PARTIAL' + | 'REFUNDED' + | 'NOT_PROCESSABLE_REFUND_NEEDED'; + +export interface FeeCost { + name: string; + description: string; + percentage: string; + token: Token; + amount: string; + amountUSD?: string; + included: boolean; +} +interface Token { + chainId: number; + address: string; + symbol: string; + decimals: number; + name: string; + coinKey?: string; + logoURI?: string; + priceUSD: string; +} + +export interface GasCost { + type: 'SUM' | 'APPROVE' | 'SEND' | 'FEE'; + price: string; + estimate: string; + limit: string; + amount: string; + amountUSD: string; + token: Token; +} +export interface Action { + fromChainId: number; + fromAmount: string; + fromToken: Token; + fromAddress?: string; + toChainId: number; + toToken: Token; + toAddress?: string; + slippage: number; +} +export interface Estimate { + tool: string; + fromAmount: string; + fromAmountUSD?: string; + toAmount: string; + toAmountMin: string; + toAmountUSD?: string; + approvalAddress: string; + feeCosts?: FeeCost[]; + gasCosts?: GasCost[]; + executionDuration: number; +} +export type Status = + | 'NOT_STARTED' + | 'STARTED' + | 'ACTION_REQUIRED' + | 'CHAIN_SWITCH_REQUIRED' + | 'PENDING' + | 'FAILED' + | 'DONE' + | 'RESUME' + | 'CANCELLED'; +export type ProcessType = + | 'TOKEN_ALLOWANCE' + | 'SWITCH_CHAIN' + | 'SWAP' + | 'CROSS_CHAIN' + | 'RECEIVING_CHAIN' + | 'TRANSACTION'; +export interface Process { + startedAt: number; + doneAt?: number; + failedAt?: number; + type: ProcessType; + status: Status; + substatus?: Substatus; + message?: string; + txHash?: string; + txLink?: string; + multisigTxHash?: string; + error?: { + code: string | number; + message: string; + htmlMessage?: string; + }; + [key: string]: unknown; +} +export interface Execution { + status: Status; + process: Array; + fromAmount?: string; + toAmount?: string; + toToken?: Token; + gasPrice?: string; + gasUsed?: string; + gasToken?: Token; + gasAmount?: string; + gasAmountUSD?: string; +} +export declare const emptyExecution: Execution; +export declare const _StepType: readonly ['lifi', 'swap', 'cross', 'protocol', 'custom']; +export type StepType = (typeof _StepType)[number]; +export type StepTool = string; +export interface StepBase { + id: string; + type: StepType; + tool: StepTool; + toolDetails: Pick; + integrator?: string; + referrer?: string; + action: Action; + estimate?: Estimate; + execution?: Execution; + transactionRequest?: LifiTransactionRequest; +} +export interface DestinationCallInfo { + toContractAddress: string; + toContractCallData: string; + toFallbackAddress: string; + callDataGasLimit: string; +} +export type CallAction = Action & DestinationCallInfo; +export interface SwapStep extends StepBase { + type: 'swap'; + action: Action; + estimate: Estimate; +} +export interface CrossStep extends StepBase { + type: 'cross'; + action: Action; + estimate: Estimate; +} +export interface ProtocolStep extends StepBase { + type: 'protocol'; + action: Action; + estimate: Estimate; +} +export interface CustomStep extends StepBase { + type: 'custom'; + action: CallAction; + estimate: Estimate; +} +type Step = SwapStep | CrossStep | CustomStep | ProtocolStep; +export interface LifiStep extends Omit { + type: 'lifi'; + includedSteps: Step[]; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-route.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-route.ts new file mode 100644 index 0000000..ec67cac --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-route.ts @@ -0,0 +1,88 @@ +import { LifiToken } from './lifi-cross-chain-token'; +import { Action, LifiStep } from './lifi-fee-cost'; + +export interface AllowDenyPrefer { + allow?: string[]; + deny?: string[]; + prefer?: string[]; +} +export interface Insurance { + state: 'INSURED' | 'INSURABLE' | 'NOT_INSURABLE'; + feeAmountUsd: string; +} +export declare const Orders: readonly ['RECOMMENDED', 'FASTEST', 'CHEAPEST', 'SAFEST']; +export type Order = (typeof Orders)[number]; +export interface RoutesRequest { + fromChainId: number; + fromAmount: string; + fromTokenAddress: string; + fromAddress?: string; + toChainId: number; + toTokenAddress: string; + toAddress?: string; + options?: RouteOptions; + fromAmountForGas?: string; +} +export interface RouteOptions { + order?: Order; + slippage?: number; + infiniteApproval?: boolean; + allowSwitchChain?: boolean; + integrator?: string; + allowDestinationCall?: boolean; + referrer?: string; + bridges?: AllowDenyPrefer; + exchanges?: AllowDenyPrefer; + fee?: number; + insurance?: boolean; + maxPriceImpact?: number; +} + +export interface Route { + id: string; + insurance: Insurance; + fromChainId: number; + fromAmountUSD: string; + fromAmount: string; + fromToken: LifiToken; + fromAddress?: string; + toChainId: number; + toAmountUSD: string; + toAmount: string; + toAmountMin: string; + toToken: LifiToken; + toAddress?: string; + gasCostUSD?: string; + containsSwitchChain?: boolean; + infiniteApproval?: boolean; + steps: LifiStep[]; + tags?: Order[]; +} +export type UnavailableRoutes = { + filteredOut: FilteredResult[]; + failed: ErroredRoute[]; +}; +export interface RoutesResponse { + routes: Route[]; + unavailableRoutes: UnavailableRoutes; +} +export type ErroredPaths = { + [subpath: string]: ToolError[]; +}; +export type ErroredRoute = { + overallPath: string; + subpaths: ErroredPaths; +}; +export type FilteredResult = { + overallPath: string; + reason: string; +}; + +export type ToolErrorType = 'NO_QUOTE'; +export interface ToolError { + errorType: ToolErrorType; + code: string; + action: Action; + tool: string; + message: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-swap-status.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-swap-status.ts new file mode 100644 index 0000000..eb39db0 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-swap-status.ts @@ -0,0 +1,9 @@ +export const LIFI_SWAP_STATUS = { + NOT_FOUND: 'NOT_FOUND', + INVALID: 'INVALID', + PENDING: 'PENDING', + DONE: 'DONE', + FAILED: 'FAILED' +} as const; + +export type LifiSwapStatus = (typeof LIFI_SWAP_STATUS)[keyof typeof LIFI_SWAP_STATUS]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-transaction-request.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-transaction-request.ts new file mode 100644 index 0000000..88029db --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-transaction-request.ts @@ -0,0 +1,5 @@ +export interface LifiTransactionRequest { + data: string; + value: string; + to: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/uniq-lifi-bridge-types.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/uniq-lifi-bridge-types.ts new file mode 100644 index 0000000..ebea5e3 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/models/uniq-lifi-bridge-types.ts @@ -0,0 +1,24 @@ +import { DEFAULT_BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/default-bridge-type'; +import { LIFI_API_CROSS_CHAIN_PROVIDERS } from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-bridge-types'; + +// @TODO add type +export const UNIQ_LIFI_BRIDGE_TYPES = { + ACROSS: DEFAULT_BRIDGE_TYPE.ACROSS, + ARBITRUM: DEFAULT_BRIDGE_TYPE.ARBITRUM_BRIDGE, + AVALANCHE: DEFAULT_BRIDGE_TYPE.AVALANCHE_BRIDGE, + CBRIDGE: LIFI_API_CROSS_CHAIN_PROVIDERS.CBRIDGE, + CONNEXT: DEFAULT_BRIDGE_TYPE.CONNEXT, + CELERIM: DEFAULT_BRIDGE_TYPE.CELERIM, + HOP: DEFAULT_BRIDGE_TYPE.HOP, + HYPHEN: DEFAULT_BRIDGE_TYPE.HYPHEN, + MULTICHAIN: DEFAULT_BRIDGE_TYPE.MULTICHAIN, + STARGATE: DEFAULT_BRIDGE_TYPE.STARGATE, + ALLBRIDGE: LIFI_API_CROSS_CHAIN_PROVIDERS.ALLBRIDGE, + POLYGON_BRIDGE: DEFAULT_BRIDGE_TYPE.POLYGON, + OMNI_BRIDGE: LIFI_API_CROSS_CHAIN_PROVIDERS.OMNI_BRIDGE, + GNOSIS_BRIDGE: LIFI_API_CROSS_CHAIN_PROVIDERS.GNOSIS_BRIDGE, + CONNEXT_AMAROK: LIFI_API_CROSS_CHAIN_PROVIDERS.CONNEXT_AMAROK, + CIRCLE_CELER_BRIDGE: LIFI_API_CROSS_CHAIN_PROVIDERS.CIRCLE_CELER_BRIDGE, + LI_FUEL: DEFAULT_BRIDGE_TYPE.LI_FUEL, + WORMHOLE: DEFAULT_BRIDGE_TYPE.WORMHOLE +} as const; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/services/lifi-api-service.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/services/lifi-api-service.ts new file mode 100644 index 0000000..7e796c9 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/lifi-provider/services/lifi-api-service.ts @@ -0,0 +1,52 @@ +import { Injector } from 'src/core/injector/injector'; + +import { Estimate } from '../models/lifi-fee-cost'; +import { RoutesRequest, RoutesResponse } from '../models/lifi-route'; +import { LifiTransactionRequest } from '../models/lifi-transaction-request'; + +export class LifiApiService { + private static LIFI_API_ENDPOINT = 'https://li.quest/v1'; + + public static LIFI_API_KEY = + '0a1eec2c-b1bd-4dc1-81cf-c988f099c929.f5950d26-5955-4e21-9db2-77ad984ea575'; + + public static async getQuote( + fromChain: number, + toChain: number, + fromToken: string, + toToken: string, + fromAmount: string, + fromAddress: string, + slippage: number + ): Promise<{ transactionRequest: LifiTransactionRequest; estimate: Estimate }> { + const result = await Injector.httpClient.get<{ + transactionRequest: LifiTransactionRequest; + estimate: Estimate; + }>(`${this.LIFI_API_ENDPOINT}/quote`, { + params: { + fromChain, + toChain, + fromToken, + toToken, + fromAmount, + fromAddress, + slippage, + integrator: 'rubic' + }, + headers: { 'x-lifi-api-key': this.LIFI_API_KEY } + }); + return result; + } + + public static async getRoutes(request: RoutesRequest): Promise { + const result = await Injector.httpClient.post( + `${this.LIFI_API_ENDPOINT}/advanced/routes`, + request, + { + headers: { 'x-lifi-api-key': this.LIFI_API_KEY } + } + ); + + return result; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/constants/meson-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/constants/meson-abi.ts new file mode 100644 index 0000000..d02b28c --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/constants/meson-abi.ts @@ -0,0 +1,15 @@ +import { AbiItem } from 'web3-utils'; + +export const MESON_ABI = [ + { + inputs: [ + { internalType: 'uint256', name: 'encodedSwap', type: 'uint256' }, + { internalType: 'uint200', name: 'postingValue', type: 'uint200' }, + { internalType: 'address', name: 'contractAddress', type: 'address' } + ], + name: 'postSwapFromContract', + outputs: [], + stateMutability: 'payable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/constants/meson-contract-addresses.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/constants/meson-contract-addresses.ts new file mode 100644 index 0000000..e5c4f5d --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/constants/meson-contract-addresses.ts @@ -0,0 +1,38 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { MesonSupportedBlockchain } from './meson-cross-chain-supported-chains'; + +export const mesonContractAddresses: Record = { + [BLOCKCHAIN_NAME.ARBITRUM]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.AVALANCHE]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.AURORA]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.BASE]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.BLAST]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.CELO]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.CRONOS]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.ETHEREUM]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.FANTOM]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.GNOSIS]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.KAVA]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.LINEA]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.MANTA_PACIFIC]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.MANTLE]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.MERLIN]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.METIS]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.MODE]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.MOONBEAM]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.MOONRIVER]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.OPTIMISM]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.POLYGON]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.SCROLL]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + // [BLOCKCHAIN_NAME.SOLANA]: 'FR1SDyLUj7PrMbtkUCkDrBymk5eWrRmr3UvWFb5Kjbmd', + // [BLOCKCHAIN_NAME.TRON]: 'TKWqpzNucNNBMpfaE47F8CLhA8vzfNndH4', + [BLOCKCHAIN_NAME.XLAYER]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.ZK_FAIR]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3', + [BLOCKCHAIN_NAME.ZK_LINK]: '0x2DcC88Fa6b6950EE28245C3238B8993BE5feeA42', + [BLOCKCHAIN_NAME.ZK_SYNC]: '0x2DcC88Fa6b6950EE28245C3238B8993BE5feeA42', + [BLOCKCHAIN_NAME.ZETACHAIN]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3' + // [BLOCKCHAIN_NAME.TAIKO]: '0x25aB3Efd52e6470681CE037cD546Dc60726948D3' +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/constants/meson-cross-chain-supported-chains.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/constants/meson-cross-chain-supported-chains.ts new file mode 100644 index 0000000..afbc9d3 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/constants/meson-cross-chain-supported-chains.ts @@ -0,0 +1,38 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const mesonCrossChainSupportedChains = [ + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.AURORA, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.MOONBEAM, + BLOCKCHAIN_NAME.BLAST, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.CELO, + BLOCKCHAIN_NAME.CRONOS, + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.GNOSIS, + BLOCKCHAIN_NAME.KAVA, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.MANTA_PACIFIC, + BLOCKCHAIN_NAME.MERLIN, + BLOCKCHAIN_NAME.METIS, + BLOCKCHAIN_NAME.MANTLE, + BLOCKCHAIN_NAME.MODE, + BLOCKCHAIN_NAME.MOONRIVER, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.SCROLL, + // BLOCKCHAIN_NAME.SOLANA, + // BLOCKCHAIN_NAME.TRON, + BLOCKCHAIN_NAME.XLAYER, + BLOCKCHAIN_NAME.ZETACHAIN, + BLOCKCHAIN_NAME.POLYGON_ZKEVM, + BLOCKCHAIN_NAME.ZK_FAIR, + BLOCKCHAIN_NAME.ZK_LINK, + BLOCKCHAIN_NAME.ZK_SYNC + // BLOCKCHAIN_NAME.TAIKO +] as const; + +export type MesonSupportedBlockchain = (typeof mesonCrossChainSupportedChains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/meson-cross-chain-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/meson-cross-chain-provider.ts new file mode 100644 index 0000000..d381b40 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/meson-cross-chain-provider.ts @@ -0,0 +1,239 @@ +import BigNumber from 'bignumber.js'; +import { + MaxAmountError, + MinAmountError, + NotSupportedBlockchain, + NotSupportedTokensError, + RubicSdkError +} from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import Web3 from 'web3'; + +import { RequiredCrossChainOptions } from '../../models/cross-chain-options'; +import { CrossChainProvider } from '../common/cross-chain-provider'; +import { BRIDGE_TYPE } from '../common/models/bridge-type'; +import { CalculationResult } from '../common/models/calculation-result'; +import { FeeInfo } from '../common/models/fee-info'; +import { RubicStep } from '../common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from '../common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { + mesonCrossChainSupportedChains, + MesonSupportedBlockchain +} from './constants/meson-cross-chain-supported-chains'; +import { MesonCrossChainTrade } from './meson-cross-chain-trade'; +import { MesonLimitsChain, MesonLimitsToken, SrcDstChainsIds } from './models/meson-api-types'; +import { FetchedMesonTradeInfo } from './models/meson-provider-types'; +import { MesonCcrApiService } from './services/meson-cross-chain-api-service'; + +export class MesonCrossChainProvider extends CrossChainProvider { + public readonly type = BRIDGE_TYPE.MESON; + + public isSupportedBlockchain(blockchain: EvmBlockchainName): boolean { + return mesonCrossChainSupportedChains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + const fromBlockchain = from.blockchain as MesonSupportedBlockchain; + const useProxy = options?.useProxy?.[this.type] ?? true; + + try { + if (!useProxy) { + throw new RubicSdkError('Meson only supports proxy swaps!'); + } + + const fromWith6Decimals = this.getFromWith6Decimals(from); + + const { max, min, mesonFee, sourceAssetString, targetAssetString } = + await this.fetchTradeInfo(fromWith6Decimals, toToken); + + const feeInfo = await this.getFeeInfo( + fromBlockchain, + options.providerAddress, + from, + useProxy + ); + + const fromWithoutFee = getFromWithoutFee( + fromWith6Decimals, + feeInfo.rubicProxy?.platformFee?.percent + ); + + const toAmount = from.tokenAmount.minus(mesonFee); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: toAmount + }); + + const gasData = + options.gasCalculation === 'enabled' + ? await MesonCrossChainTrade.getGasData({ + from: fromWithoutFee, + feeInfo, + toToken: to, + providerAddress: options.providerAddress, + sourceAssetString, + targetAssetString + }) + : null; + + const trade = new MesonCrossChainTrade({ + crossChainTrade: { + feeInfo, + from: fromWithoutFee, + gasData, + to, + priceImpact: from.calculatePriceImpactPercent(to), + sourceAssetString, + targetAssetString + }, + providerAddress: options.providerAddress, + routePath: await this.getRoutePath(from, to) + }); + + if (from.tokenAmount.lt(min)) { + return { + trade, + error: new MinAmountError(min, from.symbol), + tradeType: this.type + }; + } + if (from.tokenAmount.gt(max)) { + return { + trade, + error: new MaxAmountError(max, from.symbol), + tradeType: this.type + }; + } + + return { trade, tradeType: this.type }; + } catch (err) { + const rubicSdkError = CrossChainProvider.parseError(err); + + return { + trade: null, + error: rubicSdkError, + tradeType: this.type + }; + } + } + + private async fetchTradeInfo( + sourceToken: PriceTokenAmount, + targetToken: PriceToken + ): Promise { + const mesonChains = await MesonCcrApiService.fetchChainsLimits(); + + const sourceTokenInfo = this.getApiTokenInfo(sourceToken, mesonChains); + const targetTokenInfo = this.getApiTokenInfo(targetToken, mesonChains); + const mesonChainsSymbols = this.getTxChainsSymbols(sourceToken, targetToken, mesonChains); + + const sourceAssetString = `${mesonChainsSymbols[0]}:${sourceTokenInfo.id}`; + const targetAssetString = `${mesonChainsSymbols[1]}:${targetTokenInfo.id}`; + + const mesonFee = await MesonCcrApiService.fetchMesonFee( + sourceAssetString, + targetAssetString, + sourceToken.tokenAmount.toFixed() + ); + + return { + mesonFee, + sourceAssetString, + targetAssetString, + min: new BigNumber(sourceTokenInfo.min), + max: new BigNumber(sourceTokenInfo.max) + }; + } + + private getApiTokenInfo( + token: PriceToken, + apiChains: MesonLimitsChain[] + ): MesonLimitsToken { + const chainId = blockchainId[token.blockchain]; + const hexChainId = Web3.utils.toHex(chainId); + const foundChain = apiChains.find(chain => compareAddresses(chain.chainId, hexChainId)); + + if (!foundChain) { + throw new NotSupportedBlockchain(); + } + + const foundToken = token.isNative + ? foundChain.tokens.find(apiToken => !Object.hasOwn(apiToken, 'addr')) + : foundChain.tokens.find(apiToken => compareAddresses(apiToken.addr!, token.address)); + + if (!foundToken) { + throw new NotSupportedTokensError(); + } + + return foundToken; + } + + private getTxChainsSymbols( + sourceToken: PriceToken, + targetToken: PriceToken, + apiChains: MesonLimitsChain[] + ): SrcDstChainsIds { + const sourceChainIdHex = Web3.utils.toHex(blockchainId[sourceToken.blockchain]); + const targetChainIdHex = Web3.utils.toHex(blockchainId[targetToken.blockchain]); + const ids = ['', ''] as SrcDstChainsIds; + + for (const chain of apiChains) { + if (ids[0] && ids[1]) break; + if (compareAddresses(chain.chainId, sourceChainIdHex)) ids[0] = chain.id; + if (compareAddresses(chain.chainId, targetChainIdHex)) ids[1] = chain.id; + } + + return ids; + } + + /** + * Meson inputs only value with 6 or less decimals in swap request + */ + private getFromWith6Decimals( + from: PriceTokenAmount + ): PriceTokenAmount { + const stringAmount = from.tokenAmount.toFixed(); + const [, decimals] = stringAmount.split('.'); + if (!decimals || decimals.length <= 6) { + return from; + } + const amount = from.tokenAmount.decimalPlaces(6, BigNumber.ROUND_DOWN); + + return new PriceTokenAmount({ + ...from.asStruct, + tokenAmount: amount + }); + } + + protected async getRoutePath( + fromToken: PriceTokenAmount, + toToken: PriceTokenAmount + ): Promise { + return [{ type: 'cross-chain', provider: this.type, path: [fromToken, toToken] }]; + } + + protected async getFeeInfo( + fromBlockchain: MesonSupportedBlockchain, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/meson-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/meson-cross-chain-trade.ts new file mode 100644 index 0000000..92d74b4 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/meson-cross-chain-trade.ts @@ -0,0 +1,208 @@ +import BigNumber from 'bignumber.js'; +import { ethers } from 'ethers'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { ContractParams } from 'src/features/common/models/contract-params'; + +import { CROSS_CHAIN_TRADE_TYPE, CrossChainTradeType } from '../../models/cross-chain-trade-type'; +import { getCrossChainGasData } from '../../utils/get-cross-chain-gas-data'; +import { rubicProxyContractAddress } from '../common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from '../common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from '../common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from '../common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from '../common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE, BridgeType } from '../common/models/bridge-type'; +import { FeeInfo } from '../common/models/fee-info'; +import { GetContractParamsOptions } from '../common/models/get-contract-params-options'; +import { OnChainSubtype } from '../common/models/on-chain-subtype'; +import { TradeInfo } from '../common/models/trade-info'; +import { ProxyCrossChainEvmTrade } from '../common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { MESON_ABI } from './constants/meson-abi'; +import { mesonContractAddresses } from './constants/meson-contract-addresses'; +import { MesonSupportedBlockchain } from './constants/meson-cross-chain-supported-chains'; +import { + MesonCrossChainTradeConstructorParams, + MesonGetGasDataParams +} from './models/meson-trade-types'; +import { MesonCcrApiService } from './services/meson-cross-chain-api-service'; + +export class MesonCrossChainTrade extends EvmCrossChainTrade { + /** @internal */ + public static async getGasData({ + feeInfo, + from, + providerAddress, + sourceAssetString, + targetAssetString, + toToken + }: MesonGetGasDataParams): Promise { + try { + const trade = new MesonCrossChainTrade({ + crossChainTrade: { + from, + to: toToken, + gasData: null, + priceImpact: 0, + feeInfo, + sourceAssetString, + targetAssetString + }, + providerAddress: providerAddress || EvmWeb3Pure.EMPTY_ADDRESS, + routePath: [] + }); + + return getCrossChainGasData(trade); + } catch (_err) { + return null; + } + } + + /**ABSTRACT PROPS */ + public readonly type: CrossChainTradeType = CROSS_CHAIN_TRADE_TYPE.MESON; + + public readonly isAggregator: boolean = false; + + public readonly to: PriceTokenAmount; + + public readonly from: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly feeInfo: FeeInfo; + + public readonly onChainSubtype: OnChainSubtype = { from: undefined, to: undefined }; + + public readonly bridgeType: BridgeType = BRIDGE_TYPE.MESON; + + public readonly gasData: GasData; + + public readonly priceImpact: number | null; + /** */ + + /* Used in swap-request, example `bnb:usdc` */ + private readonly sourceAssetString: string; + + private readonly targetAssetString: string; + + private get fromBlockchain(): MesonSupportedBlockchain { + return this.from.blockchain as MesonSupportedBlockchain; + } + + protected get fromContractAddress(): string { + return this.isProxyTrade + ? rubicProxyContractAddress[this.fromBlockchain].gateway + : mesonContractAddresses[this.fromBlockchain]; + } + + protected get methodName(): string { + return 'startBridgeTokensViaGenericCrossChain'; + } + + constructor(params: MesonCrossChainTradeConstructorParams) { + super(params.providerAddress, params.routePath); + this.to = params.crossChainTrade.to; + this.from = params.crossChainTrade.from; + this.feeInfo = params.crossChainTrade.feeInfo; + this.gasData = params.crossChainTrade.gasData; + this.priceImpact = params.crossChainTrade.priceImpact; + this.toTokenAmountMin = this.to.tokenAmount; + this.sourceAssetString = params.crossChainTrade.sourceAssetString; + this.targetAssetString = params.crossChainTrade.targetAssetString; + } + + public async getContractParams(options: GetContractParamsOptions): Promise { + const receiverAddress = options?.receiverAddress || this.walletAddress; + const { + data, + value: providerValue, + to: providerRouter + } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress || this.walletAddress + ); + + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData(options, { + walletAddress: receiverAddress, + fromTokenAmount: this.from, + toTokenAmount: this.to, + srcChainTrade: null, + providerAddress: this.providerAddress, + type: `native:${this.bridgeType}`, + fromAddress: this.walletAddress + }); + + const extraNativeFee = '0'; + const providerData = await ProxyCrossChainEvmTrade.getGenericProviderData( + providerRouter, + data, + this.from.blockchain, + providerRouter, + extraNativeFee + ); + + const methodArguments = [bridgeData, providerData]; + const value = this.getSwapValue(providerValue); + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + const rubicMultiProxyAddress = rubicProxyContractAddress[this.fromBlockchain].router; + const fromAddress = this.isProxyTrade ? rubicMultiProxyAddress : this.walletAddress; + + const { encoded, initiator } = await MesonCcrApiService.fetchInfoForTx({ + sourceAssetString: this.sourceAssetString, + targetAssetString: this.targetAssetString, + amount: this.from.tokenAmount.toFixed(), + fromAddress, + receiverAddress: receiverAddress || this.walletAddress, + useProxy: this.isProxyTrade + }); + const postingValue = ethers.utils.solidityPack(['address', 'uint40'], [initiator, 1]); + + const methodName = 'postSwapFromContract'; + const methodArgs = [encoded, postingValue, rubicMultiProxyAddress]; + const value = this.from.isNative ? this.from.stringWeiAmount : '0'; + + const config = EvmWeb3Pure.encodeMethodCall( + mesonContractAddresses[this.fromBlockchain], + MESON_ABI, + methodName, + methodArgs, + value + ); + + return { config, amount: this.to.stringWeiAmount }; + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact || null, + slippage: 0, + routePath: this.routePath + }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/models/meson-api-types.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/models/meson-api-types.ts new file mode 100644 index 0000000..6fd8871 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/models/meson-api-types.ts @@ -0,0 +1,112 @@ +export type MesonErrorRes = { + error: { + error: { + code: number; + data: { + swapData: T; + }; + message: string; + }; + }; +}; + +export type MesonSuccessRes = { + result: T; +}; + +export type SrcDstChainsIds = [string, string]; + +export interface MesonChainsInfo { + result: MesonChain[]; +} + +export interface MesonChain { + /* Meson-api chain symbol */ + id: string; + name: string; + chainId: string; + /* meson contract address in chain */ + address: string; + tokens: Array<{ id: string; addr?: string }>; +} + +export interface MesonLimitsResponse { + result: MesonLimitsChain[]; +} + +export interface MesonLimitsChain { + id: string; + name: string; + /* in hex */ + chainId: string; + /* meson contract address in chain */ + address: string; + tokens: MesonLimitsToken[]; +} + +export interface MesonLimitsToken { + /* Meson-api token symbol */ + id: string; + /* addr is absent for native currency */ + addr?: string; + /* in decimal number format - example 1.5, 0.001 etc */ + min: string; + max: string; +} + +export interface FetchEncodedParamRequest { + /* chain:token - example `bnb:usdc` */ + sourceAssetString: string; + targetAssetString: string; + /* in decimal number format - example 1.5, 0.001 etc */ + amount: string; + /* WalletAddress or rubic-multi-proxy-contract address */ + fromAddress: string; + receiverAddress: string; + useProxy: boolean; +} + +export interface EncodeSwapSchema { + encoded: string; + fromAddress: string; + fromContract: string; + recipient: string; + fee: { + serviceFee: string; + lpFee: string; + totalFee: string; + signingRequest: object; + }; + converted?: { amount: string; token: string }; + initiator: string; +} + +export interface TxFeeSchema { + serviceFee: string; + lpFee: string; + /* in decimal number format - example 1.5, 0.001 etc */ + totalFee: string; + converted?: { amount: string; token: string }; +} + +export interface TxStatusSchema { + expired?: boolean; + LOCKED: string; + BONDED: string; + EXECUTED: string; + RELEASED: string; + swap: { + id: string; + encoded: string; + from: { + amount: string; + network: string; + token: string; + }; + to: { + amount: string; + network: string; + token: string; + }; + }; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/models/meson-provider-types.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/models/meson-provider-types.ts new file mode 100644 index 0000000..5d2259e --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/models/meson-provider-types.ts @@ -0,0 +1,9 @@ +import BigNumber from 'bignumber.js'; + +export interface FetchedMesonTradeInfo { + mesonFee: string; + sourceAssetString: string; + targetAssetString: string; + min: BigNumber; + max: BigNumber; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/models/meson-trade-types.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/models/meson-trade-types.ts new file mode 100644 index 0000000..ee8e49c --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/models/meson-trade-types.ts @@ -0,0 +1,29 @@ +import { PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +import { GasData } from '../../common/emv-cross-chain-trade/models/gas-data'; +import { FeeInfo } from '../../common/models/fee-info'; +import { RubicStep } from '../../common/models/rubicStep'; + +export interface MesonCrossChainTradeConstructorParams { + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + gasData: GasData | null; + feeInfo: FeeInfo; + priceImpact: number | null; + sourceAssetString: string; + targetAssetString: string; + }; + providerAddress: string; + routePath: RubicStep[]; +} + +export interface MesonGetGasDataParams { + from: PriceTokenAmount; + toToken: PriceTokenAmount; + feeInfo: FeeInfo; + providerAddress: string; + sourceAssetString: string; + targetAssetString: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/services/meson-cross-chain-api-service.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/services/meson-cross-chain-api-service.ts new file mode 100644 index 0000000..683810d --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/meson-provider/services/meson-cross-chain-api-service.ts @@ -0,0 +1,142 @@ +import { NotSupportedTokensError, RubicSdkError } from 'src/common/errors'; +import { TX_STATUS } from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; +import { Injector } from 'src/core/injector/injector'; +import { TxStatusData } from 'src/features/common/status-manager/models/tx-status-data'; + +import { + EncodeSwapSchema, + FetchEncodedParamRequest, + MesonErrorRes, + MesonLimitsChain, + MesonLimitsResponse, + MesonSuccessRes, + TxFeeSchema, + TxStatusSchema +} from '../models/meson-api-types'; + +export class MesonCcrApiService { + private static apiUrl = 'https://relayer.meson.fi/api/v1'; + + public static async fetchMesonFee( + sourceAssetString: string, + targetAssetString: string, + amount: string + ): Promise { + try { + const res = await Injector.httpClient.post>( + `${this.apiUrl}/price`, + { + from: sourceAssetString, + to: targetAssetString, + amount + } + ); + + if ('converted' in res.result) { + throw new RubicSdkError('converted'); + } + + return res.result.totalFee; + } catch (e: unknown) { + const res = this.parseMesonError(e); + + return res.totalFee; + } + } + + public static async fetchChainsLimits(): Promise { + const { result: chains } = await Injector.httpClient.get( + `${this.apiUrl}/limits` + ); + + return chains; + } + + public static async fetchInfoForTx( + params: FetchEncodedParamRequest + ): Promise { + try { + const res = await Injector.httpClient.post>( + `${this.apiUrl}/swap`, + { + from: params.sourceAssetString, + to: params.targetAssetString, + amount: params.amount, + fromAddress: params.fromAddress, + fromContract: params.useProxy, + recipient: params.receiverAddress + } + ); + + if ('converted' in res.result) { + throw new RubicSdkError('converted'); + } + + return res.result; + } catch (e: unknown) { + const res = this.parseMesonError(e); + + return res; + } + } + + public static async fetchTxStatus(srcTxHash: string): Promise { + try { + const res = await Injector.httpClient.get>( + `${this.apiUrl}/swap`, + { + params: { + hash: srcTxHash + } + } + ); + + if (res.result.expired) { + return { + hash: null, + status: TX_STATUS.FAIL + }; + } + + if (res.result.RELEASED) { + return { + hash: res.result.RELEASED, + status: TX_STATUS.SUCCESS, + extraInfo: { + mesonSwapId: res.result.swap.id + } + }; + } + + return { + hash: null, + status: TX_STATUS.PENDING + }; + } catch { + return { + hash: null, + status: TX_STATUS.PENDING + }; + } + } + + private static parseMesonError(err: unknown): T { + if ((err as RubicSdkError).message?.includes('converted')) { + throw new NotSupportedTokensError(); + } + + const { + error: { + error: { + data: { swapData } + } + } + } = err as MesonErrorRes; + + if ('converted' in swapData) { + throw new NotSupportedTokensError(); + } + + return swapData; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/constants/orbiter-api.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/constants/orbiter-api.ts new file mode 100644 index 0000000..6421b00 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/constants/orbiter-api.ts @@ -0,0 +1,2 @@ +export const ORBITER_API_ENDPOINT = 'https://api.orbiter.finance/sdk'; +export const ORBITER_FEE_DIVIDER = 1_000_000; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/constants/orbiter-router-v3-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/constants/orbiter-router-v3-abi.ts new file mode 100644 index 0000000..962379a --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/constants/orbiter-router-v3-abi.ts @@ -0,0 +1,50 @@ +import { AbiItem } from 'web3-utils'; + +export const ORBITER_ROUTER_V3_ABI: AbiItem[] = [ + { + inputs: [ + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'bytes', + name: 'data', + type: 'bytes' + } + ], + name: 'transfer', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'value', + type: 'uint256' + }, + { + internalType: 'bytes', + name: 'data', + type: 'bytes' + } + ], + name: 'transferToken', + outputs: [], + stateMutability: 'payable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-api-common-types.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-api-common-types.ts new file mode 100644 index 0000000..dd4f679 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-api-common-types.ts @@ -0,0 +1,32 @@ +export interface OrbiterResponse { + message: string; + params: { + method: string; + routes: object; + url: string; + }; + status: string; + result: T; +} + +export const ORBITER_STATUS = { + ERROR: 3, + SUCCESS: 2 +} as const; + +export type OrbiterTxStatus = (typeof ORBITER_STATUS)[keyof typeof ORBITER_STATUS]; + +export const ORBITER_OP_STATUS = { + SOURCE_CHAIN_OR_TOKEN_NOT_FOUND: 2, + TARGET_CHAIN_OR_TOKEN_NOT_FOUND: 3, + RULE_NOT_FOUND: 4, + NONCE_EXCEED_MAXIMUM: 5, + AMOUNT_TOO_SMALL: 6, + REFUND: 80, + MONEY_WILL_REFUND: 96, + ABNORMAL_PAYMENT: 97, + MONEY_REFUNDED: 98, + SUCCESS_PAYMENT: 99 +}; + +export type OrbiterOpTxStatus = (typeof ORBITER_OP_STATUS)[keyof typeof ORBITER_OP_STATUS]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-api-quote-types.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-api-quote-types.ts new file mode 100644 index 0000000..c2a6768 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-api-quote-types.ts @@ -0,0 +1,26 @@ +import { OrbiterResponse } from './orbiter-api-common-types'; + +export type OrbiterQuoteResponse = OrbiterResponse<{ + receiveAmount: string; + router: OrbiterQuoteConfig; +}>; + +export type OrbiterQuoteConfigsResponse = OrbiterResponse; + +export interface OrbiterQuoteConfig { + line: string; + endpoint: string; + endpointContract: string | null; + srcChain: string; + tgtChain: string; + srcToken: string; + tgtToken: string; + maxAmt: string; + minAmt: string; + tradeFee: string; + withholdingFee: string; + vc: string; + state: string; + compRatio: number; + spentTime: number; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-api-status-types.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-api-status-types.ts new file mode 100644 index 0000000..bba0d36 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-api-status-types.ts @@ -0,0 +1,18 @@ +import { OrbiterOpTxStatus, OrbiterResponse, OrbiterTxStatus } from './orbiter-api-common-types'; + +export type OrbiterStatusResponse = OrbiterResponse<{ + chainId: string; + hash: string; + sender: string; + receiver: string; + amount: string; + symbol: string; + timestamp: string; + status: OrbiterTxStatus; + opStatus: OrbiterOpTxStatus; + /* destination status tx-hash */ + targetId: string; + targetAmount: string; + targetSymbol: string; + targetChain: string; +}>; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-bridge-trade-types.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-bridge-trade-types.ts new file mode 100644 index 0000000..aeb6d35 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-bridge-trade-types.ts @@ -0,0 +1,29 @@ +import { PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +import { GasData } from '../../common/emv-cross-chain-trade/models/gas-data'; +import { FeeInfo } from '../../common/models/fee-info'; +import { RubicStep } from '../../common/models/rubicStep'; +import { OrbiterQuoteConfig } from './orbiter-api-quote-types'; + +export interface OrbiterGetGasDataParams { + fromToken: PriceTokenAmount; + toToken: PriceTokenAmount; + feeInfo: FeeInfo; + providerAddress: string; + quoteConfig: OrbiterQuoteConfig; + receiverAddress?: string; +} + +export interface OrbiterTradeParams { + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + gasData: GasData | null; + feeInfo: FeeInfo; + priceImpact: number | null; + quoteConfig: OrbiterQuoteConfig; + }; + providerAddress: string; + routePath: RubicStep[]; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-contract-addresses.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-contract-addresses.ts new file mode 100644 index 0000000..625d91f --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-contract-addresses.ts @@ -0,0 +1,29 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { OrbiterSupportedBlockchain } from './orbiter-supported-blockchains'; + +export const orbiterContractAddresses: Record = { + [BLOCKCHAIN_NAME.ETHEREUM]: '0xc741900276cd598060b0fe6594fbe977392928f4', + [BLOCKCHAIN_NAME.ARBITRUM]: '0x6a065083886ec63d274b8e1fe19ae2ddf498bfdd', + [BLOCKCHAIN_NAME.ZK_SYNC]: '0xb4ab2ff34fadc774aff45f1c4566cb5e16bd4867', + [BLOCKCHAIN_NAME.POLYGON]: '0x653f25dc641544675338cb47057f8ea530c69b78', + [BLOCKCHAIN_NAME.OPTIMISM]: '0x3191f40de6991b1bb1f61b7cec43d62bb337786b', + [BLOCKCHAIN_NAME.ZETACHAIN]: '0x13E46b2a3f8512eD4682a8Fb8B560589fE3C2172', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: '0x13e46b2a3f8512ed4682a8fb8b560589fe3c2172', + [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: '0x13e46b2a3f8512ed4682a8fb8b560589fe3c2172', + [BLOCKCHAIN_NAME.LINEA]: '0x13e46b2a3f8512ed4682a8fb8b560589fe3c2172', + [BLOCKCHAIN_NAME.MANTLE]: '0x13e46b2a3f8512ed4682a8fb8b560589fe3c2172', + [BLOCKCHAIN_NAME.BASE]: '0x13e46b2a3f8512ed4682a8fb8b560589fe3c2172', + [BLOCKCHAIN_NAME.MANTA_PACIFIC]: '0x13e46b2a3f8512ed4682a8fb8b560589fe3c2172', + [BLOCKCHAIN_NAME.SCROLL]: '0x13e46b2a3f8512ed4682a8fb8b560589fe3c2172', + [BLOCKCHAIN_NAME.BLAST]: '0x13E46b2a3f8512eD4682a8Fb8B560589fE3C2172', + [BLOCKCHAIN_NAME.KROMA]: '0x13E46b2a3f8512eD4682a8Fb8B560589fE3C2172', + [BLOCKCHAIN_NAME.STARKNET]: + '0x0173f81c529191726c6e7287e24626fe24760ac44dae2a1f7e02080230f8458b', + [BLOCKCHAIN_NAME.MODE]: '0x13E46b2a3f8512eD4682a8Fb8B560589fE3C2172', + [BLOCKCHAIN_NAME.ZK_FAIR]: '0x13e46b2a3f8512ed4682a8fb8b560589fe3c2172', + [BLOCKCHAIN_NAME.XLAYER]: '0x13e46b2a3f8512ed4682a8fb8b560589fe3c2172', + [BLOCKCHAIN_NAME.TAIKO]: '0x2598d7bc9d3b4b6124f3282e49eee68db270f516' + // [BLOCKCHAIN_NAME.ZK_LINK]: '0xb4ab2ff34fadc774aff45f1c4566cb5e16bd4867' + // [BLOCKCHAIN_NAME.MERLIN]: '0x4b8a4641c140b3aa6be8d99786fafe47a65869db' +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-supported-blockchains.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-supported-blockchains.ts new file mode 100644 index 0000000..b5c82a1 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-supported-blockchains.ts @@ -0,0 +1,28 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const orbiterSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.ZK_SYNC, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON_ZKEVM, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.MANTLE, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.MANTA_PACIFIC, + BLOCKCHAIN_NAME.SCROLL, + BLOCKCHAIN_NAME.ZETACHAIN, + BLOCKCHAIN_NAME.BLAST, + BLOCKCHAIN_NAME.KROMA, + BLOCKCHAIN_NAME.STARKNET, + BLOCKCHAIN_NAME.MODE, + BLOCKCHAIN_NAME.ZK_FAIR, + BLOCKCHAIN_NAME.XLAYER, + BLOCKCHAIN_NAME.TAIKO + // BLOCKCHAIN_NAME.ZK_LINK + // BLOCKCHAIN_NAME.MERLIN +] as const; + +export type OrbiterSupportedBlockchain = (typeof orbiterSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-utils-types.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-utils-types.ts new file mode 100644 index 0000000..75ccaa7 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/models/orbiter-utils-types.ts @@ -0,0 +1,9 @@ +import { PriceToken } from 'src/common/tokens'; + +import { OrbiterQuoteConfig } from './orbiter-api-quote-types'; + +export interface OrbiterGetQuoteConfigParams { + from: PriceToken; + to: PriceToken; + configs: OrbiterQuoteConfig[]; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/orbiter-bridge-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/orbiter-bridge-provider.ts new file mode 100644 index 0000000..7990694 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/orbiter-bridge-provider.ts @@ -0,0 +1,161 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; + +import { RequiredCrossChainOptions } from '../../models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from '../../models/cross-chain-trade-type'; +import { CrossChainProvider } from '../common/cross-chain-provider'; +import { CalculationResult } from '../common/models/calculation-result'; +import { FeeInfo } from '../common/models/fee-info'; +import { RubicStep } from '../common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from '../common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { OrbiterQuoteConfig } from './models/orbiter-api-quote-types'; +import { + OrbiterSupportedBlockchain, + orbiterSupportedBlockchains +} from './models/orbiter-supported-blockchains'; +import { OrbiterBridgeTrade } from './orbiter-bridge-trade'; +import { OrbiterApiService } from './services/orbiter-api-service'; +import { OrbiterUtils } from './services/orbiter-utils'; + +export class OrbiterBridgeProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.ORBITER_BRIDGE; + + private orbiterQuoteConfigs: OrbiterQuoteConfig[] = []; + + public isSupportedBlockchain(blockchain: EvmBlockchainName): boolean { + return orbiterSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + const fromBlockchain = from.blockchain as OrbiterSupportedBlockchain; + const useProxy = options?.useProxy?.[this.type] ?? true; + + try { + this.orbiterQuoteConfigs = await OrbiterApiService.getQuoteConfigs(); + + const feeInfo = await this.getFeeInfo( + fromBlockchain, + options.providerAddress, + from, + useProxy + ); + + const fromWithoutFee = getFromWithoutFee( + from, + feeInfo.rubicProxy?.platformFee?.percent + ); + + const quoteConfig = OrbiterUtils.getQuoteConfig({ + from, + to: toToken, + configs: this.orbiterQuoteConfigs + }); + + if (!OrbiterUtils.isAmountCorrect(from.tokenAmount, quoteConfig)) { + throw new RubicSdkError(` + [ORBITER] Amount is out of range. + Min amount - ${quoteConfig.minAmt} ${from.symbol}. + Max amount - ${quoteConfig.maxAmt} ${from.symbol}. + `); + } + + const toAmount = await OrbiterApiService.calculateAmount(fromWithoutFee, quoteConfig); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: toAmount + }); + + const fromWithoutFeeWithCode = new PriceTokenAmount({ + ...fromWithoutFee.asStruct, + weiAmount: this.getSendingAmount( + fromWithoutFee.stringWeiAmount, + quoteConfig, + feeInfo + ) + }); + + const gasData = + options.gasCalculation === 'enabled' + ? await OrbiterBridgeTrade.getGasData({ + feeInfo, + fromToken: fromWithoutFeeWithCode, + toToken: to, + receiverAddress: options.receiverAddress, + providerAddress: options.providerAddress, + quoteConfig + }) + : null; + + const trade = new OrbiterBridgeTrade({ + crossChainTrade: { + feeInfo, + from: fromWithoutFeeWithCode, + gasData, + to, + priceImpact: from.calculatePriceImpactPercent(to), + quoteConfig + }, + providerAddress: options.providerAddress, + routePath: await this.getRoutePath(from, to) + }); + + return { trade, tradeType: this.type }; + } catch (err) { + const rubicSdkError = CrossChainProvider.parseError(err); + + return { + trade: null, + error: rubicSdkError, + tradeType: this.type + }; + } + } + + private getSendingAmount( + fromStringWeiAmount: string, + quoteConfig: OrbiterQuoteConfig, + feeInfo: FeeInfo + ): BigNumber { + const desiredAmount = OrbiterUtils.getAmountWithVcCode(fromStringWeiAmount, quoteConfig); + const fee = (feeInfo.rubicProxy?.platformFee?.percent || 0) * 1_000; + const denominator = new BigNumber(1_000_000); + const sendingAmount = new BigNumber(desiredAmount) + .multipliedBy(denominator) + .dividedBy(denominator.minus(fee)) + .decimalPlaces(0, 1) + .toFixed(); + + return new BigNumber(sendingAmount); + } + + protected async getRoutePath( + fromToken: PriceTokenAmount, + toToken: PriceTokenAmount + ): Promise { + return [{ type: 'cross-chain', provider: this.type, path: [fromToken, toToken] }]; + } + + protected async getFeeInfo( + fromBlockchain: OrbiterSupportedBlockchain, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/orbiter-bridge-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/orbiter-bridge-trade.ts new file mode 100644 index 0000000..9c37d74 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/orbiter-bridge-trade.ts @@ -0,0 +1,238 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; + +import { CROSS_CHAIN_TRADE_TYPE, CrossChainTradeType } from '../../models/cross-chain-trade-type'; +import { rubicProxyContractAddress } from '../common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from '../common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from '../common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from '../common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from '../common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE, BridgeType } from '../common/models/bridge-type'; +import { FeeInfo } from '../common/models/fee-info'; +import { GetContractParamsOptions } from '../common/models/get-contract-params-options'; +import { OnChainSubtype } from '../common/models/on-chain-subtype'; +import { TradeInfo } from '../common/models/trade-info'; +import { ProxyCrossChainEvmTrade } from '../common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { ORBITER_ROUTER_V3_ABI } from './constants/orbiter-router-v3-abi'; +import { OrbiterQuoteConfig } from './models/orbiter-api-quote-types'; +import { OrbiterGetGasDataParams, OrbiterTradeParams } from './models/orbiter-bridge-trade-types'; +import { orbiterContractAddresses } from './models/orbiter-contract-addresses'; +import { OrbiterSupportedBlockchain } from './models/orbiter-supported-blockchains'; +import { OrbiterUtils } from './services/orbiter-utils'; + +export class OrbiterBridgeTrade extends EvmCrossChainTrade { + /** @internal */ + public static async getGasData({ + fromToken, + toToken, + feeInfo, + providerAddress, + quoteConfig + }: OrbiterGetGasDataParams): Promise { + try { + const trade = new OrbiterBridgeTrade({ + crossChainTrade: { + from: fromToken, + to: toToken, + feeInfo, + gasData: null, + priceImpact: fromToken.calculatePriceImpactPercent(toToken) || 0, + quoteConfig + }, + routePath: [], + providerAddress + }); + + return getCrossChainGasData(trade); + } catch (err) { + return null; + } + } + + /**ABSTRACT PROPS */ + public readonly type: CrossChainTradeType = CROSS_CHAIN_TRADE_TYPE.ORBITER_BRIDGE; + + public readonly isAggregator: boolean = false; + + public readonly to: PriceTokenAmount; + + public readonly from: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly feeInfo: FeeInfo; + + public readonly onChainSubtype: OnChainSubtype = { from: undefined, to: undefined }; + + public readonly bridgeType: BridgeType = BRIDGE_TYPE.ORBITER_BRIDGE; + + public readonly gasData: GasData; + + public readonly priceImpact: number | null; + /** */ + + /* used to get extraNativeFee(tradeFee) and orbiter contract params */ + private quoteConfig: OrbiterQuoteConfig; + + private get fromBlockchain(): OrbiterSupportedBlockchain { + return this.from.blockchain as OrbiterSupportedBlockchain; + } + + protected get fromContractAddress(): string { + return this.isProxyTrade + ? rubicProxyContractAddress[this.fromBlockchain].gateway + : orbiterContractAddresses[this.fromBlockchain]; + } + + protected get methodName(): string { + return 'startBridgeTokensViaGenericCrossChain'; + } + + constructor(params: OrbiterTradeParams) { + super(params.providerAddress, params.routePath); + this.to = params.crossChainTrade.to; + this.from = params.crossChainTrade.from; + this.toTokenAmountMin = params.crossChainTrade.to.tokenAmount; + this.feeInfo = params.crossChainTrade.feeInfo; + this.gasData = params.crossChainTrade.gasData; + this.priceImpact = params.crossChainTrade.priceImpact; + this.quoteConfig = params.crossChainTrade.quoteConfig; + } + + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + await this.checkTradeErrors(); + await this.checkAllowanceAndApprove(options); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + // eslint-disable-next-line no-useless-catch + try { + const { data, to, value } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress || this.walletAddress + ); + + await this.web3Private.trySendTransaction(to, { + data, + value, + onTransactionHash, + gas: gasLimit, + gasPriceOptions + }); + + return transactionHash!; + } catch (err) { + throw err; + } + } + + public async getContractParams(options: GetContractParamsOptions): Promise { + const receiverAddress = options?.receiverAddress || this.walletAddress; + const { + data, + value: providerValue, + to: providerRouter + } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress || this.walletAddress + ); + + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData(options, { + walletAddress: receiverAddress, + fromTokenAmount: this.from, + toTokenAmount: this.to, + srcChainTrade: null, + providerAddress: this.providerAddress, + type: `native:${this.bridgeType}`, + fromAddress: this.walletAddress + }); + + const extraNativeFee = '0'; + const providerData = await ProxyCrossChainEvmTrade.getGenericProviderData( + providerRouter, + data, + this.from.blockchain, + providerRouter, + extraNativeFee + ); + + const methodArguments = [bridgeData, providerData]; + const value = this.getSwapValue(providerValue); + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } + + protected async getTransactionConfigAndAmount(receiverAddress?: string): Promise<{ + config: EvmEncodeConfig; + amount: string; + }> { + // Orbiter deposit address to send funds money to receiverWalletAddress after transfer confirmation + const orbiterTokensDispenser = this.quoteConfig.endpoint; + + const transferAmount = this.from.stringWeiAmount; + const encodedReceiverAndCode = OrbiterUtils.getHexDataArg( + this.quoteConfig.vc, + receiverAddress || this.walletAddress + ); + + const methodName = this.from.isNative ? 'transfer' : 'transferToken'; + const methodArgs = this.from.isNative + ? [orbiterTokensDispenser, encodedReceiverAndCode] + : [this.from.address, orbiterTokensDispenser, transferAmount, encodedReceiverAndCode]; + const value = this.from.isNative ? transferAmount : '0'; + + const config = EvmWeb3Pure.encodeMethodCall( + orbiterContractAddresses[this.fromBlockchain], + ORBITER_ROUTER_V3_ABI, + methodName, + methodArgs, + value + ); + + return { + config, + amount: this.to.stringWeiAmount + }; + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact, + slippage: 0, + routePath: this.routePath + }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/services/orbiter-api-service.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/services/orbiter-api-service.ts new file mode 100644 index 0000000..3d35bc9 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/services/orbiter-api-service.ts @@ -0,0 +1,62 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { TX_STATUS } from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; +import { Injector } from 'src/core/injector/injector'; +import { TxStatusData } from 'src/features/common/status-manager/models/tx-status-data'; + +import { ORBITER_API_ENDPOINT } from '../constants/orbiter-api'; +import { ORBITER_OP_STATUS, ORBITER_STATUS } from '../models/orbiter-api-common-types'; +import { OrbiterQuoteConfig, OrbiterQuoteConfigsResponse } from '../models/orbiter-api-quote-types'; +import { OrbiterStatusResponse } from '../models/orbiter-api-status-types'; +import { OrbiterUtils } from './orbiter-utils'; + +export class OrbiterApiService { + private static dealerId: string | null = null; + + public static async getQuoteConfigs(): Promise { + const { result } = await Injector.httpClient.get( + `${ORBITER_API_ENDPOINT}/routers`, + { params: { ...(this.dealerId && { dealerId: this.dealerId }) } } + ); + + return result; + } + + public static calculateAmount( + from: PriceTokenAmount, + config: OrbiterQuoteConfig + ): BigNumber { + const tradingFee = OrbiterUtils.getTradingFee(from, config); + + return from.tokenAmount.minus(tradingFee).minus(config.withholdingFee); + } + + public static async getTxStatus(txHash: string): Promise { + const response = await Injector.httpClient.get( + `${ORBITER_API_ENDPOINT}/transaction/status/${txHash}` + ); + + if (!response?.result) { + return { hash: null, status: TX_STATUS.PENDING }; + } + + const { targetId: hash, status: txStatus, opStatus } = response.result; + + if (txStatus === ORBITER_STATUS.ERROR) { + return { + hash, + status: TX_STATUS.FAIL + }; + } + + if (txStatus === ORBITER_STATUS.SUCCESS && opStatus === ORBITER_OP_STATUS.SUCCESS_PAYMENT) { + return { + hash, + status: TX_STATUS.SUCCESS + }; + } + + return { hash, status: TX_STATUS.PENDING }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/services/orbiter-utils.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/services/orbiter-utils.ts new file mode 100644 index 0000000..6f56263 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/orbiter-bridge/services/orbiter-utils.ts @@ -0,0 +1,83 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import Web3 from 'web3'; + +import { ORBITER_FEE_DIVIDER } from '../constants/orbiter-api'; +import { OrbiterQuoteConfig } from '../models/orbiter-api-quote-types'; +import { OrbiterGetQuoteConfigParams } from '../models/orbiter-utils-types'; + +export class OrbiterUtils { + public static compareChainId(orbiterChainId: string, blockchainName: BlockchainName): boolean { + if (blockchainName === BLOCKCHAIN_NAME.STARKNET) { + return orbiterChainId === 'SN_MAIN'; + } + + return orbiterChainId === blockchainId[blockchainName].toString(); + } + + public static getQuoteConfig({ + configs, + from, + to + }: OrbiterGetQuoteConfigParams): OrbiterQuoteConfig { + const config = configs.find(conf => { + return ( + this.compareChainId(conf.srcChain, from.blockchain) && + this.compareChainId(conf.tgtChain, to.blockchain) && + compareAddresses(conf.srcToken, from.address) && + compareAddresses(conf.tgtToken, to.address) + ); + }); + + if (!config) { + throw new RubicSdkError('[ORBITER] Unsupported pair of tokens!'); + } + + return config; + } + + public static isAmountCorrect(fromAmount: BigNumber, config: OrbiterQuoteConfig): boolean { + return fromAmount.lt(config.maxAmt) && fromAmount.gt(config.minAmt); + } + + /** + * + * @param code Orbiter identification code of chain(9001, 9002 etc), equals quoteConfig.vc + * @param receiverAddress + * @returns data argument for orbiter-abi methods as hex string + */ + public static getHexDataArg(code: string, receiverAddress: string): string { + const value = `c=${code}&t=${receiverAddress}`; + const hexString = Web3.utils.toHex(value); + + return hexString; + } + + public static getTradingFee( + from: PriceTokenAmount, + config: OrbiterQuoteConfig + ): BigNumber { + const digit = from.decimals === 18 ? 8 : 5; + const tradingFee = from.tokenAmount + .multipliedBy(config.tradeFee) + .dividedBy(ORBITER_FEE_DIVIDER) + .decimalPlaces(digit, BigNumber.ROUND_UP); + + return tradingFee; + } + + public static getAmountWithVcCode(fromWeiAmount: string, config: OrbiterQuoteConfig): string { + const regex = new RegExp(`\\d{${config.vc.length}}$`, 'g'); + const total = fromWeiAmount.replace(regex, config.vc); + + return total; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/erc-677-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/erc-677-abi.ts new file mode 100644 index 0000000..194a414 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/erc-677-abi.ts @@ -0,0 +1,15 @@ +import { AbiItem } from 'web3-utils'; + +export const erc677Abi: AbiItem[] = [ + { + inputs: [ + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'bytes', name: '', type: 'bytes' } + ], + name: 'transferAndCall', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/fee-manager-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/fee-manager-abi.ts new file mode 100644 index 0000000..02e3c3e --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/fee-manager-abi.ts @@ -0,0 +1,59 @@ +import { AbiItem } from 'web3-utils'; + +export const feeManagerAbi: AbiItem[] = [ + { + inputs: [], + name: 'FOREIGN_TO_HOME_FEE', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'HOME_TO_FOREIGN_FEE', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_feeType', + type: 'bytes32' + }, + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256' + } + ], + name: 'calculateFee', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/foreign-bridge-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/foreign-bridge-abi.ts new file mode 100644 index 0000000..35bdf5f --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/foreign-bridge-abi.ts @@ -0,0 +1,70 @@ +import { AbiItem } from 'web3-utils'; + +export const foreignBridgeAbi: AbiItem[] = [ + { + inputs: [{ internalType: 'address', name: '_nativeToken', type: 'address' }], + name: 'bridgedTokenAddress', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'address', name: '_token', type: 'address' }], + name: 'isRegisteredAsNativeToken', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'address', name: '_token', type: 'address' }], + name: 'isTokenRegistered', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'address', name: '_token', type: 'address' }], + name: 'minPerTx', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'address', name: '_bridgedToken', type: 'address' }], + name: 'nativeTokenAddress', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'contract IERC677', name: 'token', type: 'address' }, + { internalType: 'address', name: '_receiver', type: 'address' }, + { internalType: 'uint256', name: '_value', type: 'uint256' } + ], + name: 'relayTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: '_token', type: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' } + ], + name: 'withinExecutionLimit', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: '_token', type: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' } + ], + name: 'withinLimit', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/home-bridge-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/home-bridge-abi.ts new file mode 100644 index 0000000..fa9ead8 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/home-bridge-abi.ts @@ -0,0 +1,1388 @@ +import { AbiItem } from 'web3-utils'; + +export const homeBridgeAbi: AbiItem[] = [ + { + inputs: [ + { + internalType: 'string', + name: '_suffix', + type: 'string' + } + ], + type: 'constructor' + }, + { + inputs: [], + name: 'bridgeContract', + outputs: [ + { + internalType: 'contract IAMB', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_nativeToken', + type: 'address' + } + ], + name: 'bridgedTokenAddress', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'address', + name: '_to', + type: 'address' + } + ], + name: 'claimTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_bridgedToken', + type: 'address' + }, + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'address', + name: '_to', + type: 'address' + } + ], + name: 'claimTokensFromTokenContract', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + } + ], + name: 'dailyLimit', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'string', + name: '_name', + type: 'string' + }, + { + internalType: 'string', + name: '_symbol', + type: 'string' + }, + { + internalType: 'uint8', + name: '_decimals', + type: 'uint8' + }, + { + internalType: 'address', + name: '_recipient', + type: 'address' + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256' + } + ], + name: 'deployAndHandleBridgedTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'string', + name: '_name', + type: 'string' + }, + { + internalType: 'string', + name: '_symbol', + type: 'string' + }, + { + internalType: 'uint8', + name: '_decimals', + type: 'uint8' + }, + { + internalType: 'address', + name: '_recipient', + type: 'address' + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256' + }, + { + internalType: 'bytes', + name: '_data', + type: 'bytes' + } + ], + name: 'deployAndHandleBridgedTokensAndCall', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + } + ], + name: 'executionDailyLimit', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + } + ], + name: 'executionMaxPerTx', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'feeManager', + outputs: [ + { + internalType: 'contract OmnibridgeFeeManager', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_messageId', + type: 'bytes32' + } + ], + name: 'fixFailedMessage', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'address', + name: '_receiver', + type: 'address' + } + ], + name: 'fixMediatorBalance', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_homeToken', + type: 'address' + } + ], + name: 'foreignTokenAddress', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'forwardingRulesManager', + outputs: [ + { + internalType: 'contract MultiTokenForwardingRulesManager', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'gasLimitManager', + outputs: [ + { + internalType: 'contract SelectorTokenGasLimitManager', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'getBridgeInterfacesVersion', + outputs: [ + { + internalType: 'uint64', + name: 'major', + type: 'uint64' + }, + { + internalType: 'uint64', + name: 'minor', + type: 'uint64' + }, + { + internalType: 'uint64', + name: 'patch', + type: 'uint64' + } + ], + stateMutability: 'pure', + type: 'function' + }, + { + inputs: [], + name: 'getBridgeMode', + outputs: [ + { + internalType: 'bytes4', + name: '_data', + type: 'bytes4' + } + ], + stateMutability: 'pure', + type: 'function' + }, + { + inputs: [], + name: 'getCurrentDay', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'address', + name: '_recipient', + type: 'address' + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256' + } + ], + name: 'handleBridgedTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'address', + name: '_recipient', + type: 'address' + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256' + }, + { + internalType: 'bytes', + name: '_data', + type: 'bytes' + } + ], + name: 'handleBridgedTokensAndCall', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'address', + name: '_recipient', + type: 'address' + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256' + } + ], + name: 'handleNativeTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'address', + name: '_recipient', + type: 'address' + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256' + }, + { + internalType: 'bytes', + name: '_data', + type: 'bytes' + } + ], + name: 'handleNativeTokensAndCall', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_foreignToken', + type: 'address' + } + ], + name: 'homeTokenAddress', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_bridgeContract', + type: 'address' + }, + { + internalType: 'address', + name: '_mediatorContract', + type: 'address' + }, + { + internalType: 'uint256[3]', + name: '_dailyLimitMaxPerTxMinPerTxArray', + type: 'uint256[3]' + }, + { + internalType: 'uint256[2]', + name: '_executionDailyLimitExecutionMaxPerTxArray', + type: 'uint256[2]' + }, + { + internalType: 'address', + name: '_gasLimitManager', + type: 'address' + }, + { + internalType: 'address', + name: '_owner', + type: 'address' + }, + { + internalType: 'address', + name: '_tokenFactory', + type: 'address' + }, + { + internalType: 'address', + name: '_feeManager', + type: 'address' + }, + { + internalType: 'address', + name: '_forwardingRulesManager', + type: 'address' + } + ], + name: 'initialize', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + } + ], + name: 'isBridgedTokenDeployAcknowledged', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'isInitialized', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + } + ], + name: 'isRegisteredAsNativeToken', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + } + ], + name: 'isTokenRegistered', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + } + ], + name: 'maxAvailablePerTx', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + } + ], + name: 'maxPerTx', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + } + ], + name: 'mediatorBalance', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'mediatorContractOnOtherSide', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_messageId', + type: 'bytes32' + } + ], + name: 'messageFixed', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + } + ], + name: 'minPerTx', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_bridgedToken', + type: 'address' + } + ], + name: 'nativeTokenAddress', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_from', + type: 'address' + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256' + }, + { + internalType: 'bytes', + name: '_data', + type: 'bytes' + } + ], + name: 'onTokenTransfer', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [], + name: 'owner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'contract IERC677', + name: 'token', + type: 'address' + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256' + } + ], + name: 'relayTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'contract IERC677', + name: 'token', + type: 'address' + }, + { + internalType: 'address', + name: '_receiver', + type: 'address' + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256' + } + ], + name: 'relayTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'contract IERC677', + name: 'token', + type: 'address' + }, + { + internalType: 'address', + name: '_receiver', + type: 'address' + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256' + }, + { + internalType: 'bytes', + name: '_data', + type: 'bytes' + } + ], + name: 'relayTokensAndCall', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_messageId', + type: 'bytes32' + } + ], + name: 'requestFailedMessageFix', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_bridgeContract', + type: 'address' + } + ], + name: 'setBridgeContract', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_nativeToken', + type: 'address' + }, + { + internalType: 'address', + name: '_bridgedToken', + type: 'address' + } + ], + name: 'setCustomTokenAddressPair', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'uint256', + name: '_dailyLimit', + type: 'uint256' + } + ], + name: 'setDailyLimit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'uint256', + name: '_dailyLimit', + type: 'uint256' + } + ], + name: 'setExecutionDailyLimit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'uint256', + name: '_maxPerTx', + type: 'uint256' + } + ], + name: 'setExecutionMaxPerTx', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_feeManager', + type: 'address' + } + ], + name: 'setFeeManager', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_manager', + type: 'address' + } + ], + name: 'setForwardingRulesManager', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_manager', + type: 'address' + } + ], + name: 'setGasLimitManager', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'uint256', + name: '_maxPerTx', + type: 'uint256' + } + ], + name: 'setMaxPerTx', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_mediatorContract', + type: 'address' + } + ], + name: 'setMediatorContractOnOtherSide', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'uint256', + name: '_minPerTx', + type: 'uint256' + } + ], + name: 'setMinPerTx', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_tokenFactory', + type: 'address' + } + ], + name: 'setTokenFactory', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [], + name: 'tokenFactory', + outputs: [ + { + internalType: 'contract TokenFactory', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'uint256', + name: '_day', + type: 'uint256' + } + ], + name: 'totalExecutedPerDay', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'uint256', + name: '_day', + type: 'uint256' + } + ], + name: 'totalSpentPerDay', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'newOwner', + type: 'address' + } + ], + name: 'transferOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_tokenFactory', + type: 'address' + }, + { + internalType: 'address', + name: '_forwardingRulesManager', + type: 'address' + }, + { + internalType: 'address', + name: '_gasLimitManager', + type: 'address' + }, + { + internalType: 'uint256', + name: '_dailyLimit', + type: 'uint256' + } + ], + name: 'upgradeToReverseMode', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'uint256', + name: '_amount', + type: 'uint256' + } + ], + name: 'withinExecutionLimit', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address' + }, + { + internalType: 'uint256', + name: '_amount', + type: 'uint256' + } + ], + name: 'withinLimit', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: 'token', + type: 'address' + }, + { + indexed: false, + name: 'newLimit', + type: 'uint256' + } + ], + name: 'DailyLimitChanged', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: 'token', + type: 'address' + }, + { + indexed: false, + name: 'newLimit', + type: 'uint256' + } + ], + name: 'ExecutionDailyLimitChanged', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: 'messageId', + type: 'bytes32' + }, + { + indexed: false, + name: 'token', + type: 'address' + }, + { + indexed: false, + name: 'recipient', + type: 'address' + }, + { + indexed: false, + name: 'value', + type: 'uint256' + } + ], + name: 'FailedMessageFixed', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + name: 'fee', + type: 'uint256' + }, + { + indexed: true, + name: 'token', + type: 'address' + }, + { + indexed: true, + name: 'messageId', + type: 'bytes32' + } + ], + name: 'FeeDistributed', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: 'token', + type: 'address' + }, + { + indexed: false, + name: 'fee', + type: 'uint256' + } + ], + name: 'FeeDistributionFailed', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: 'nativeToken', + type: 'address' + }, + { + indexed: true, + name: 'bridgedToken', + type: 'address' + } + ], + name: 'NewTokenRegistered', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + name: 'previousOwner', + type: 'address' + }, + { + indexed: false, + name: 'newOwner', + type: 'address' + } + ], + name: 'OwnershipTransferred', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: 'token', + type: 'address' + }, + { + indexed: true, + name: 'recipient', + type: 'address' + }, + { + indexed: false, + name: 'value', + type: 'uint256' + }, + { + indexed: true, + name: 'messageId', + type: 'bytes32' + } + ], + name: 'TokensBridged', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: 'token', + type: 'address' + }, + { + indexed: true, + name: 'sender', + type: 'address' + }, + { + indexed: false, + name: 'value', + type: 'uint256' + }, + { + indexed: true, + name: 'messageId', + type: 'bytes32' + } + ], + name: 'TokensBridgingInitiated', + type: 'event' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/native-bridge-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/native-bridge-abi.ts new file mode 100644 index 0000000..a8fd144 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/native-bridge-abi.ts @@ -0,0 +1,12 @@ +import { AbiItem } from 'web3-utils'; + +export const nativeBridgeAbi: AbiItem[] = [ + { + inputs: [{ internalType: 'address', name: '_receiver', type: 'address' }], + name: 'wrapAndRelayTokens', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { stateMutability: 'payable', type: 'receive' } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-contract-address.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-contract-address.ts new file mode 100644 index 0000000..0faf955 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-contract-address.ts @@ -0,0 +1,21 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { PulseChainCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-supported-blockchains'; + +export const pulseChainContractAddress: Record< + PulseChainCrossChainSupportedBlockchain, + { + omniBridge: string; + omniBridgeWrapped: string; + } +> = { + [BLOCKCHAIN_NAME.ETHEREUM]: { + omniBridge: '0x1715a3E4A142d8b698131108995174F37aEBA10D', + omniBridgeWrapped: '0xe20E337DB2a00b1C37139c873B92a0AAd3F468bF' + }, + [BLOCKCHAIN_NAME.PULSECHAIN]: { + omniBridge: '0x4fD0aaa7506f3d9cB8274bdB946Ec42A1b8751Ef', + omniBridgeWrapped: '0x0e18d0d556b652794EF12Bf68B2dC857EF5f3996' + } +}; + +export const omniBridgeNativeRouter = '0x8AC4ae65b3656e26dC4e0e69108B392283350f55'; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-supported-blockchains.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-supported-blockchains.ts new file mode 100644 index 0000000..14105f0 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-supported-blockchains.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const pulseChainSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.PULSECHAIN +] as const; + +export type PulseChainCrossChainSupportedBlockchain = + (typeof pulseChainSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/bridge-manager.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/bridge-manager.ts new file mode 100644 index 0000000..ebd5420 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/bridge-manager.ts @@ -0,0 +1,16 @@ +import { Token } from 'src/common/tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { PulseChainCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-supported-blockchains'; +import { ForeignBridge } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/foreign-bridge'; +import { HomeBridge } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/home-bridge'; + +export class BridgeManager { + public static createBridge( + fromToken: Token, + toToken: Token + ) { + return fromToken.blockchain === BLOCKCHAIN_NAME.ETHEREUM + ? new ForeignBridge(fromToken, toToken) + : new HomeBridge(fromToken, toToken); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/foreign-bridge.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/foreign-bridge.ts new file mode 100644 index 0000000..2875855 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/foreign-bridge.ts @@ -0,0 +1,115 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { erc677Abi } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/erc-677-abi'; +import { nativeBridgeAbi } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/native-bridge-abi'; +import { omniBridgeNativeRouter } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-contract-address'; +import { OmniBridge } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/omni-bridge'; + +export class ForeignBridge extends OmniBridge { + public isTokenRegistered(address: string): Promise { + return this.sourceWeb3Public.callContractMethod( + this.sourceBridgeAddress, + this.sourceBridgeAbi, + 'isTokenRegistered', + [address] + ); + } + + protected isRegisteredAsNative(address: string): Promise { + return this.sourceWeb3Public.callContractMethod( + this.sourceBridgeAddress, + this.sourceBridgeAbi, + 'isRegisteredAsNativeToken', + [address] + ); + } + + protected getNonNativeToken(address: string): Promise { + return this.sourceWeb3Public.callContractMethod( + this.sourceBridgeAddress, + this.sourceBridgeAbi, + 'nativeTokenAddress', + [address] + ); + } + + protected getNativeToken(address: string): Promise { + return this.targetWeb3Public.callContractMethod( + this.targetBridgeAddress, + this.targetBridgeAbi, + 'bridgedTokenAddress', + [address] + ); + } + + public async getMinAmountToken(address: string): Promise { + const amount = await this.sourceWeb3Public.callContractMethod( + this.sourceBridgeAddress, + this.sourceBridgeAbi, + 'minPerTx', + [address] + ); + return new BigNumber(amount); + } + + protected async checkSourceLimits(address: string, amount: string): Promise { + const allowSend = await this.sourceWeb3Public.callContractMethod( + this.sourceBridgeAddress, + this.sourceBridgeAbi, + 'withinLimit', + [address, amount] + ); + if (!allowSend) { + throw new RubicSdkError('Swap is not allowed due to contract limitations'); + } + } + + protected async checkTargetLimits(address: string, amount: string): Promise { + const allowSend = await this.targetWeb3Public.callContractMethod( + this.targetBridgeAddress, + this.targetBridgeAbi, + 'withinExecutionLimit', + [address, amount] + ); + if (!allowSend) { + throw new RubicSdkError('Swap is not allowed due to contract limitations'); + } + } + + public getDataForNativeSwap(receiverAddress: string, value: string): EvmEncodeConfig { + return EvmWeb3Pure.encodeMethodCall( + omniBridgeNativeRouter, + nativeBridgeAbi, + 'wrapAndRelayTokens', + [receiverAddress], + value + ); + } + + public getDataForTokenSwap( + receiverAddress: string, + amount: string, + isERC677: boolean, + tokenAddress: string + ): EvmEncodeConfig { + if (isERC677) { + return EvmWeb3Pure.encodeMethodCall( + tokenAddress, + erc677Abi, + 'transferAndCall', + [this.sourceBridgeAddress, amount, receiverAddress], + '0' + ); + } + + return EvmWeb3Pure.encodeMethodCall( + this.sourceBridgeAddress, + this.sourceBridgeAbi, + 'relayTokens', + [tokenAddress, receiverAddress, amount], + '0' + ); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/home-bridge.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/home-bridge.ts new file mode 100644 index 0000000..e16c146 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/home-bridge.ts @@ -0,0 +1,115 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { erc677Abi } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/erc-677-abi'; +import { nativeBridgeAbi } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/native-bridge-abi'; +import { omniBridgeNativeRouter } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-contract-address'; +import { OmniBridge } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/omni-bridge'; + +export class HomeBridge extends OmniBridge { + public isTokenRegistered(address: string): Promise { + return this.sourceWeb3Public.callContractMethod( + this.sourceBridgeAddress, + this.sourceBridgeAbi, + 'isTokenRegistered', + [address] + ); + } + + protected isRegisteredAsNative(address: string): Promise { + return this.sourceWeb3Public.callContractMethod( + this.sourceBridgeAddress, + this.sourceBridgeAbi, + 'isRegisteredAsNativeToken', + [address] + ); + } + + protected getNonNativeToken(address: string): Promise { + return this.sourceWeb3Public.callContractMethod( + this.sourceBridgeAddress, + this.sourceBridgeAbi, + 'nativeTokenAddress', + [address] + ); + } + + protected getNativeToken(address: string): Promise { + return this.targetWeb3Public.callContractMethod( + this.targetBridgeAddress, + this.targetBridgeAbi, + 'bridgedTokenAddress', + [address] + ); + } + + public async getMinAmountToken(address: string): Promise { + const amount = await this.sourceWeb3Public.callContractMethod( + this.sourceBridgeAddress, + this.sourceBridgeAbi, + 'minPerTx', + [address] + ); + return new BigNumber(amount); + } + + protected async checkSourceLimits(address: string, amount: string): Promise { + const allowSend = await this.sourceWeb3Public.callContractMethod( + this.sourceBridgeAddress, + this.sourceBridgeAbi, + 'withinLimit', + [address, amount] + ); + if (!allowSend) { + throw new RubicSdkError('Swap is not allowed due to contract limitations'); + } + } + + protected async checkTargetLimits(address: string, amount: string): Promise { + const allowSend = await this.targetWeb3Public.callContractMethod( + this.targetBridgeAddress, + this.targetBridgeAbi, + 'withinExecutionLimit', + [address, amount] + ); + if (!allowSend) { + throw new RubicSdkError('Swap is not allowed due to contract limitations'); + } + } + + public getDataForNativeSwap(receiverAddress: string, value: string): EvmEncodeConfig { + return EvmWeb3Pure.encodeMethodCall( + omniBridgeNativeRouter, + nativeBridgeAbi, + 'wrapAndRelayTokens', + [receiverAddress], + value + ); + } + + public getDataForTokenSwap( + receiverAddress: string, + amount: string, + isERC677: boolean, + tokenAddress: string + ): EvmEncodeConfig { + if (isERC677) { + return EvmWeb3Pure.encodeMethodCall( + tokenAddress, + erc677Abi, + 'transferAndCall', + [this.sourceBridgeAddress, amount, receiverAddress], + '0' + ); + } + + return EvmWeb3Pure.encodeMethodCall( + this.sourceBridgeAddress, + this.sourceBridgeAbi, + 'relayTokens', + [tokenAddress, receiverAddress, amount], + '0' + ); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/omni-bridge.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/omni-bridge.ts new file mode 100644 index 0000000..03412c8 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/omni-bridge.ts @@ -0,0 +1,227 @@ +import BigNumber from 'bignumber.js'; +import { Token } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Injector } from 'src/core/injector/injector'; +import { feeManagerAbi } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/fee-manager-abi'; +import { foreignBridgeAbi } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/foreign-bridge-abi'; +import { homeBridgeAbi } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/home-bridge-abi'; +import { pulseChainContractAddress } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-contract-address'; +import { PulseChainCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-supported-blockchains'; +import { AbiItem } from 'web3-utils'; + +export abstract class OmniBridge { + public readonly sourceBridgeAddress: string; + + protected readonly targetBridgeAddress: string; + + protected readonly sourceBridgeAbi: AbiItem[]; + + protected readonly targetBridgeAbi: AbiItem[]; + + protected readonly sourceBlockchain: PulseChainCrossChainSupportedBlockchain; + + protected readonly targetBlockchain: PulseChainCrossChainSupportedBlockchain; + + protected get web3Private(): EvmWeb3Private { + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(this.sourceBlockchain); + } + + protected get sourceWeb3Public(): EvmWeb3Public { + return Injector.web3PublicService.getWeb3Public(this.sourceBlockchain); + } + + protected get targetWeb3Public(): EvmWeb3Public { + return Injector.web3PublicService.getWeb3Public(this.targetBlockchain); + } + + constructor( + private readonly fromToken: Token, + private readonly toToken: Token + ) { + this.sourceBlockchain = fromToken.blockchain; + this.targetBlockchain = toToken.blockchain; + + this.sourceBridgeAddress = OmniBridge.isCustomWrap(fromToken) + ? pulseChainContractAddress[this.sourceBlockchain].omniBridgeWrapped + : pulseChainContractAddress[this.sourceBlockchain].omniBridge; + + this.targetBridgeAddress = OmniBridge.isCustomWrap(toToken) + ? pulseChainContractAddress[this.targetBlockchain].omniBridgeWrapped + : pulseChainContractAddress[this.targetBlockchain].omniBridge; + + if (this.sourceBlockchain === BLOCKCHAIN_NAME.ETHEREUM) { + this.sourceBridgeAbi = foreignBridgeAbi; + this.targetBridgeAbi = homeBridgeAbi; + } else { + this.sourceBridgeAbi = foreignBridgeAbi; + this.targetBridgeAbi = homeBridgeAbi; + } + } + + /** + * Check if token registered in source network. + * @param address Token address in source network. + */ + public abstract isTokenRegistered(address: string): Promise; + + /** + * Check if registered is native bridge token. + * @param address Token address in source network. + */ + protected abstract isRegisteredAsNative(address: string): Promise; + + /** + * Fetch target token address for non native token. + * @param address Token address in source network. + */ + protected abstract getNonNativeToken(address: string): Promise; + + /** + * Fetch target token address for native token. + * @param address Token address in source network. + */ + protected abstract getNativeToken(address: string): Promise; + + /** + * Get bridge token address in target network. + * @param fromAddress Token address in source network. + */ + public async getBridgeToken(fromAddress: string): Promise { + const isRegisteredAsNative = await this.isRegisteredAsNative(fromAddress); + return isRegisteredAsNative + ? this.getNativeToken(fromAddress) + : this.getNonNativeToken(fromAddress); + } + + /** + * Get min swap amount in source network. + * @param address Token address in source network. + */ + public abstract getMinAmountToken(address: string): Promise; + + /** + * Check if token allowed to send in source network (min/max amounts, daily limits). + * @param address Token address in source network. + * @param amount Swap amount. + */ + protected abstract checkSourceLimits(address: string, amount: string): Promise; + + /** + * Check if token allowed to get in target network (min/max amounts, daily limits). + * @param address Token address in target network. + * @param amount Swap amount. + */ + protected abstract checkTargetLimits(address: string, amount: string): Promise; + + /** + * Check if allowed to swap. + * @param fromAddress Token address in source network. + * @param toAddress Token address in target network. + * @param amount Swap amount. + */ + public async checkLimits( + fromAddress: string, + toAddress: string, + amount: string + ): Promise { + await this.checkSourceLimits(fromAddress, amount); + await this.checkTargetLimits(toAddress, amount); + } + + /** + * Get fee manager address. + */ + private getFeeManager(): Promise { + const web3Public = Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.PULSECHAIN); + return web3Public.callContractMethod( + this.sourceBlockchain === BLOCKCHAIN_NAME.PULSECHAIN + ? this.sourceBridgeAddress + : this.targetBridgeAddress, + homeBridgeAbi, + 'feeManager', + [] + ); + } + + /** + * + * Get fee type for trade. + */ + private getFeeType(feeManagerAddress: string): Promise { + const web3Public = Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.PULSECHAIN); + return web3Public.callContractMethod( + feeManagerAddress, + feeManagerAbi, + this.sourceBlockchain === BLOCKCHAIN_NAME.ETHEREUM + ? 'FOREIGN_TO_HOME_FEE' + : 'HOME_TO_FOREIGN_FEE', + [] + ); + } + + /** + * Calculate output amount for trade. + * @param toAddress Token address in target network. + * @param feeManagerAddress Fee manager contract address. + * @param feeType Type of fee. + * @param fromAmount Amount of tokens to send. + */ + private async getOutputAmount( + toAddress: string, + feeManagerAddress: string, + feeType: string, + fromAmount: string + ): Promise { + const web3Public = Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.PULSECHAIN); + const feeAmount = await web3Public.callContractMethod( + feeManagerAddress, + feeManagerAbi, + 'calculateFee', + [feeType, toAddress, fromAmount] + ); + return new BigNumber(fromAmount).minus(feeAmount); + } + + /** + * Calculate output amount for trade. + * @param toAddress Token address in target network. + * @param fromAmount Amount of tokens to send. + */ + public async calculateAmount(toAddress: string, fromAmount: string): Promise { + const feeManagerAddress = await this.getFeeManager(); + const feeType = await this.getFeeType(feeManagerAddress); + return this.getOutputAmount(toAddress, feeManagerAddress, feeType, fromAmount); + } + + public static isCustomWrap(token: Token): boolean { + return ( + token.blockchain === BLOCKCHAIN_NAME.ETHEREUM && + compareAddresses(token.address, '0xA882606494D86804B5514E07e6Bd2D6a6eE6d68A') + ); + } + + /** + * Get swap data for native token trade. + * @param receiverAddress Receiver address user get money to. + * @param value Amount of money user spend. + */ + public abstract getDataForNativeSwap(receiverAddress: string, value: string): EvmEncodeConfig; + + /** + * Get swap data for native token trade. + * @param receiverAddress Receiver address user get money to. + * @param amount Amount of money user spend. + * @param isERC677 Is token part of ERC677 token standard. + * @param tokenAddress Address of sending token. + */ + public abstract getDataForTokenSwap( + receiverAddress: string, + amount: string, + isERC677: boolean, + tokenAddress: string + ): EvmEncodeConfig; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/pulse-chain-cross-chain-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/pulse-chain-cross-chain-provider.ts new file mode 100644 index 0000000..44377f6 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/pulse-chain-cross-chain-provider.ts @@ -0,0 +1,299 @@ +import BigNumber from 'bignumber.js'; +import { MinAmountError, NotSupportedTokensError, RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, Token } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { + PulseChainCrossChainSupportedBlockchain, + pulseChainSupportedBlockchains +} from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-supported-blockchains'; +import { BridgeManager } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/bridge-manager'; +import { OmniBridge } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/omni-bridge'; +import { PulseChainCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/pulse-chain-cross-chain-trade'; +import { typedTradeProviders } from 'src/features/on-chain/calculation-manager/constants/trade-providers/typed-trade-providers'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +export class PulseChainCrossChainProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.PULSE_CHAIN_BRIDGE; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is PulseChainCrossChainSupportedBlockchain { + return pulseChainSupportedBlockchains.some(chain => chain === blockchain); + } + + public async calculate( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + const fromBlockchain = fromToken.blockchain as PulseChainCrossChainSupportedBlockchain; + const toBlockchain = toToken.blockchain as PulseChainCrossChainSupportedBlockchain; + const useProxy = options?.useProxy?.[this.type] ?? true; + + if ( + !this.areSupportedBlockchains(fromBlockchain, toBlockchain) || + // @TODO Remove after home bridge development + fromToken.blockchain === BLOCKCHAIN_NAME.PULSECHAIN + ) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + const feeInfo = await this.getFeeInfo( + fromBlockchain, + options.providerAddress, + fromToken, + useProxy + ); + + try { + const sourceBridgeManager = BridgeManager.createBridge( + fromToken as Token, + toToken as Token + ); + const targetBridgeManager = BridgeManager.createBridge( + fromToken as Token, + toToken as Token + ); + const fromTokenAddress = this.getTokenAddress(fromToken); + const toTokenAddress = this.getTokenAddress(toToken); + + const tokenRegistered = await sourceBridgeManager.isTokenRegistered(fromTokenAddress); + const targetTokenAddress = await sourceBridgeManager.getBridgeToken(fromTokenAddress); + + if (!compareAddresses(toTokenAddress, targetTokenAddress)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + const fromWithoutFee = getFromWithoutFee( + fromToken, + feeInfo.rubicProxy?.platformFee?.percent + ); + + let onChainTrade: EvmOnChainTrade | null = null; + let transitTokenAmount = fromWithoutFee.tokenAmount; + let transitMinAmount = transitTokenAmount; + let transitToken = fromWithoutFee; + + if (!tokenRegistered) { + if (!useProxy) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + const transitTokenAddress = await targetBridgeManager.getBridgeToken( + toToken.address + ); + + onChainTrade = await this.getOnChainTrade( + fromWithoutFee, + [], + options.slippageTolerance, + transitTokenAddress + ); + if (!onChainTrade) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + transitTokenAmount = onChainTrade.to.tokenAmount; + transitMinAmount = onChainTrade.toTokenAmountMin.tokenAmount; + transitToken = onChainTrade.to; + } + + const targetAmount = await sourceBridgeManager.calculateAmount( + toToken.address, + Web3Pure.toWei(transitTokenAmount, transitToken.decimals) + ); + const targetAmountMin = await sourceBridgeManager.calculateAmount( + toToken.address, + Web3Pure.toWei(transitMinAmount, transitToken.decimals) + ); + + const amountsErrors = await this.getMinMaxAmountsErrors( + fromTokenAddress, + sourceBridgeManager, + transitToken + ); + + if (!targetAmount) { + throw new RubicSdkError('Can not estimate trade'); + } + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: Web3Pure.fromWei(targetAmount, toToken.decimals) + }); + + const gasData = + options.gasCalculation === 'enabled' + ? await PulseChainCrossChainTrade.getGasData( + fromToken, + to, + onChainTrade, + feeInfo, + targetAmountMin, + options.providerAddress, + options.receiverAddress || this.getWalletAddress(fromToken.blockchain), + sourceBridgeManager.sourceBridgeAddress, + tokenRegistered + ) + : null; + + return { + trade: new PulseChainCrossChainTrade( + { + from: fromToken, + to, + gasData, + slippage: options.slippageTolerance, + feeInfo: feeInfo, + toTokenAmountMin: targetAmountMin, + onChainTrade, + priceImpact: fromToken.calculatePriceImpactPercent(to), + routerAddress: sourceBridgeManager.sourceBridgeAddress, + tokenRegistered + }, + options.providerAddress, + await this.getRoutePath(fromToken, transitToken, to, onChainTrade) + ), + error: amountsErrors, + tradeType: this.type + }; + } catch (err) { + const rubicSdkError = CrossChainProvider.parseError(err); + + return { + trade: null, + error: rubicSdkError, + tradeType: this.type + }; + } + } + + private async getOnChainTrade( + from: PriceTokenAmount, + _availableDexes: string[], + slippageTolerance: number, + transitTokenAddress: string + ): Promise { + const fromBlockchain = from.blockchain; + + const dexes = Object.values(typedTradeProviders[fromBlockchain]).filter( + dex => dex.supportReceiverAddress + ); + const to = await PriceToken.createToken({ + address: transitTokenAddress, + blockchain: fromBlockchain + }); + const onChainTrades = ( + await Promise.allSettled( + dexes.map(dex => + dex.calculate(from, to, { + slippageTolerance, + gasCalculation: 'disabled', + useProxy: false + }) + ) + ) + ) + .filter(value => value.status === 'fulfilled') + .map(value => (value as PromiseFulfilledResult).value) + .sort((a, b) => b.to.tokenAmount.comparedTo(a.to.tokenAmount)); + + if (!onChainTrades.length) { + return null; + } + return onChainTrades[0]!; + } + + private async getMinMaxAmountsErrors( + fromTokenAddress: string, + fromBridgeManager: OmniBridge, + fromToken: PriceTokenAmount + ): Promise { + try { + const minAmountWei = await fromBridgeManager.getMinAmountToken(fromTokenAddress); + const minAmount = new BigNumber(minAmountWei); + + if (minAmount.gte(fromToken.stringWeiAmount)) { + return new MinAmountError( + Web3Pure.fromWei(minAmount, fromToken.decimals), + fromToken.symbol + ); + } + } catch { + return undefined; + } + return undefined; + } + + protected async getFeeInfo( + fromBlockchain: PulseChainCrossChainSupportedBlockchain, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } + + protected async getRoutePath( + from: PriceTokenAmount, + transit: PriceTokenAmount, + to: PriceTokenAmount, + onChainTrade: EvmOnChainTrade | null + ): Promise { + const routePath: RubicStep[] = []; + if (onChainTrade) { + routePath.push({ + type: 'on-chain', + path: [from, transit], + provider: onChainTrade.type + }); + } + routePath.push({ + type: 'cross-chain', + path: [transit, to], + provider: CROSS_CHAIN_TRADE_TYPE.PULSE_CHAIN_BRIDGE + }); + return routePath; + } + + private getTokenAddress(token: Token): string { + if (token.blockchain === BLOCKCHAIN_NAME.ETHEREUM) { + return token.isNative ? '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' : token.address; + } + return token.isNative ? '0xA1077a294dDE1B09bB078844df40758a5D0f9a27' : token.address; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/pulse-chain-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/pulse-chain-cross-chain-trade.ts new file mode 100644 index 0000000..4246f42 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/pulse-chain-cross-chain-trade.ts @@ -0,0 +1,336 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount, Token } from 'src/common/tokens'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { OnChainSubtype } from 'src/features/cross-chain/calculation-manager/providers/common/models/on-chain-subtype'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { PulseChainCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/constants/pulse-chain-supported-blockchains'; +import { BridgeManager } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/bridge-manager'; +import { OmniBridge } from 'src/features/cross-chain/calculation-manager/providers/pulse-chain-bridge/omni-bridge-entities/omni-bridge'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +export class PulseChainCrossChainTrade extends EvmCrossChainTrade { + /** @internal */ + public static async getGasData( + from: PriceTokenAmount, + toToken: PriceTokenAmount, + onChainTrade: EvmOnChainTrade | null, + feeInfo: FeeInfo, + toTokenAmountMin: BigNumber, + providerAddress: string, + receiverAddress: string, + routerAddress: string, + tokenRegistered: boolean + ): Promise { + try { + const trade = new PulseChainCrossChainTrade( + { + from, + to: toToken, + gasData: null, + priceImpact: 0, + slippage: 0, + feeInfo: feeInfo!, + toTokenAmountMin, + onChainTrade: onChainTrade, + routerAddress, + tokenRegistered + }, + providerAddress || EvmWeb3Pure.EMPTY_ADDRESS, + [] + ); + + return getCrossChainGasData(trade, receiverAddress); + } catch (_err) { + return null; + } + } + + public readonly type = CROSS_CHAIN_TRADE_TYPE.PULSE_CHAIN_BRIDGE; + + public readonly isAggregator = false; + + public readonly bridgeType = BRIDGE_TYPE.PULSE_CHAIN_BRIDGE; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly priceImpact: number | null; + + public readonly gasData: GasData | null; + + private readonly routerAddress: string; + + private get fromBlockchain(): PulseChainCrossChainSupportedBlockchain { + return this.from.blockchain as PulseChainCrossChainSupportedBlockchain; + } + + protected get fromContractAddress(): string { + return this.isProxyTrade + ? rubicProxyContractAddress[this.fromBlockchain].gateway + : this.routerAddress; + } + + public readonly feeInfo: FeeInfo; + + private readonly slippage: number; + + public readonly onChainSubtype: OnChainSubtype; + + public readonly onChainTrade: EvmOnChainTrade | null; + + protected get methodName(): string { + if (this.isErc677 && this.from.blockchain === BLOCKCHAIN_NAME.ETHEREUM) { + return this.onChainTrade + ? 'swapAndStartBridgeTokensViaTransferAndCall' + : 'startBridgeTokensViaTransferAndCall'; + } + return this.onChainTrade + ? 'swapAndStartBridgeTokensViaGenericCrossChain' + : 'startBridgeTokensViaGenericCrossChain'; + } + + private readonly isTokenRegistered: boolean; + + private get isErc677(): boolean { + return !this.isTokenRegistered || OmniBridge.isCustomWrap(this.from); + } + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + gasData: GasData | null; + slippage: number; + feeInfo: FeeInfo; + toTokenAmountMin: BigNumber; + onChainTrade: EvmOnChainTrade | null; + priceImpact: number | null; + routerAddress: string; + tokenRegistered: boolean; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.gasData = crossChainTrade.gasData; + this.slippage = crossChainTrade.slippage; + this.toTokenAmountMin = Web3Pure.fromWei( + crossChainTrade.toTokenAmountMin, + crossChainTrade.to.decimals + ); + this.feeInfo = crossChainTrade.feeInfo; + this.priceImpact = crossChainTrade.priceImpact; + this.routerAddress = crossChainTrade.routerAddress; + this.onChainSubtype = crossChainTrade.onChainTrade + ? { from: crossChainTrade.onChainTrade.type, to: undefined } + : { from: undefined, to: undefined }; + this.onChainTrade = crossChainTrade.onChainTrade; + this.isTokenRegistered = crossChainTrade.tokenRegistered; + } + + public async needApprove(): Promise { + this.checkWalletConnected(); + + if (this.from.isNative || (this.isErc677 && !this.isProxyTrade)) { + return false; + } + + const allowance = await this.fromWeb3Public.getAllowance( + this.from.address, + this.walletAddress, + this.fromContractAddress + ); + return this.from.weiAmount.gt(allowance); + } + + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + await this.checkTradeErrors(); + + if (!this.isProxyTrade || this.isTokenRegistered) { + await this.checkAllowanceAndApprove(options); + } + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + // eslint-disable-next-line no-useless-catch + try { + const { data, to, value } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options.receiverAddress || this.walletAddress + ); + + await this.web3Private.trySendTransaction(to, { + data, + value, + onTransactionHash, + gas: gasLimit, + gasPriceOptions + }); + + return transactionHash!; + } catch (err) { + throw err; + } + } + + public async getContractParams(options: GetContractParamsOptions): Promise { + const receiverAddress = options?.receiverAddress || this.walletAddress; + const { + data, + to, + value: providerValue + } = await this.setTransactionConfig(false, options?.useCacheData || false, receiverAddress); + + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData(options, { + walletAddress: this.walletAddress, + fromTokenAmount: this.from, + toTokenAmount: this.to, + srcChainTrade: this.onChainTrade, + providerAddress: this.providerAddress, + type: `native:${this.type}`, + fromAddress: this.walletAddress + }); + const swapData = + this.onChainTrade && + (await ProxyCrossChainEvmTrade.getSwapData(options, { + walletAddress: this.walletAddress, + contractAddress: rubicProxyContractAddress[this.from.blockchain].router, + fromTokenAmount: this.from, + toTokenAmount: this.onChainTrade.to, + onChainEncodeFn: this.onChainTrade.encode.bind(this.onChainTrade) + })); + + const providerData = this.isErc677 + ? this.getProviderDataForErc677(this.from, this.to, receiverAddress) + : await ProxyCrossChainEvmTrade.getGenericProviderData( + to, + data!, + this.fromBlockchain, + to, + '0' + ); + + const methodArguments = swapData + ? [bridgeData, swapData, providerData] + : [bridgeData, providerData]; + + const value = this.getSwapValue(providerValue); + + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + return fromUsd.dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact ?? null, + slippage: this.slippage * 100, + routePath: this.routePath + }; + } + + protected async getTransactionConfigAndAmount(receiverAddress: string): Promise<{ + config: EvmEncodeConfig; + amount: string; + }> { + const toToken = this.to; + const fromToken = this.onChainTrade ? this.onChainTrade.to : this.from; + const sourceBridgeManager = BridgeManager.createBridge( + fromToken as Token, + toToken as Token + ); + const tokenAddress = this.getTokenAddress(fromToken); + const amount = this.to.stringWeiAmount; + + if (fromToken.isNative) { + return { + config: sourceBridgeManager.getDataForNativeSwap( + receiverAddress, + fromToken.stringWeiAmount + ), + amount + }; + } + + return { + config: sourceBridgeManager.getDataForTokenSwap( + receiverAddress, + fromToken.stringWeiAmount, + this.isErc677, + tokenAddress + ), + amount + }; + } + + private getTokenAddress(token: Token): string { + if (token.blockchain === BLOCKCHAIN_NAME.ETHEREUM) { + return token.isNative ? '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' : token.address; + } + return token.isNative ? '0xA1077a294dDE1B09bB078844df40758a5D0f9a27' : token.address; + } + + private getProviderDataForErc677( + fromToken: Token, + toToken: Token, + receiverAddress: string + ): unknown[] { + const sourceBridgeManager = BridgeManager.createBridge( + fromToken as Token, + toToken as Token + ); + + return [sourceBridgeManager.sourceBridgeAddress, receiverAddress]; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/model/rango-cross-chain-api-types.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/model/rango-cross-chain-api-types.ts new file mode 100644 index 0000000..bc89640 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/model/rango-cross-chain-api-types.ts @@ -0,0 +1,9 @@ +import { RequiredCrossChainOptions } from '../../../models/cross-chain-options'; + +/** + * @property {RangoSupportedBlockchain[]} swappers List of all accepted swappers (e.g. providers), an empty list means no filter is required + * @property {boolean} [swappersGroupsExclude] - Defines the provided swappers' tags as the include/exclude list. Default is false (include) + */ +export type RangoCrossChainOptions = RequiredCrossChainOptions & { + swappersGroupsExclude?: boolean; +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/model/rango-cross-chain-parser-types.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/model/rango-cross-chain-parser-types.ts new file mode 100644 index 0000000..a29f788 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/model/rango-cross-chain-parser-types.ts @@ -0,0 +1,43 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { RangoSwapQueryParams } from 'src/features/common/providers/rango/models/rango-parser-types'; +import { BridgeType } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; + +import { GasData } from '../../common/emv-cross-chain-trade/models/gas-data'; +import { FeeInfo } from '../../common/models/fee-info'; +import { RubicStep } from '../../common/models/rubicStep'; +import { RangoCrossChainOptions } from './rango-cross-chain-api-types'; + +export interface RangoCrossChainTradeConstructorParams { + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + gasData: GasData | null; + toTokenAmountMin: BigNumber; + feeInfo: FeeInfo; + priceImpact: number | null; + slippage: number; + swapQueryParams: RangoSwapQueryParams; + bridgeSubtype: BridgeType; + }; + providerAddress: string; + routePath: RubicStep[]; +} + +export interface GetCrossChainTradeConstructorParamsType { + fromToken: PriceTokenAmount; + toToken: PriceTokenAmount; + options: RangoCrossChainOptions; + routePath: RubicStep[]; + feeInfo: FeeInfo; + toTokenAmountMin: BigNumber; + swapQueryParams: RangoSwapQueryParams; + bridgeSubtype: BridgeType; + receiverAddress: string; +} + +export type RangoGetGasDataParams = Omit< + GetCrossChainTradeConstructorParamsType, + 'toTokenAmountMin' | 'options' +>; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/rango-cross-chain-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/rango-cross-chain-provider.ts new file mode 100644 index 0000000..6c1c0f0 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/rango-cross-chain-provider.ts @@ -0,0 +1,225 @@ +import BigNumber from 'bignumber.js'; +import { PriceToken, PriceTokenAmount, TokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { Any } from 'src/common/utils/types'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { + RANGO_API_ENDPOINT, + RANGO_API_KEY +} from 'src/features/common/providers/rango/constants/rango-api-common'; +import { + RangoBestRouteSimulationResult, + RangoQuotePath, + RangoSwapFee +} from 'src/features/common/providers/rango/models/rango-api-best-route-types'; +import { RangoTradeType } from 'src/features/common/providers/rango/models/rango-api-trade-types'; +import { + RangoSupportedBlockchain, + rangoSupportedBlockchains +} from 'src/features/common/providers/rango/models/rango-supported-blockchains'; +import { RangoCommonParser } from 'src/features/common/providers/rango/services/rango-parser'; +import { RangoUtils } from 'src/features/common/providers/rango/utils/rango-utils'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; + +import { CROSS_CHAIN_TRADE_TYPE, CrossChainTradeType } from '../../models/cross-chain-trade-type'; +import { CrossChainProvider } from '../common/cross-chain-provider'; +import { CalculationResult } from '../common/models/calculation-result'; +import { FeeInfo } from '../common/models/fee-info'; +import { CrossChainStep, RubicStep } from '../common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from '../common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { RangoCrossChainOptions } from './model/rango-cross-chain-api-types'; +import { RangoCrossChainTrade } from './rango-cross-chain-trade'; +import { RangoCrossChainApiService } from './services/rango-cross-chain-api-service'; +import { RangoCrossChainParser } from './services/rango-cross-chain-params-parser'; + +export class RangoCrossChainProvider extends CrossChainProvider { + public type: CrossChainTradeType = CROSS_CHAIN_TRADE_TYPE.RANGO; + + public static readonly apiKey = RANGO_API_KEY; + + public static readonly apiEndpoint = RANGO_API_ENDPOINT; + + private rangoSupportedBlockchains = rangoSupportedBlockchains; + + public isSupportedBlockchain(blockchain: EvmBlockchainName): boolean { + return this.rangoSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RangoCrossChainOptions + ): Promise { + const fromBlockchain = from.blockchain as RangoSupportedBlockchain; + const useProxy = options?.useProxy?.[this.type] ?? true; + + try { + const feeInfo = await this.getFeeInfo( + fromBlockchain, + options.providerAddress, + from, + useProxy + ); + + const fromWithoutFee = getFromWithoutFee( + from, + feeInfo.rubicProxy?.platformFee?.percent + ); + + const bestRouteParams = await RangoCommonParser.getBestRouteQueryParams( + fromWithoutFee, + toToken, + { ...options, swapperGroups: options.rangoDisabledProviders } + ); + + const { route } = await RangoCrossChainApiService.getBestRoute(bestRouteParams); + const { outputAmountMin, outputAmount, path, fee } = + route as RangoBestRouteSimulationResult; + + const toTokenAmountMin = Web3Pure.fromWei(outputAmountMin, toToken.decimals); + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: Web3Pure.fromWei(outputAmount, toToken.decimals) + }); + const routePath = await this.getRoutePath(from, to, path); + + const swapQueryParams = await RangoCommonParser.getSwapQueryParams( + fromWithoutFee, + toToken, + { ...options, swapperGroups: options.rangoDisabledProviders } + ); + + const cryptoFee = await this.getCryptoFee(fee, fromBlockchain); + + if (cryptoFee?.amount.gt(0)) { + feeInfo.provider = { + cryptoFee + }; + } + + const bridgeSubtype = ( + routePath.find(el => el.type === 'cross-chain') as CrossChainStep + )?.provider; + const fakeAddress = '0xe388Ed184958062a2ea29B7fD049ca21244AE02e'; + const tradeParams = await RangoCrossChainParser.getTradeConstructorParams({ + fromToken: from, + toToken: to, + options, + routePath, + feeInfo, + toTokenAmountMin, + swapQueryParams, + bridgeSubtype, + receiverAddress: options?.receiverAddress || fakeAddress + }); + + const trade = new RangoCrossChainTrade(tradeParams); + const tradeType = this.type; + + return { trade, tradeType }; + } catch (err) { + const rubicSdkError = CrossChainProvider.parseError(err); + + return { + trade: null, + error: rubicSdkError, + tradeType: this.type + }; + } + } + + protected async getRoutePath( + fromToken: PriceTokenAmount, + toToken: PriceTokenAmount, + path: RangoQuotePath[] | null + ): Promise { + if (!path) { + return [{ type: 'cross-chain', provider: this.type, path: [fromToken, toToken] }]; + } + + return Promise.all(path.map(path => this.getStep(path))); + } + + private async getStep(rangoPath: RangoQuotePath): Promise { + const type = rangoPath.swapperType === 'DEX' ? 'on-chain' : 'cross-chain'; + + const provider = RangoUtils.getTradeTypeForRubic( + rangoPath.swapper.swapperGroup as RangoTradeType + ); + + const fromBlockchain = RangoUtils.getRubicBlockchainByRangoBlockchain( + rangoPath.from.blockchain + ); + const toBlockchain = RangoUtils.getRubicBlockchainByRangoBlockchain( + rangoPath.to.blockchain + ); + + const fromTokenAmount = await TokenAmount.createToken({ + address: rangoPath.from.address || nativeTokensList[fromBlockchain].address, + blockchain: fromBlockchain, + weiAmount: new BigNumber(rangoPath.inputAmount) + }); + + const toTokenAmount = await TokenAmount.createToken({ + address: rangoPath.to.address || nativeTokensList[toBlockchain].address, + blockchain: toBlockchain, + weiAmount: new BigNumber(rangoPath.expectedOutput) + }); + + return { + provider: provider as Any, + type: type, + path: [fromTokenAmount, toTokenAmount] + }; + } + + protected async getFeeInfo( + fromBlockchain: RangoSupportedBlockchain, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } + + private async getCryptoFee( + fee: RangoSwapFee[], + fromBlockchain: BlockchainName + ): Promise<{ + amount: BigNumber; + token: PriceToken; + }> { + const nativeToken = nativeTokensList[fromBlockchain]; + + if (!fee) { + return { + amount: new BigNumber(0), + token: await PriceTokenAmount.createFromToken({ + ...nativeToken, + weiAmount: new BigNumber(0) + }) + }; + } + + const feeAmount = fee + .filter(fee => fee.expenseType === 'FROM_SOURCE_WALLET') + .reduce((acc, fee) => acc.plus(fee.amount), new BigNumber(0)); + const cryptoFeeToken = await PriceTokenAmount.createFromToken({ + ...nativeToken, + weiAmount: new BigNumber(feeAmount) + }); + + return { + amount: Web3Pure.fromWei(feeAmount, nativeToken.decimals), + token: cryptoFeeToken + }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/rango-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/rango-cross-chain-trade.ts new file mode 100644 index 0000000..bcdd43e --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/rango-cross-chain-trade.ts @@ -0,0 +1,245 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { rangoContractAddresses } from 'src/features/common/providers/rango/constants/rango-contract-address'; +import { RangoSwapQueryParams } from 'src/features/common/providers/rango/models/rango-parser-types'; +import { RangoSupportedBlockchain } from 'src/features/common/providers/rango/models/rango-supported-blockchains'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; + +import { CROSS_CHAIN_TRADE_TYPE, CrossChainTradeType } from '../../models/cross-chain-trade-type'; +import { rubicProxyContractAddress } from '../common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from '../common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from '../common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from '../common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from '../common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE, BridgeType } from '../common/models/bridge-type'; +import { FeeInfo } from '../common/models/fee-info'; +import { GetContractParamsOptions } from '../common/models/get-contract-params-options'; +import { OnChainSubtype } from '../common/models/on-chain-subtype'; +import { TradeInfo } from '../common/models/trade-info'; +import { ProxyCrossChainEvmTrade } from '../common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { + RangoCrossChainTradeConstructorParams, + RangoGetGasDataParams +} from './model/rango-cross-chain-parser-types'; +import { RangoCrossChainApiService } from './services/rango-cross-chain-api-service'; + +export class RangoCrossChainTrade extends EvmCrossChainTrade { + /** @internal */ + public static async getGasData({ + fromToken, + toToken, + feeInfo, + routePath, + swapQueryParams, + bridgeSubtype, + receiverAddress + }: RangoGetGasDataParams): Promise { + try { + const trade = new RangoCrossChainTrade({ + crossChainTrade: { + from: fromToken, + to: toToken, + toTokenAmountMin: new BigNumber(0), + feeInfo, + gasData: null, + priceImpact: fromToken.calculatePriceImpactPercent(toToken) || 0, + slippage: swapQueryParams.slippage, + bridgeSubtype, + swapQueryParams + }, + routePath, + providerAddress: swapQueryParams.toAddress + }); + + return getCrossChainGasData(trade, receiverAddress); + } catch (err) { + return null; + } + } + + public readonly type: CrossChainTradeType = CROSS_CHAIN_TRADE_TYPE.RANGO; + + public readonly isAggregator: boolean = true; + + public readonly to: PriceTokenAmount; + + public readonly from: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly feeInfo: FeeInfo; + + public readonly onChainSubtype: OnChainSubtype = { from: undefined, to: undefined }; + + public readonly bridgeType: BridgeType = BRIDGE_TYPE.RANGO; + + public readonly gasData: GasData; + + public readonly priceImpact: number | null; + + private readonly slippage: number; + + /** + * @description UUID returned by rango-api to track transaction status in getRangoDstSwapStatus + */ + public rangoRequestId: string | undefined; + + private readonly swapQueryParams: RangoSwapQueryParams; + + private get fromBlockchain(): RangoSupportedBlockchain { + return this.from.blockchain as RangoSupportedBlockchain; + } + + protected get fromContractAddress(): string { + return this.isProxyTrade + ? rubicProxyContractAddress[this.fromBlockchain].gateway + : rangoContractAddresses[this.fromBlockchain].providerGateway; + } + + protected get methodName(): string { + return 'startBridgeTokensViaGenericCrossChain'; + } + + constructor(params: RangoCrossChainTradeConstructorParams) { + super(params.providerAddress, params.routePath); + this.to = params.crossChainTrade.to; + this.from = params.crossChainTrade.from; + this.toTokenAmountMin = params.crossChainTrade.toTokenAmountMin; + this.feeInfo = params.crossChainTrade.feeInfo; + this.gasData = params.crossChainTrade.gasData; + this.priceImpact = params.crossChainTrade.priceImpact; + this.slippage = params.crossChainTrade.slippage; + this.swapQueryParams = params.crossChainTrade.swapQueryParams; + this.bridgeType = params.crossChainTrade.bridgeSubtype || BRIDGE_TYPE.RANGO; + } + + public async getContractParams(options: GetContractParamsOptions): Promise { + const receiverAddress = options?.receiverAddress || this.walletAddress; + + const { + data, + value: providerValue, + to: providerRouter + } = await this.setTransactionConfig(false, options?.useCacheData || false, receiverAddress); + + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData(options, { + walletAddress: receiverAddress, + fromTokenAmount: this.from, + toTokenAmount: this.to, + srcChainTrade: null, + providerAddress: this.providerAddress, + type: `rango:${this.bridgeType}`, + fromAddress: this.walletAddress + }); + + const extraNativeFee = this.from.isNative + ? new BigNumber(providerValue).minus(this.from.stringWeiAmount).toFixed() + : new BigNumber(providerValue).toFixed(); + + const providerData = await ProxyCrossChainEvmTrade.getGenericProviderData( + providerRouter, + data!, + this.from.blockchain, + providerRouter, + extraNativeFee + ); + + const methodArguments = [bridgeData, providerData]; + const value = this.getSwapValue(providerValue); + + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } + + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + await this.checkTradeErrors(); + await this.checkAllowanceAndApprove(options); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + // eslint-disable-next-line no-useless-catch + try { + const receiverAddress = options?.receiverAddress || this.walletAddress; + const { data, value, to } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + receiverAddress + ); + + await this.web3Private.trySendTransaction(to, { + data, + value, + onTransactionHash, + gas: gasLimit, + gasPriceOptions + }); + + return transactionHash!; + } catch (err) { + throw err; + } + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + const { route, tx, error, requestId } = await RangoCrossChainApiService.getSwapTransaction({ + ...this.swapQueryParams, + toAddress: receiverAddress || this.swapQueryParams.toAddress + }); + + this.rangoRequestId = requestId; + + if (!route || !tx) { + throw new RubicSdkError('Invalid data after sending swap request. Error text:' + error); + } + + const config = { + data: tx.txData!, + value: tx.value || '0', + to: tx.txTo + }; + + return { config, amount: route.outputAmount }; + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact || null, + slippage: this.slippage * 100, + routePath: this.routePath + }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/services/rango-cross-chain-api-service.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/services/rango-cross-chain-api-service.ts new file mode 100644 index 0000000..7ae3e7f --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/services/rango-cross-chain-api-service.ts @@ -0,0 +1,70 @@ +import { RubicSdkError } from 'src/common/errors'; +import { Cache } from 'src/common/utils/decorators'; +import { Injector } from 'src/core/injector/injector'; +import { RANGO_API_ENDPOINT } from 'src/features/common/providers/rango/constants/rango-api-common'; +import { RangoBestRouteResponse } from 'src/features/common/providers/rango/models/rango-api-best-route-types'; +import { HttpClientParams } from 'src/features/common/providers/rango/models/rango-api-common-types'; +import { RangoTxStatusResponse } from 'src/features/common/providers/rango/models/rango-api-status-types'; +import { RangoSwapTransactionResponse } from 'src/features/common/providers/rango/models/rango-api-swap-types'; +import { + RangoBestRouteQueryParams, + RangoSwapQueryParams, + RangoTxStatusQueryParams +} from 'src/features/common/providers/rango/models/rango-parser-types'; + +export class RangoCrossChainApiService { + public static async getBestRoute( + params: RangoBestRouteQueryParams + ): Promise { + const res = await Injector.httpClient.get( + `${RANGO_API_ENDPOINT}/quote`, + { + params: params as unknown as HttpClientParams + } + ); + + if (!res.route || res.error) { + throw new RubicSdkError(res.error ?? 'No available routes in rango.'); + } + + return res; + } + + @Cache({ + maxAge: 15_000 + }) + public static async getSwapTransaction( + params: RangoSwapQueryParams + ): Promise { + const res = await Injector.httpClient.get( + `${RANGO_API_ENDPOINT}/swap`, + { params: params as unknown as HttpClientParams } + ); + + if (!res.route || res.error) { + throw new RubicSdkError(res.error ?? 'No available routes in rango.'); + } + + return res; + } + + /** + * @description Get transaction status data + */ + public static async getTxStatus( + params: RangoTxStatusQueryParams + ): Promise { + const res = await Injector.httpClient.get( + `${RANGO_API_ENDPOINT}/status`, + { params: params as unknown as HttpClientParams } + ); + + if (res.error || !res.bridgeData || !res.status) { + throw new RubicSdkError( + "Can't get status, res has error or null data in getTxStatus method" + ); + } + + return res; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/services/rango-cross-chain-params-parser.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/services/rango-cross-chain-params-parser.ts new file mode 100644 index 0000000..1666a11 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/rango-provider/services/rango-cross-chain-params-parser.ts @@ -0,0 +1,54 @@ +import { + GetCrossChainTradeConstructorParamsType, + RangoCrossChainTradeConstructorParams +} from '../model/rango-cross-chain-parser-types'; +import { RangoCrossChainTrade } from '../rango-cross-chain-trade'; + +export class RangoCrossChainParser { + public static async getTradeConstructorParams({ + feeInfo, + fromToken, + options, + routePath, + swapQueryParams, + toToken, + toTokenAmountMin, + bridgeSubtype, + receiverAddress + }: GetCrossChainTradeConstructorParamsType): Promise { + const gasData = + options.gasCalculation === 'enabled' + ? await RangoCrossChainTrade.getGasData({ + fromToken, + toToken, + swapQueryParams, + feeInfo, + routePath, + bridgeSubtype, + receiverAddress + }) + : null; + const priceImpact = fromToken.calculatePriceImpactPercent(toToken); + const slippage = options.slippageTolerance; + + const crossChainTrade: RangoCrossChainTradeConstructorParams['crossChainTrade'] = { + from: fromToken, + to: toToken, + gasData, + toTokenAmountMin, + priceImpact, + slippage, + feeInfo, + swapQueryParams, + bridgeSubtype + }; + + const providerAddress = options.providerAddress; + + return { + crossChainTrade, + providerAddress, + routePath + }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/l1-erc20-scroll-gateway-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/l1-erc20-scroll-gateway-abi.ts new file mode 100644 index 0000000..534c1a0 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/l1-erc20-scroll-gateway-abi.ts @@ -0,0 +1,55 @@ +import { AbiItem } from 'web3-utils'; + +export const l1Erc20ScrollGatewayAbi: AbiItem[] = [ + { + inputs: [ + { internalType: 'address', name: '_token', type: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + { internalType: 'uint256', name: '_gasLimit', type: 'uint256' } + ], + name: 'depositERC20', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: '_token', type: 'address' }, + { internalType: 'address', name: '_to', type: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + { internalType: 'uint256', name: '_gasLimit', type: 'uint256' } + ], + name: 'depositERC20', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + { internalType: 'uint256', name: '_gasLimit', type: 'uint256' } + ], + name: 'depositETH', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: '_to', type: 'address' }, + { internalType: 'uint256', name: '_amount', type: 'uint256' }, + { internalType: 'uint256', name: '_gasLimit', type: 'uint256' } + ], + name: 'depositETH', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [{ internalType: 'address', name: '_l1Address', type: 'address' }], + name: 'getL2ERC20Address', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/l2-erc20-scroll-gateway-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/l2-erc20-scroll-gateway-abi.ts new file mode 100644 index 0000000..732617b --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/l2-erc20-scroll-gateway-abi.ts @@ -0,0 +1,55 @@ +import { AbiItem } from 'web3-utils'; + +export const l2Erc20ScrollGatewayAbi: AbiItem[] = [ + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'address', name: '', internalType: 'address' }], + name: 'getL1ERC20Address', + inputs: [{ type: 'address', name: '_l2Address', internalType: 'address' }] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'withdrawERC20', + inputs: [ + { type: 'address', name: '_token', internalType: 'address' }, + { type: 'uint256', name: '_amount', internalType: 'uint256' }, + { type: 'uint256', name: '_gasLimit', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'withdrawERC20', + inputs: [ + { type: 'address', name: '_token', internalType: 'address' }, + { type: 'address', name: '_to', internalType: 'address' }, + { type: 'uint256', name: '_amount', internalType: 'uint256' }, + { type: 'uint256', name: '_gasLimit', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'withdrawETH', + inputs: [ + { type: 'address', name: '_to', internalType: 'address' }, + { type: 'uint256', name: '_amount', internalType: 'uint256' }, + { type: 'uint256', name: '_gasLimit', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'withdrawETH', + inputs: [ + { type: 'uint256', name: '_amount', internalType: 'uint256' }, + { type: 'uint256', name: '_gasLimit', internalType: 'uint256' } + ] + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/scroll-bridge-contract-address.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/scroll-bridge-contract-address.ts new file mode 100644 index 0000000..8e33200 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/scroll-bridge-contract-address.ts @@ -0,0 +1,19 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniversalContract } from 'src/features/cross-chain/calculation-manager/providers/common/models/universal-contract'; +import { ScrollBridgeSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/scroll-bridge/models/scroll-bridge-supported-blockchain'; + +export const scrollBridgeContractAddress: Record< + ScrollBridgeSupportedBlockchain, + UniversalContract +> = { + [BLOCKCHAIN_NAME.SCROLL_SEPOLIA]: { + providerGateway: '0x91e8ADDFe1358aCa5314c644312d38237fC1101C', + providerRouter: '0x91e8ADDFe1358aCa5314c644312d38237fC1101C', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + }, + [BLOCKCHAIN_NAME.GOERLI]: { + providerGateway: '0xe5E30E7c24e4dFcb281A682562E53154C15D3332', + providerRouter: '0xe5E30E7c24e4dFcb281A682562E53154C15D3332', + rubicRouter: '0x33798753ec66aEc00ed7E337B41F444f53A63333' + } +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/models/scroll-bridge-supported-blockchain.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/models/scroll-bridge-supported-blockchain.ts new file mode 100644 index 0000000..7ae456c --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/models/scroll-bridge-supported-blockchain.ts @@ -0,0 +1,8 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const scrollBridgeSupportedBlockchains = [ + BLOCKCHAIN_NAME.GOERLI, + BLOCKCHAIN_NAME.SCROLL_SEPOLIA +] as const; + +export type ScrollBridgeSupportedBlockchain = (typeof scrollBridgeSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/scroll-bridge-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/scroll-bridge-provider.ts new file mode 100644 index 0000000..90233d4 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/scroll-bridge-provider.ts @@ -0,0 +1,133 @@ +import { NotSupportedTokensError, RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { Injector } from 'src/core/injector/injector'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CbridgeCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-supported-blockchains'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { l1Erc20ScrollGatewayAbi } from 'src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/l1-erc20-scroll-gateway-abi'; +import { l2Erc20ScrollGatewayAbi } from 'src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/l2-erc20-scroll-gateway-abi'; +import { + ScrollBridgeSupportedBlockchain, + scrollBridgeSupportedBlockchains +} from 'src/features/cross-chain/calculation-manager/providers/scroll-bridge/models/scroll-bridge-supported-blockchain'; +import { ScrollBridgeTrade } from 'src/features/cross-chain/calculation-manager/providers/scroll-bridge/scroll-bridge-trade'; + +import { scrollBridgeContractAddress } from './constants/scroll-bridge-contract-address'; + +export class ScrollBridgeProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.SCROLL_BRIDGE; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is ScrollBridgeSupportedBlockchain { + return scrollBridgeSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public async calculate( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + const fromBlockchain = fromToken.blockchain as ScrollBridgeSupportedBlockchain; + const toBlockchain = toToken.blockchain as ScrollBridgeSupportedBlockchain; + + if (!this.areSupportedBlockchains(fromBlockchain, toBlockchain)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + try { + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + + if (!fromToken.isNative) { + if (fromBlockchain === BLOCKCHAIN_NAME.GOERLI) { + const l2Address = await web3Public.callContractMethod( + scrollBridgeContractAddress[fromBlockchain]!.providerGateway, + l1Erc20ScrollGatewayAbi, + 'getL2ERC20Address', + [fromToken.address] + ); + if (!compareAddresses(toToken.address, l2Address)) { + throw new RubicSdkError('Swap is not allowed.'); + } + } else { + const l1Address = await web3Public.callContractMethod( + scrollBridgeContractAddress[fromBlockchain]!.providerGateway, + l2Erc20ScrollGatewayAbi, + 'getL1ERC20Address', + [fromToken.address] + ); + if (!compareAddresses(toToken.address, l1Address)) { + throw new RubicSdkError('Swap is not allowed.'); + } + } + } else { + if (!toToken.isNative) { + throw new RubicSdkError('Swap is not allowed.'); + } + } + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: fromToken.tokenAmount + }); + + const gasData = + options.gasCalculation === 'enabled' + ? await ScrollBridgeTrade.getGasData(fromToken, to) + : null; + + return { + trade: new ScrollBridgeTrade( + { + from: fromToken, + to, + gasData + }, + options.providerAddress, + await this.getRoutePath(fromToken, to) + ), + tradeType: this.type + }; + } catch (err) { + const rubicSdkError = CrossChainProvider.parseError(err); + + return { + trade: null, + error: rubicSdkError, + tradeType: this.type + }; + } + } + + protected async getFeeInfo( + _fromBlockchain: CbridgeCrossChainSupportedBlockchain, + _providerAddress: string, + _percentFeeToken: PriceTokenAmount, + _useProxy: boolean + ): Promise { + return {}; + } + + protected async getRoutePath( + fromToken: PriceTokenAmount, + toToken: PriceTokenAmount + ): Promise { + return [{ type: 'cross-chain', provider: this.type, path: [fromToken, toToken] }]; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/scroll-bridge-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/scroll-bridge-trade.ts new file mode 100644 index 0000000..9361c64 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/scroll-bridge/scroll-bridge-trade.ts @@ -0,0 +1,283 @@ +import { + getL2Network, + L1TransactionReceipt, + L2ToL1MessageReader, + L2TransactionReceipt +} from '@arbitrum/sdk'; +import { JsonRpcProvider } from '@ethersproject/providers'; +import BigNumber from 'bignumber.js'; +import { BigNumber as EtherBigNumber } from 'ethers'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { outboxAbi } from 'src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/outbox-abi'; +import { retryableFactoryAbi } from 'src/features/cross-chain/calculation-manager/providers/arbitrum-rbc-bridge/constants/retryable-factory-abi'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { l1Erc20ScrollGatewayAbi } from 'src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/l1-erc20-scroll-gateway-abi'; +import { l2Erc20ScrollGatewayAbi } from 'src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/l2-erc20-scroll-gateway-abi'; +import { scrollBridgeContractAddress } from 'src/features/cross-chain/calculation-manager/providers/scroll-bridge/constants/scroll-bridge-contract-address'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; +import { TransactionReceipt } from 'web3-eth'; + +import { ScrollBridgeSupportedBlockchain } from './models/scroll-bridge-supported-blockchain'; + +export class ScrollBridgeTrade extends EvmCrossChainTrade { + /** @internal */ + public static async getGasData( + from: PriceTokenAmount, + to: PriceTokenAmount + ): Promise { + const trade = new ScrollBridgeTrade( + { + from, + to, + gasData: null + }, + EvmWeb3Pure.EMPTY_ADDRESS, + [] + ); + return getCrossChainGasData(trade); + } + + public readonly onChainSubtype = { from: undefined, to: undefined }; + + public readonly type = CROSS_CHAIN_TRADE_TYPE.SCROLL_BRIDGE; + + public readonly isAggregator = false; + + public readonly bridgeType = BRIDGE_TYPE.SCROLL_BRIDGE; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly gasData: GasData | null; + + private get fromBlockchain(): ScrollBridgeSupportedBlockchain { + return this.from.blockchain as ScrollBridgeSupportedBlockchain; + } + + protected get fromContractAddress(): string { + return scrollBridgeContractAddress[this.fromBlockchain]!.providerGateway; + } + + public readonly feeInfo: FeeInfo = {}; + + public readonly onChainTrade = null; + + protected get methodName(): string { + return this.onChainTrade + ? 'swapAndStartBridgeTokensViaGenericCrossChain' + : 'startBridgeTokensViaGenericCrossChain'; + } + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + gasData: GasData | null; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.gasData = crossChainTrade.gasData; + this.toTokenAmountMin = crossChainTrade.to.tokenAmount; + } + + public async getContractParams(_options: GetContractParamsOptions): Promise { + throw new Error('Method is not supported'); + } + + public getTradeAmountRatio(_fromUsd: BigNumber): BigNumber { + return new BigNumber(1); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: null, + slippage: 0, + routePath: this.routePath + }; + } + + public static async claimTargetTokens( + sourceTransaction: string, + options: SwapTransactionOptions + ): Promise { + const web3Private = Injector.web3PrivateService.getWeb3PrivateByBlockchain( + BLOCKCHAIN_NAME.ETHEREUM + ); + await web3Private.checkBlockchainCorrect(BLOCKCHAIN_NAME.ETHEREUM); + + const rpcProviders = Injector.web3PublicService.rpcProvider; + const l1Provider = new JsonRpcProvider( + rpcProviders[BLOCKCHAIN_NAME.ETHEREUM]!.rpcList[0]!, + blockchainId[BLOCKCHAIN_NAME.ETHEREUM] + ); + const l2Provider = new JsonRpcProvider( + rpcProviders[BLOCKCHAIN_NAME.ARBITRUM]!.rpcList[0]!, + blockchainId[BLOCKCHAIN_NAME.ARBITRUM] + ); + const targetReceipt = await l2Provider.getTransactionReceipt(sourceTransaction); + const l2TxReceipt = new L2TransactionReceipt(targetReceipt); + const [event] = l2TxReceipt.getL2ToL1Events(); + if (!event) { + throw new RubicSdkError('Transaction is not ready'); + } + const messageReader = new L2ToL1MessageReader(l1Provider, event); + + const proof = await messageReader.getOutboxProof(l2Provider); + const l2network = await getL2Network(blockchainId[BLOCKCHAIN_NAME.ARBITRUM]); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + }; + + return web3Private.tryExecuteContractMethod( + l2network.ethBridge.outbox, + outboxAbi, + 'executeTransaction', + [ + proof, + (event as unknown as { position: EtherBigNumber }).position.toString(), + event.caller, + event.destination, + event.arbBlockNum.toString(), + event.ethBlockNum.toString(), + event.timestamp.toString(), + event.callvalue.toString(), + event.data + ], + { + onTransactionHash, + gas: gasLimit, + gasPriceOptions + } + ); + } + + public static async redeemTokens( + sourceTransactionHash: string, + options: SwapTransactionOptions + ): Promise { + const rpcProviders = Injector.web3PublicService.rpcProvider; + const l1Provider = new JsonRpcProvider( + rpcProviders[BLOCKCHAIN_NAME.ETHEREUM]!.rpcList[0]!, + blockchainId[BLOCKCHAIN_NAME.ETHEREUM] + ); + const l2Provider = new JsonRpcProvider( + rpcProviders[BLOCKCHAIN_NAME.ARBITRUM]!.rpcList[0]!, + blockchainId[BLOCKCHAIN_NAME.ARBITRUM] + ); + + const receipt = await l1Provider.getTransactionReceipt(sourceTransactionHash); + const messages = await new L1TransactionReceipt(receipt).getL1ToL2Messages(l2Provider); + const creationIdMessage = messages.find(el => el.retryableCreationId); + if (!creationIdMessage) { + throw new RubicSdkError('Can not find creation id message.'); + } + const { retryableCreationId } = creationIdMessage; + + const web3Private = Injector.web3PrivateService.getWeb3PrivateByBlockchain( + BLOCKCHAIN_NAME.ARBITRUM + ); + await web3Private.checkBlockchainCorrect(BLOCKCHAIN_NAME.ARBITRUM); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + }; + + return web3Private.tryExecuteContractMethod( + '0x000000000000000000000000000000000000006E', + retryableFactoryAbi, + 'redeem', + [retryableCreationId], + { + onTransactionHash, + gas: gasLimit, + gasPriceOptions + } + ); + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + let contractParams: ContractParams | null = null; + if (this.fromBlockchain === BLOCKCHAIN_NAME.GOERLI) { + const methodArguments = [ + ...(this.from.isNative ? [] : [this.from.address]), + ...(receiverAddress ? [receiverAddress] : []), + this.from.stringWeiAmount, + '40000' + ]; + const fee = Web3Pure.toWei(0.005); + + contractParams = { + contractAddress: scrollBridgeContractAddress[this.fromBlockchain]!.providerGateway, + contractAbi: l1Erc20ScrollGatewayAbi, + methodName: this.from.isNative ? 'depositETH' : 'depositERC20', + methodArguments, + value: this.from.isNative + ? this.from.weiAmount.plus(fee).toFixed() + : this.from.stringWeiAmount + }; + } else { + const methodArguments = [ + ...(this.from.isNative ? [] : [this.from.address]), + ...(receiverAddress ? [receiverAddress] : []), + this.from.stringWeiAmount, + '160000' + ]; + const fee = Web3Pure.toWei(0.005); + + contractParams = { + contractAddress: scrollBridgeContractAddress[this.fromBlockchain]!.providerGateway, + contractAbi: l2Erc20ScrollGatewayAbi, + methodName: this.from.isNative ? 'withdrawETH' : 'withdrawERC20', + methodArguments, + value: this.from.isNative + ? this.from.weiAmount.plus(fee).toFixed() + : this.from.stringWeiAmount + }; + } + + const config = EvmWeb3Pure.encodeMethodCall( + contractParams.contractAddress, + contractParams.contractAbi, + contractParams.methodName, + contractParams.methodArguments, + contractParams.value + ); + + return { config, amount: this.to.stringWeiAmount }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/constants/squidrouter-contract-address.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/constants/squidrouter-contract-address.ts new file mode 100644 index 0000000..847e450 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/constants/squidrouter-contract-address.ts @@ -0,0 +1,20 @@ +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { UniversalContract } from 'src/features/cross-chain/calculation-manager/providers/common/models/universal-contract'; +import { + SquidrouterCrossChainSupportedBlockchain, + squidrouterCrossChainSupportedBlockchains +} from 'src/features/cross-chain/calculation-manager/providers/squidrouter-provider/constants/squidrouter-cross-chain-supported-blockchain'; + +export const SquidrouterContractAddress: Record< + SquidrouterCrossChainSupportedBlockchain, + UniversalContract +> = squidrouterCrossChainSupportedBlockchains.reduce((acc, blockchain) => { + return { + ...acc, + [blockchain]: { + providerRouter: '0xce16F69375520ab01377ce7B88f5BA8C48F8D666', + providerGateway: '0xce16F69375520ab01377ce7B88f5BA8C48F8D666', + rubicRouter: rubicProxyContractAddress[blockchain].gateway + } + }; +}, {} as Record); diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/constants/squidrouter-cross-chain-supported-blockchain.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/constants/squidrouter-cross-chain-supported-blockchain.ts new file mode 100644 index 0000000..d0d9020 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/constants/squidrouter-cross-chain-supported-blockchain.ts @@ -0,0 +1,22 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const squidrouterCrossChainSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.MOONBEAM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.CELO, + BLOCKCHAIN_NAME.KAVA, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.MANTLE, + BLOCKCHAIN_NAME.SCROLL, + BLOCKCHAIN_NAME.BLAST +] as const; + +export type SquidrouterCrossChainSupportedBlockchain = + (typeof squidrouterCrossChainSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/estimation-response.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/estimation-response.ts new file mode 100644 index 0000000..4224a72 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/estimation-response.ts @@ -0,0 +1,85 @@ +interface Token { + readonly chainId: number; + readonly address: string; + readonly name: string; + readonly symbol: string; + readonly decimals: number; + readonly logoURI: string; + readonly coingeckoId: string; +} + +interface Dex { + readonly chainName: string; + readonly dexName: string; + readonly swapRouter: string; + readonly factory: string; + readonly isStable: boolean; +} + +interface RouteElement { + readonly type: string; + readonly dex: Dex; + readonly path: string[]; + readonly squidCallType: number; + readonly fromToken: Token; + readonly toToken: Token; + readonly fromAmount: string; + readonly toAmount: string; + readonly exchangeRate: string; + readonly priceImpact: string; + readonly dynamicSlippage: number; +} + +interface BridgeRouteElement { + readonly callData: string; + readonly callType: number; + readonly estimatedGas: string; + readonly payload: { + inputPos: number; + tokenAddress: string; + }; + readonly target: string; + readonly type: string; + readonly value: string; +} + +interface FeeCost { + readonly name: string; + readonly description: string; + readonly percentage: string; + readonly token: Token; + readonly amount: string; + readonly amountUSD: string; +} + +interface GasCost { + readonly type: string; + readonly token: Token; + readonly amount: string; + readonly amountUSD: string; + readonly gasPrice: string; + readonly maxFeePerGas: string; + readonly maxPriorityFeePerGas: string; + readonly estimate: string; + readonly limit: string; +} + +/** + * Estimation object. + */ +export interface SquidrouterEstimation { + readonly fromAmount: string; + readonly sendAmount: string; + readonly toAmount: string; + readonly toAmountMin: string; + readonly toAmountUSD: string; + readonly route: { + readonly fromChain: RouteElement[]; + readonly toChain: RouteElement[] | BridgeRouteElement[]; + }; + readonly feeCosts: FeeCost[]; + readonly gasCosts: GasCost[]; + readonly estimatedRouteDuration: number; + readonly exchangeRate: string; + readonly aggregatePriceImpact: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/transaction-request.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/transaction-request.ts new file mode 100644 index 0000000..7d51e95 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/transaction-request.ts @@ -0,0 +1,13 @@ +/** + * Transaction request params. + */ +export interface SquidrouterTransactionRequest { + readonly fromChain: number; + readonly fromToken: string; + readonly fromAmount: string; + readonly toChain: number; + readonly toToken: string; + readonly toAddress: string; + readonly slippage: number; + readonly enableForecall?: true; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/transaction-response.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/transaction-response.ts new file mode 100644 index 0000000..b91c73c --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/transaction-response.ts @@ -0,0 +1,29 @@ +import { SquidrouterEstimation } from 'src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/estimation-response'; + +export interface SquirouterTransaction { + readonly routeType: string; + readonly targetAddress: string; + readonly data: string; + readonly value: string; + readonly gasLimit: string; + readonly gasPrice: string; + readonly maxFeePerGas: string; + readonly maxPriorityFeePerGas: string; +} + +/** + * Swap transaction response. + */ +export interface SquidrouterTransactionResponse { + readonly route: { + /** + * Trade estimation response. + */ + readonly estimate: SquidrouterEstimation; + + /** + * Transaction data. + */ + readonly transactionRequest: SquirouterTransaction; + }; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/squidrouter-cross-chain-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/squidrouter-cross-chain-provider.ts new file mode 100644 index 0000000..02a53ad --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/squidrouter-cross-chain-provider.ts @@ -0,0 +1,250 @@ +import BigNumber from 'bignumber.js'; +import { NotSupportedTokensError, RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, Token, TokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { + SquidrouterCrossChainSupportedBlockchain, + squidrouterCrossChainSupportedBlockchains +} from 'src/features/cross-chain/calculation-manager/providers/squidrouter-provider/constants/squidrouter-cross-chain-supported-blockchain'; +import { SquidrouterEstimation } from 'src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/estimation-response'; +import { SquidrouterTransactionRequest } from 'src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/transaction-request'; +import { SquidrouterCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/squidrouter-provider/squidrouter-cross-chain-trade'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +export class SquidrouterCrossChainProvider extends CrossChainProvider { + public static readonly apiEndpoint = 'https://api.0xsquid.com/v1/'; + + private readonly nativeAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; + + public readonly type = CROSS_CHAIN_TRADE_TYPE.SQUIDROUTER; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is SquidrouterCrossChainSupportedBlockchain { + return squidrouterCrossChainSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + const fromBlockchain = from.blockchain as SquidrouterCrossChainSupportedBlockchain; + const toBlockchain = toToken.blockchain as SquidrouterCrossChainSupportedBlockchain; + const useProxy = options?.useProxy?.[this.type] ?? true; + + if (!this.areSupportedBlockchains(fromBlockchain, toBlockchain)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + try { + const feeInfo = await this.getFeeInfo( + fromBlockchain, + options.providerAddress, + from, + useProxy + ); + const fromWithoutFee = getFromWithoutFee( + from, + feeInfo.rubicProxy?.platformFee?.percent + ); + + const fakeAddress = '0xe388Ed184958062a2ea29B7fD049ca21244AE02e'; + const receiver = + options?.receiverAddress || this.getWalletAddress(fromBlockchain) || fakeAddress; + const requestParams: SquidrouterTransactionRequest = { + fromChain: blockchainId[fromBlockchain], + fromToken: from.isNative ? this.nativeAddress : from.address, + fromAmount: fromWithoutFee.stringWeiAmount, + toChain: blockchainId[toBlockchain], + toToken: toToken.isNative ? this.nativeAddress : toToken.address, + toAddress: receiver, + slippage: Number(options.slippageTolerance * 100) + }; + const { + route: { transactionRequest, estimate } + } = await SquidrouterCrossChainTrade.getResponseFromApiToTransactionRequest( + requestParams + ); + + const squidGasData: GasData = { + gasLimit: new BigNumber(transactionRequest.gasLimit).plus(useProxy ? 120000 : 0), + gasPrice: Web3Pure.fromWei(transactionRequest.gasPrice), + maxFeePerGas: new BigNumber(transactionRequest.maxFeePerGas), + maxPriorityFeePerGas: new BigNumber(transactionRequest.maxPriorityFeePerGas) + }; + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: Web3Pure.fromWei(estimate.toAmount, toToken.decimals) + }); + + const gasData = + options.gasCalculation === 'enabled' + ? await SquidrouterCrossChainTrade.getGasData( + from, + to, + requestParams, + feeInfo, + receiver, + options.providerAddress + ) + : null; + + const feeAmount = estimate.feeCosts + .filter(fee => compareAddresses(this.nativeAddress, fee.token.address)) + .reduce((acc, fee) => acc.plus(fee.amount), new BigNumber(0)); + const nativeToken = nativeTokensList[fromBlockchain]; + const cryptoFeeToken = await PriceTokenAmount.createFromToken({ + ...nativeToken, + weiAmount: new BigNumber(feeAmount) + }); + + const transitRoute = estimate.route.toChain.at(-1)!; + + const transitUSDAmount = + 'toAmount' in transitRoute + ? Web3Pure.fromWei(transitRoute.toAmount, transitRoute.toToken.decimals) + : new BigNumber(estimate.toAmountUSD); + + return { + trade: new SquidrouterCrossChainTrade( + { + from, + to, + gasData: gasData || squidGasData, + priceImpact: from.calculatePriceImpactPercent(to), + allowanceTarget: transactionRequest.targetAddress, + slippage: options.slippageTolerance, + feeInfo: { + ...feeInfo, + provider: { + cryptoFee: { + amount: Web3Pure.fromWei(feeAmount, nativeToken.decimals), + token: cryptoFeeToken + } + } + }, + transitUSDAmount, + cryptoFeeToken, + onChainTrade: null, + onChainSubtype: { from: undefined, to: undefined }, + transactionRequest: requestParams + }, + options.providerAddress, + await this.getRoutePath(estimate, from, to) + ), + tradeType: this.type + }; + } catch (err) { + let rubicSdkError = CrossChainProvider.parseError(err); + const httpError = err?.error?.errors?.[0]; + if (httpError?.message) { + rubicSdkError = new RubicSdkError(httpError.message); + } + + return { + trade: null, + error: rubicSdkError, + tradeType: this.type + }; + } + } + + protected async getFeeInfo( + fromBlockchain: SquidrouterCrossChainSupportedBlockchain, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } + + protected async getRoutePath( + estimation: SquidrouterEstimation, + from: PriceTokenAmount, + to: PriceTokenAmount + ): Promise { + const transitFrom = estimation.route.fromChain.map(el => ({ + address: el.toToken.address, + amount: new BigNumber(el.toAmount) + })); + + const transitTo = ( + estimation.route.toChain + // @ts-ignore + .filter(el => 'dex' in el) as SquidrouterEstimation['route']['fromChain'] + ).map(el => ({ + amount: new BigNumber(el.fromAmount), + address: el.fromToken.address + })); + + const fromTransitTokens = await Token.createTokens( + transitFrom.map(el => el.address), + from.blockchain + ); + + const toTransitTokens = await Token.createTokens( + transitTo.map(el => el.address), + to.blockchain + ); + + const fromTokenAmount = fromTransitTokens.map( + (token, index) => new TokenAmount({ ...token, weiAmount: transitFrom[index]!.amount }) + ); + + const toTokenAmount = toTransitTokens.map( + (token, index) => new TokenAmount({ ...token, weiAmount: transitTo[index]!.amount }) + ); + + const routePath: RubicStep[] = []; + if (fromTokenAmount.length) { + routePath.push({ + type: 'on-chain', + // @TODO Add generic provider + provider: ON_CHAIN_TRADE_TYPE.ONE_INCH, + path: [from, ...fromTokenAmount] + }); + } + routePath.push({ + type: 'cross-chain', + // @TODO Add generic provider + provider: CROSS_CHAIN_TRADE_TYPE.SQUIDROUTER, + path: [fromTokenAmount.at(-1) || from, toTokenAmount.at(0) || to] + }); + if (toTokenAmount.length) { + routePath.push({ + type: 'on-chain', + // @TODO Add generic provider + provider: ON_CHAIN_TRADE_TYPE.ONE_INCH, + path: [...toTokenAmount, to] + }); + } + + return routePath; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/squidrouter-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/squidrouter-cross-chain-trade.ts new file mode 100644 index 0000000..73928c3 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/squidrouter-provider/squidrouter-cross-chain-trade.ts @@ -0,0 +1,310 @@ +import BigNumber from 'bignumber.js'; +import { FailedToCheckForTransactionReceiptError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { Cache } from 'src/common/utils/decorators'; +import { parseError } from 'src/common/utils/errors'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Injector } from 'src/core/injector/injector'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { OnChainSubtype } from 'src/features/cross-chain/calculation-manager/providers/common/models/on-chain-subtype'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { SquidrouterContractAddress } from 'src/features/cross-chain/calculation-manager/providers/squidrouter-provider/constants/squidrouter-contract-address'; +import { SquidrouterCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/squidrouter-provider/constants/squidrouter-cross-chain-supported-blockchain'; +import { SquidrouterTransactionRequest } from 'src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/transaction-request'; +import { SquidrouterTransactionResponse } from 'src/features/cross-chain/calculation-manager/providers/squidrouter-provider/models/transaction-response'; +import { SquidrouterCrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/squidrouter-provider/squidrouter-cross-chain-provider'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +/** + * Calculated DeBridge cross-chain trade. + */ +export class SquidrouterCrossChainTrade extends EvmCrossChainTrade { + /** @internal */ + public readonly transitUSDAmount: BigNumber; + + private readonly cryptoFeeToken: PriceTokenAmount; + + private readonly slippage: number; + + private readonly onChainTrade: EvmOnChainTrade | null; + + private readonly transactionRequest: SquidrouterTransactionRequest; + + /** @internal */ + public static async getGasData( + from: PriceTokenAmount, + toToken: PriceTokenAmount, + transactionRequest: SquidrouterTransactionRequest, + feeInfo: FeeInfo, + receiverAddress: string, + providerAddress: string + ): Promise { + const fromBlockchain = from.blockchain as SquidrouterCrossChainSupportedBlockchain; + const walletAddress = + Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address; + if (!walletAddress) { + return null; + } + + try { + const trade = new SquidrouterCrossChainTrade( + { + from, + to: toToken, + gasData: null, + priceImpact: 0, + allowanceTarget: '', + slippage: 0, + feeInfo, + transitUSDAmount: new BigNumber(NaN), + cryptoFeeToken: from, + onChainTrade: null, + onChainSubtype: { from: undefined, to: undefined }, + transactionRequest + }, + providerAddress || EvmWeb3Pure.EMPTY_ADDRESS, + [] + ); + + return getCrossChainGasData(trade, receiverAddress); + } catch (_err) { + return null; + } + } + + public readonly type = CROSS_CHAIN_TRADE_TYPE.SQUIDROUTER; + + public readonly isAggregator = false; + + public readonly onChainSubtype: OnChainSubtype = { + from: undefined, + to: undefined + }; + + public readonly bridgeType = BRIDGE_TYPE.SQUIDROUTER; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly priceImpact: number | null; + + public readonly allowanceTarget: string; + + public readonly gasData: GasData | null; + + private get fromBlockchain(): SquidrouterCrossChainSupportedBlockchain { + return this.from.blockchain as SquidrouterCrossChainSupportedBlockchain; + } + + protected get fromContractAddress(): string { + return this.isProxyTrade + ? rubicProxyContractAddress[this.fromBlockchain].gateway + : SquidrouterContractAddress[this.fromBlockchain].providerGateway; + } + + public readonly feeInfo: FeeInfo; + + protected get methodName(): string { + return 'startBridgeTokensViaGenericCrossChain'; + } + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + gasData: GasData | null; + priceImpact: number | null; + allowanceTarget: string; + slippage: number; + feeInfo: FeeInfo; + transitUSDAmount: BigNumber; + cryptoFeeToken: PriceTokenAmount; + onChainTrade: EvmOnChainTrade | null; + onChainSubtype: OnChainSubtype; + transactionRequest: SquidrouterTransactionRequest; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.gasData = crossChainTrade.gasData; + this.priceImpact = crossChainTrade.priceImpact; + this.allowanceTarget = crossChainTrade.allowanceTarget; + this.slippage = crossChainTrade.slippage; + this.onChainTrade = crossChainTrade.onChainTrade; + this.toTokenAmountMin = this.to.tokenAmount.multipliedBy(1 - crossChainTrade.slippage); + this.feeInfo = crossChainTrade.feeInfo; + this.cryptoFeeToken = crossChainTrade.cryptoFeeToken; + this.onChainSubtype = crossChainTrade.onChainSubtype; + this.transactionRequest = crossChainTrade.transactionRequest; + this.transitUSDAmount = crossChainTrade.transitUSDAmount; + } + + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + this.checkWalletConnected(); + await this.checkAllowanceAndApprove(options); + let transactionHash: string; + + try { + const { data, value, to } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress || this.walletAddress + ); + + const { onConfirm } = options; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + await this.web3Private.trySendTransaction(to, { + onTransactionHash, + data, + value, + gas: options.gasLimit, + gasPriceOptions: options.gasPriceOptions + }); + + return transactionHash!; + } catch (err) { + if (err instanceof FailedToCheckForTransactionReceiptError) { + return transactionHash!; + } + throw parseError(err); + } + } + + public async getContractParams(options: GetContractParamsOptions): Promise { + const { + data, + value: providerValue, + to + } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress || this.walletAddress + ); + + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData(options, { + walletAddress: this.walletAddress, + fromTokenAmount: this.from, + toTokenAmount: this.to, + srcChainTrade: this.onChainTrade, + providerAddress: this.providerAddress, + type: `native:${this.type}`, + fromAddress: this.walletAddress + }); + + const extraNativeFee = this.from.isNative + ? new BigNumber(providerValue).minus(this.from.stringWeiAmount).toFixed() + : new BigNumber(providerValue).toFixed(); + const providerData = await ProxyCrossChainEvmTrade.getGenericProviderData( + to, + data!, + this.fromBlockchain, + to, + extraNativeFee + ); + + const methodArguments = [bridgeData, providerData]; + + const value = this.getSwapValue(providerValue); + + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + const usdCryptoFee = this.cryptoFeeToken.price.multipliedBy( + this.cryptoFeeToken.tokenAmount + ); + return fromUsd.plus(usdCryptoFee.isNaN() ? 0 : usdCryptoFee).dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact ?? null, + slippage: this.slippage * 100, + routePath: this.routePath + }; + } + + @Cache({ + maxAge: 15_000 + }) + public static async getResponseFromApiToTransactionRequest( + requestParams: SquidrouterTransactionRequest + ): Promise { + return Injector.httpClient.get( + `${SquidrouterCrossChainProvider.apiEndpoint}route`, + { + params: requestParams as unknown as {}, + headers: { + 'x-integrator-id': 'rubic-api' + } + } + ); + } + + protected async getTransactionConfigAndAmount( + receiverAddress: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + const requestParams: SquidrouterTransactionRequest = { + ...this.transactionRequest, + toAddress: receiverAddress + }; + + const { + route: { transactionRequest, estimate } + } = await SquidrouterCrossChainTrade.getResponseFromApiToTransactionRequest(requestParams); + + return { + config: { + data: transactionRequest.data, + value: transactionRequest.value, + to: transactionRequest.targetAddress + }, + amount: estimate.toAmount + }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/fee-library-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/fee-library-abi.ts new file mode 100644 index 0000000..f6413be --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/fee-library-abi.ts @@ -0,0 +1,48 @@ +import { AbiItem } from 'web3-utils'; + +export const feeLibraryAbi: AbiItem[] = [ + { + inputs: [ + { internalType: 'uint256', name: 'srcPoolId', type: 'uint256' }, + { internalType: 'uint256', name: 'dstPoolId', type: 'uint256' }, + { internalType: 'uint16', name: 'dstChainId', type: 'uint16' }, + { internalType: 'uint256', name: 'amountSD', type: 'uint256' }, + { internalType: 'bool', name: 'whitelisted', type: 'bool' }, + { internalType: 'bool', name: 'hasEqReward', type: 'bool' } + ], + name: 'getEquilibriumFee', + outputs: [ + { internalType: 'uint256', name: '', type: 'uint256' }, + { internalType: 'uint256', name: '', type: 'uint256' } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: '_srcPoolId', type: 'uint256' }, + { internalType: 'uint256', name: '_dstPoolId', type: 'uint256' }, + { internalType: 'uint16', name: '_dstChainId', type: 'uint16' }, + { internalType: 'address', name: '_from', type: 'address' }, + { internalType: 'uint256', name: '_amountSD', type: 'uint256' } + ], + name: 'getFees', + outputs: [ + { + components: [ + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint256', name: 'eqFee', type: 'uint256' }, + { internalType: 'uint256', name: 'eqReward', type: 'uint256' }, + { internalType: 'uint256', name: 'lpFee', type: 'uint256' }, + { internalType: 'uint256', name: 'protocolFee', type: 'uint256' }, + { internalType: 'uint256', name: 'lkbRemove', type: 'uint256' } + ], + internalType: 'struct Pool.SwapObj', + name: 's', + type: 'tuple' + } + ], + stateMutability: 'view', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/relayers-addresses.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/relayers-addresses.ts new file mode 100644 index 0000000..acd85c0 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/relayers-addresses.ts @@ -0,0 +1,14 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { StargateCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-cross-chain-supported-blockchain'; + +export const relayersAddresses: Record = { + [BLOCKCHAIN_NAME.ETHEREUM]: '0x', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: '0xBa26705a3EC868eDc5554FA18F9C756859fabB6E', + [BLOCKCHAIN_NAME.POLYGON]: '0x', + [BLOCKCHAIN_NAME.AVALANCHE]: '0x', + [BLOCKCHAIN_NAME.FANTOM]: '0x', + [BLOCKCHAIN_NAME.ARBITRUM]: '0x', + [BLOCKCHAIN_NAME.OPTIMISM]: '0x', + [BLOCKCHAIN_NAME.METIS]: '0x', + [BLOCKCHAIN_NAME.BASE]: '0x' +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-blockchain-supported-pool.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-blockchain-supported-pool.ts new file mode 100644 index 0000000..1155f8f --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-blockchain-supported-pool.ts @@ -0,0 +1,72 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { stargateBridgeToken } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-bridge-token'; +import { stargatePoolId } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-id'; + +import { StargateCrossChainSupportedBlockchain } from './stargate-cross-chain-supported-blockchain'; + +// Stargate close pools for BUSD and MAI +export const stargateBlockchainSupportedPools: Record< + StargateCrossChainSupportedBlockchain, + number[] +> = { + [BLOCKCHAIN_NAME.ETHEREUM]: [ + stargatePoolId[stargateBridgeToken.USDC], + stargatePoolId[stargateBridgeToken.USDT], + stargatePoolId[stargateBridgeToken.DAI], + stargatePoolId[stargateBridgeToken.FRAX], + stargatePoolId[stargateBridgeToken.USDD], + stargatePoolId[stargateBridgeToken.ETH], + stargatePoolId[stargateBridgeToken.sUSD], + stargatePoolId[stargateBridgeToken.LUSD], + // stargatePoolId[stargateBridgeToken.MAI], + stargatePoolId[stargateBridgeToken.METIS], + stargatePoolId[stargateBridgeToken.mUSD] + ], + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [ + stargatePoolId[stargateBridgeToken.USDT], + // stargatePoolId[stargateBridgeToken.BUSD], + stargatePoolId[stargateBridgeToken.USDD], + // stargatePoolId[stargateBridgeToken.MAI], + stargatePoolId[stargateBridgeToken.METIS], + stargatePoolId[stargateBridgeToken.mUSD] + ], + [BLOCKCHAIN_NAME.POLYGON]: [ + stargatePoolId[stargateBridgeToken.USDC], + stargatePoolId[stargateBridgeToken.USDT], + stargatePoolId[stargateBridgeToken.DAI] + // stargatePoolId[stargateBridgeToken.MAI] + ], + [BLOCKCHAIN_NAME.AVALANCHE]: [ + stargatePoolId[stargateBridgeToken.USDC], + stargatePoolId[stargateBridgeToken.USDT], + // stargatePoolId[stargateBridgeToken.MAI], + stargatePoolId[stargateBridgeToken.FRAX], + stargatePoolId[stargateBridgeToken.METIS], + stargatePoolId[stargateBridgeToken.mUSD] + ], + [BLOCKCHAIN_NAME.FANTOM]: [stargatePoolId[stargateBridgeToken.FUSDC]], + [BLOCKCHAIN_NAME.ARBITRUM]: [ + stargatePoolId[stargateBridgeToken.USDC], + stargatePoolId[stargateBridgeToken.USDT], + // stargatePoolId[stargateBridgeToken.MAI], + stargatePoolId[stargateBridgeToken.FRAX], + stargatePoolId[stargateBridgeToken.ETH] + ], + [BLOCKCHAIN_NAME.OPTIMISM]: [ + stargatePoolId[stargateBridgeToken.USDC], + stargatePoolId[stargateBridgeToken.DAI], + // stargatePoolId[stargateBridgeToken.MAI], + stargatePoolId[stargateBridgeToken.FRAX], + stargatePoolId[stargateBridgeToken.ETH], + stargatePoolId[stargateBridgeToken.sUSD], + stargatePoolId[stargateBridgeToken.LUSD] + ], + [BLOCKCHAIN_NAME.METIS]: [ + stargatePoolId[stargateBridgeToken.mUSD], + stargatePoolId[stargateBridgeToken.METIS] + ], + [BLOCKCHAIN_NAME.BASE]: [ + stargatePoolId[stargateBridgeToken.USDC], + stargatePoolId[stargateBridgeToken.ETH] + ] +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-bridge-token.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-bridge-token.ts new file mode 100644 index 0000000..92b4188 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-bridge-token.ts @@ -0,0 +1,22 @@ +// Stargate close pools for BUSD and MAI +export const stargateBridgeToken = { + USDC: 'USDC', + USDT: 'USDT', + DAI: 'DAI', + // MAI: 'MAI', + FRAX: 'FRAX', + USDD: 'USDD', + sUSD: 'sUSD', + LUSD: 'LUSD', + // BUSD: 'BUSD', + mUSD: 'm.USDT', + METIS: 'METIS', + // ETHs + ETH: 'ETH', + WETH: 'WETH', + SGETH: 'SGETH', + AETH: 'AETH', + FUSDC: 'FUSDC' +} as const; + +export type StargateBridgeToken = (typeof stargateBridgeToken)[keyof typeof stargateBridgeToken]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-chain-id.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-chain-id.ts new file mode 100644 index 0000000..8a708d1 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-chain-id.ts @@ -0,0 +1,15 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { StargateCrossChainSupportedBlockchain } from './stargate-cross-chain-supported-blockchain'; + +export const stargateChainId: Record = { + [BLOCKCHAIN_NAME.ETHEREUM]: 101, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: 102, + [BLOCKCHAIN_NAME.POLYGON]: 109, + [BLOCKCHAIN_NAME.AVALANCHE]: 106, + [BLOCKCHAIN_NAME.FANTOM]: 112, + [BLOCKCHAIN_NAME.ARBITRUM]: 110, + [BLOCKCHAIN_NAME.OPTIMISM]: 111, + [BLOCKCHAIN_NAME.METIS]: 151, + [BLOCKCHAIN_NAME.BASE]: 184 +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-contract-address.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-contract-address.ts new file mode 100644 index 0000000..4b1da96 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-contract-address.ts @@ -0,0 +1,24 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { StargateCrossChainSupportedBlockchain } from './stargate-cross-chain-supported-blockchain'; + +export const stargateContractAddress: Record = { + [BLOCKCHAIN_NAME.ETHEREUM]: '0x8731d54E9D02c286767d56ac03e8037C07e01e98', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: '0x4a364f8c717cAAD9A442737Eb7b8A55cc6cf18D8', + [BLOCKCHAIN_NAME.POLYGON]: '0x45A01E4e04F14f7A4a6702c74187c5F6222033cd', + [BLOCKCHAIN_NAME.AVALANCHE]: '0x45A01E4e04F14f7A4a6702c74187c5F6222033cd', + [BLOCKCHAIN_NAME.FANTOM]: '0xAf5191B0De278C7286d6C7CC6ab6BB8A73bA2Cd6', + [BLOCKCHAIN_NAME.ARBITRUM]: '0x53Bf833A5d6c4ddA888F69c22C88C9f356a41614', + [BLOCKCHAIN_NAME.OPTIMISM]: '0xB0D502E938ed5f4df2E681fE6E419ff29631d62b', + [BLOCKCHAIN_NAME.METIS]: '0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590', + [BLOCKCHAIN_NAME.BASE]: '0x45f1a95a4d3f3836523f5c83673c797f4d4d263b' +}; + +export const stargateEthContractAddress: Partial< + Record +> = { + [BLOCKCHAIN_NAME.ETHEREUM]: '0x150f94B44927F078737562f0fcF3C95c01Cc2376', + [BLOCKCHAIN_NAME.OPTIMISM]: '0xB49c4e680174E331CB0A7fF3Ab58afC9738d5F8b', + [BLOCKCHAIN_NAME.ARBITRUM]: '0xbf22f0f184bCcbeA268dF387a49fF5238dD23E40', + [BLOCKCHAIN_NAME.BASE]: '0x50B6EbC2103BFEc165949CC946d739d5650d7ae4' +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-cross-chain-supported-blockchain.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-cross-chain-supported-blockchain.ts new file mode 100644 index 0000000..0e166ad --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-cross-chain-supported-blockchain.ts @@ -0,0 +1,16 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const stargateCrossChainSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.METIS, + BLOCKCHAIN_NAME.BASE +] as const; + +export type StargateCrossChainSupportedBlockchain = + (typeof stargateCrossChainSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-factory-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-factory-abi.ts new file mode 100644 index 0000000..711abf5 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-factory-abi.ts @@ -0,0 +1,11 @@ +import { AbiItem } from 'web3-utils'; + +export const stargateFactoryAbi: AbiItem[] = [ + { + inputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + name: 'getPool', + outputs: [{ internalType: 'contract Pool', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-fee-library-contract-address.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-fee-library-contract-address.ts new file mode 100644 index 0000000..1217267 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-fee-library-contract-address.ts @@ -0,0 +1,17 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { StargateCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-cross-chain-supported-blockchain'; + +export const stargateFeeLibraryContractAddress: Record< + StargateCrossChainSupportedBlockchain, + string +> = { + [BLOCKCHAIN_NAME.ETHEREUM]: '0x8C3085D9a554884124C998CDB7f6d7219E9C1e6F', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: '0xCA6522116e8611A346D53Cc2005AC4192e3fc2BC', + [BLOCKCHAIN_NAME.POLYGON]: '0xb279b324Ea5648bE6402ABc727173A225383494C', + [BLOCKCHAIN_NAME.AVALANCHE]: '0x5E8eC15ACB5Aa94D5f0589E54441b31c5e0B992d', + [BLOCKCHAIN_NAME.FANTOM]: '0x616a68BD6DAd19e066661C7278611487d4072839', + [BLOCKCHAIN_NAME.ARBITRUM]: '0x1cF31666c06ac3401ed0C1c6346C4A9425dd7De4', + [BLOCKCHAIN_NAME.OPTIMISM]: '0x505eCDF2f14Cd4f1f413d04624b009A449D38D7E', + [BLOCKCHAIN_NAME.METIS]: '0x55bDb4164D28FBaF0898e0eF14a589ac09Ac9970', + [BLOCKCHAIN_NAME.BASE]: '0x9d1b1669c73b033dfe47ae5a0164ab96df25b944' +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-abi.ts new file mode 100644 index 0000000..9622666 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-abi.ts @@ -0,0 +1,11 @@ +import { AbiItem } from 'web3-utils'; + +export const stargatePoolAbi: AbiItem[] = [ + { + inputs: [], + name: 'token', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-id.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-id.ts new file mode 100644 index 0000000..21fc168 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-id.ts @@ -0,0 +1,29 @@ +import { + StargateBridgeToken, + stargateBridgeToken +} from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-bridge-token'; + +// Stargate close pools for BUSD and MAI +const pools = { + [stargateBridgeToken.USDC]: 1, + [stargateBridgeToken.USDT]: 2, + [stargateBridgeToken.DAI]: 3, + // [stargateBridgeToken.BUSD]: 5, + [stargateBridgeToken.FRAX]: 7, + [stargateBridgeToken.USDD]: 11, + [stargateBridgeToken.sUSD]: 14, + [stargateBridgeToken.LUSD]: 15, + // [stargateBridgeToken.MAI]: 16, + [stargateBridgeToken.METIS]: 17, + [stargateBridgeToken.mUSD]: 19, + // ETHs + [stargateBridgeToken.ETH]: 13, + [stargateBridgeToken.WETH]: 13, + [stargateBridgeToken.AETH]: 13, + [stargateBridgeToken.SGETH]: 13, + [stargateBridgeToken.FUSDC]: 21 +} as const; + +export const stargatePoolId: Record = pools; + +export type StargatePoolId = (typeof pools)[keyof typeof pools]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-mapping.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-mapping.ts new file mode 100644 index 0000000..3a4943b --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-mapping.ts @@ -0,0 +1,343 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { + StargateBridgeToken, + stargateBridgeToken +} from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-bridge-token'; +import { StargateCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-cross-chain-supported-blockchain'; + +type StargatePoolMapping = Record< + StargateCrossChainSupportedBlockchain, + Partial< + Record< + StargateBridgeToken, + Partial> + > + > +>; + +// Stargate close pools for BUSD and MAI +export const stargatePoolMapping: StargatePoolMapping = { + [BLOCKCHAIN_NAME.ETHEREUM]: { + [stargateBridgeToken.USDC]: { + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [ + stargateBridgeToken.USDT + // stargateBridgeToken.BUSD + ], + [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.FANTOM]: [stargateBridgeToken.FUSDC], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.BASE]: [stargateBridgeToken.USDC, stargateBridgeToken.ETH] + }, + [stargateBridgeToken.USDT]: { + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [ + // stargateBridgeToken.BUSD, + stargateBridgeToken.USDT + ], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.FANTOM]: [stargateBridgeToken.FUSDC] + }, + [stargateBridgeToken.DAI]: { + [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.DAI], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.DAI] + }, + // [stargateBridgeToken.MAI]: { + // [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.MAI] + // }, + [stargateBridgeToken.FRAX]: { + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.FRAX], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.FRAX], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.FRAX] + }, + [stargateBridgeToken.USDD]: { + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [stargateBridgeToken.USDD] + }, + [stargateBridgeToken.ETH]: { + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.ETH], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.ETH], + [BLOCKCHAIN_NAME.BASE]: [stargateBridgeToken.ETH] + }, + [stargateBridgeToken.sUSD]: { + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.sUSD] + }, + [stargateBridgeToken.LUSD]: { + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.LUSD], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.LUSD] + }, + [stargateBridgeToken.METIS]: { + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [stargateBridgeToken.METIS], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.METIS], + [BLOCKCHAIN_NAME.METIS]: [stargateBridgeToken.METIS] + }, + [stargateBridgeToken.mUSD]: { + [BLOCKCHAIN_NAME.METIS]: [stargateBridgeToken.mUSD] + } + }, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: { + [stargateBridgeToken.USDT]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [ + // stargateBridgeToken.BUSD, + stargateBridgeToken.USDT + ], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.FANTOM]: [stargateBridgeToken.FUSDC], + [BLOCKCHAIN_NAME.BASE]: [stargateBridgeToken.USDC] + }, + // [stargateBridgeToken.BUSD]: { + // [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + // [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + // [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + // [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + // [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.USDC], + // [BLOCKCHAIN_NAME.FANTOM]: [stargateBridgeToken.FUSDC] + // }, + [stargateBridgeToken.USDD]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.USDD] + }, + // [stargateBridgeToken.MAI]: { + // [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.MAI] + // }, + [stargateBridgeToken.METIS]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.METIS], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.METIS], + [BLOCKCHAIN_NAME.METIS]: [stargateBridgeToken.METIS] + }, + [stargateBridgeToken.mUSD]: { + [BLOCKCHAIN_NAME.METIS]: [stargateBridgeToken.mUSD] + } + }, + [BLOCKCHAIN_NAME.POLYGON]: { + [stargateBridgeToken.USDC]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [ + // stargateBridgeToken.BUSD, + stargateBridgeToken.USDT + ], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.FANTOM]: [stargateBridgeToken.FUSDC], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.BASE]: [stargateBridgeToken.USDC] + }, + [stargateBridgeToken.USDT]: { + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [ + // stargateBridgeToken.BUSD, + stargateBridgeToken.USDT + ], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.FANTOM]: [stargateBridgeToken.FUSDC], + [BLOCKCHAIN_NAME.METIS]: [stargateBridgeToken.mUSD], + [BLOCKCHAIN_NAME.BASE]: [stargateBridgeToken.USDC] + }, + [stargateBridgeToken.DAI]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.DAI], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.DAI] + } + // [stargateBridgeToken.MAI]: { + // [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.MAI] + // } + }, + [BLOCKCHAIN_NAME.AVALANCHE]: { + [stargateBridgeToken.USDC]: { + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [ + // stargateBridgeToken.BUSD, + stargateBridgeToken.USDT + ], + [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.FANTOM]: [stargateBridgeToken.FUSDC], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.BASE]: [stargateBridgeToken.USDC] + }, + [stargateBridgeToken.USDT]: { + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [ + // stargateBridgeToken.BUSD, + stargateBridgeToken.USDT + ], + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.FANTOM]: [stargateBridgeToken.FUSDC] + }, + // [stargateBridgeToken.MAI]: { + // [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.MAI] + // }, + [stargateBridgeToken.METIS]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.METIS], + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [stargateBridgeToken.METIS], + [BLOCKCHAIN_NAME.METIS]: [stargateBridgeToken.METIS] + }, + [stargateBridgeToken.mUSD]: { + [BLOCKCHAIN_NAME.METIS]: [stargateBridgeToken.mUSD] + } + }, + [BLOCKCHAIN_NAME.FANTOM]: { + [stargateBridgeToken.FUSDC]: { + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [ + // stargateBridgeToken.BUSD, + stargateBridgeToken.USDT + ], + [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.USDC, stargateBridgeToken.USDT], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.USDC] + } + }, + [BLOCKCHAIN_NAME.ARBITRUM]: { + [stargateBridgeToken.USDC]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [ + stargateBridgeToken.USDT + // stargateBridgeToken.BUSD + ], + [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.FANTOM]: [stargateBridgeToken.FUSDC], + [BLOCKCHAIN_NAME.BASE]: [stargateBridgeToken.USDC, stargateBridgeToken.ETH] + }, + [stargateBridgeToken.USDT]: { + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [ + // stargateBridgeToken.BUSD, + stargateBridgeToken.USDT + ], + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.FANTOM]: [stargateBridgeToken.FUSDC], + [BLOCKCHAIN_NAME.METIS]: [stargateBridgeToken.mUSD] + }, + [stargateBridgeToken.ETH]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.ETH], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.ETH], + [BLOCKCHAIN_NAME.BASE]: [stargateBridgeToken.ETH] + }, + [stargateBridgeToken.FRAX]: { + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.FRAX], + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.FRAX], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.FRAX] + }, + // [stargateBridgeToken.MAI]: { + // [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.MAI] + // }, + [stargateBridgeToken.LUSD]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.LUSD], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.LUSD] + } + }, + [BLOCKCHAIN_NAME.OPTIMISM]: { + [stargateBridgeToken.USDC]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [ + stargateBridgeToken.USDT + // stargateBridgeToken.BUSD + ], + [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.FANTOM]: [stargateBridgeToken.FUSDC], + [BLOCKCHAIN_NAME.BASE]: [stargateBridgeToken.USDC, stargateBridgeToken.ETH] + }, + [stargateBridgeToken.USDT]: { + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [ + // stargateBridgeToken.BUSD, + stargateBridgeToken.USDT + ], + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.USDT, stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.USDC], + [BLOCKCHAIN_NAME.FANTOM]: [stargateBridgeToken.FUSDC], + [BLOCKCHAIN_NAME.METIS]: [stargateBridgeToken.mUSD] + }, + [stargateBridgeToken.FRAX]: { + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.FRAX], + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.FRAX], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.FRAX] + }, + // [stargateBridgeToken.MAI]: { + // [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.MAI], + // [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.MAI] + // }, + [stargateBridgeToken.LUSD]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.LUSD], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.LUSD] + }, + [stargateBridgeToken.DAI]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.DAI], + [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.DAI] + }, + [stargateBridgeToken.sUSD]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.sUSD] + }, + [stargateBridgeToken.ETH]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.ETH], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.ETH], + [BLOCKCHAIN_NAME.BASE]: [stargateBridgeToken.ETH] + } + }, + [BLOCKCHAIN_NAME.METIS]: { + [stargateBridgeToken.METIS]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.METIS], + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [stargateBridgeToken.METIS], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.METIS] + }, + [stargateBridgeToken.mUSD]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.mUSD], + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [stargateBridgeToken.mUSD], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.mUSD] + } + }, + [BLOCKCHAIN_NAME.BASE]: { + [stargateBridgeToken.USDC]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.USDC, stargateBridgeToken.USDT], + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [stargateBridgeToken.USDT], + [BLOCKCHAIN_NAME.AVALANCHE]: [stargateBridgeToken.USDC, stargateBridgeToken.USDT], + [BLOCKCHAIN_NAME.POLYGON]: [stargateBridgeToken.USDC, stargateBridgeToken.USDT], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.USDC, stargateBridgeToken.USDT], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.USDC] + }, + [stargateBridgeToken.ETH]: { + [BLOCKCHAIN_NAME.ETHEREUM]: [stargateBridgeToken.ETH], + [BLOCKCHAIN_NAME.ARBITRUM]: [stargateBridgeToken.ETH], + [BLOCKCHAIN_NAME.OPTIMISM]: [stargateBridgeToken.ETH] + } + } +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pools-decimals.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pools-decimals.ts new file mode 100644 index 0000000..4842686 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pools-decimals.ts @@ -0,0 +1,25 @@ +import { + StargateBridgeToken, + stargateBridgeToken +} from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-bridge-token'; + +// Stargate close pools for BUSD and MAI +export const stargatePoolsDecimals: Record = { + [stargateBridgeToken.USDC]: 6, + [stargateBridgeToken.USDT]: 6, + [stargateBridgeToken.DAI]: 6, + // [stargateBridgeToken.MAI]: 6, + [stargateBridgeToken.FRAX]: 18, + [stargateBridgeToken.USDD]: 18, + [stargateBridgeToken.sUSD]: 6, + [stargateBridgeToken.LUSD]: 6, + // [stargateBridgeToken.BUSD]: 6, + [stargateBridgeToken.mUSD]: 6, + [stargateBridgeToken.FUSDC]: 6, + [stargateBridgeToken.METIS]: 18, + // ETHs + [stargateBridgeToken.ETH]: 18, + [stargateBridgeToken.WETH]: 18, + [stargateBridgeToken.AETH]: 18, + [stargateBridgeToken.SGETH]: 18 +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-router-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-router-abi.ts new file mode 100644 index 0000000..82c3acc --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-router-abi.ts @@ -0,0 +1,62 @@ +import { AbiItem } from 'web3-utils'; + +export const stargateRouterAbi = [ + { + inputs: [ + { internalType: 'uint16', name: '_dstChainId', type: 'uint16' }, + { internalType: 'uint8', name: '_functionType', type: 'uint8' }, + { internalType: 'bytes', name: '_toAddress', type: 'bytes' }, + { internalType: 'bytes', name: '_transferAndCallPayload', type: 'bytes' }, + { + components: [ + { internalType: 'uint256', name: 'dstGasForCall', type: 'uint256' }, + { internalType: 'uint256', name: 'dstNativeAmount', type: 'uint256' }, + { internalType: 'bytes', name: 'dstNativeAddr', type: 'bytes' } + ], + internalType: 'struct IStargateRouter.lzTxObj', + name: '_lzTxParams', + type: 'tuple' + } + ], + name: 'quoteLayerZeroFee', + outputs: [ + { internalType: 'uint256', name: '', type: 'uint256' }, + { internalType: 'uint256', name: '', type: 'uint256' } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint16', name: '_dstChainId', type: 'uint16' }, + { internalType: 'uint256', name: '_srcPoolId', type: 'uint256' }, + { internalType: 'uint256', name: '_dstPoolId', type: 'uint256' }, + { internalType: 'address payable', name: '_refundAddress', type: 'address' }, + { internalType: 'uint256', name: '_amountLD', type: 'uint256' }, + { internalType: 'uint256', name: '_minAmountLD', type: 'uint256' }, + { + components: [ + { internalType: 'uint256', name: 'dstGasForCall', type: 'uint256' }, + { internalType: 'uint256', name: 'dstNativeAmount', type: 'uint256' }, + { internalType: 'bytes', name: 'dstNativeAddr', type: 'bytes' } + ], + internalType: 'struct IStargateRouter.lzTxObj', + name: '_lzTxParams', + type: 'tuple' + }, + { internalType: 'bytes', name: '_to', type: 'bytes' }, + { internalType: 'bytes', name: '_payload', type: 'bytes' } + ], + name: 'swap', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [], + name: 'factory', + outputs: [{ internalType: 'contract Factory', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-router-eth-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-router-eth-abi.ts new file mode 100644 index 0000000..146bcfa --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-router-eth-abi.ts @@ -0,0 +1,17 @@ +import { AbiItem } from 'web3-utils'; + +export const stargateRouterEthAbi = [ + { + inputs: [ + { internalType: 'uint16', name: '_dstChainId', type: 'uint16' }, + { internalType: 'address payable', name: '_refundAddress', type: 'address' }, + { internalType: 'bytes', name: '_toAddress', type: 'bytes' }, + { internalType: 'uint256', name: '_amountLD', type: 'uint256' }, + { internalType: 'uint256', name: '_minAmountLD', type: 'uint256' } + ], + name: 'swapETH', + outputs: [], + stateMutability: 'payable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/stargate-cross-chain-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/stargate-cross-chain-provider.ts new file mode 100644 index 0000000..4453f59 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/stargate-cross-chain-provider.ts @@ -0,0 +1,582 @@ +import BigNumber from 'bignumber.js'; +import { NotSupportedTokensError, RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { parseError } from 'src/common/utils/errors'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { feeLibraryAbi } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/fee-library-abi'; +import { relayersAddresses } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/relayers-addresses'; +import { + StargateBridgeToken, + stargateBridgeToken +} from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-bridge-token'; +import { stargateFactoryAbi } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-factory-abi'; +import { stargateFeeLibraryContractAddress } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-fee-library-contract-address'; +import { stargatePoolAbi } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-abi'; +import { stargatePoolId } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-id'; +import { stargatePoolMapping } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-mapping'; +import { stargatePoolsDecimals } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pools-decimals'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +import { stargateBlockchainSupportedPools } from './constants/stargate-blockchain-supported-pool'; +import { stargateChainId } from './constants/stargate-chain-id'; +import { stargateContractAddress } from './constants/stargate-contract-address'; +import { + StargateCrossChainSupportedBlockchain, + stargateCrossChainSupportedBlockchains +} from './constants/stargate-cross-chain-supported-blockchain'; +import { stargateRouterAbi } from './constants/stargate-router-abi'; +import { StargateCrossChainTrade } from './stargate-cross-chain-trade'; + +export class StargateCrossChainProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.STARGATE; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is StargateCrossChainSupportedBlockchain { + return stargateCrossChainSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + private static hasDirectRoute( + from: PriceTokenAmount, + to: PriceToken + ): boolean { + const fromBlockchain = from.blockchain as StargateCrossChainSupportedBlockchain; + const toBlockchain = to.blockchain as StargateCrossChainSupportedBlockchain; + const swapToMetisBlockchain = toBlockchain === BLOCKCHAIN_NAME.METIS; + const swapFromMetisBlockchain = fromBlockchain === BLOCKCHAIN_NAME.METIS; + const fromSymbol = StargateCrossChainProvider.getSymbol( + from.symbol, + fromBlockchain, + swapToMetisBlockchain + ); + const toSymbol = StargateCrossChainProvider.getSymbol( + to.symbol, + toBlockchain, + swapFromMetisBlockchain + ); + + const srcPoolId = stargatePoolId[fromSymbol as StargateBridgeToken]; + const srcSupportedPools = stargateBlockchainSupportedPools[fromBlockchain]; + if (!srcPoolId || !srcSupportedPools.includes(srcPoolId)) { + return false; + } + + const dstPoolId = stargatePoolId[toSymbol as StargateBridgeToken]; + + if (srcPoolId === dstPoolId && srcPoolId === 13) { + return true; + } + const dstSupportedPools = stargateBlockchainSupportedPools[toBlockchain]; + if (!dstSupportedPools.includes(dstPoolId)) { + throw new RubicSdkError('Tokens are not supported.'); + } + + const poolPathExists = stargatePoolMapping[fromBlockchain]?.[ + fromSymbol as StargateBridgeToken + ]?.[toBlockchain]?.includes(toSymbol as StargateBridgeToken); + + return Boolean(poolPathExists); + } + + // eslint-disable-next-line complexity + public async calculate( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + const from = new PriceTokenAmount({ + ...fromToken.asStruct, + tokenAmount: fromToken.tokenAmount, + address: + fromToken.isNative && fromToken.blockchain === BLOCKCHAIN_NAME.METIS + ? '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000' + : fromToken.address + }); + + try { + const fromBlockchain = from.blockchain as StargateCrossChainSupportedBlockchain; + const toBlockchain = toToken.blockchain as StargateCrossChainSupportedBlockchain; + const useProxy = options?.useProxy?.[this.type] ?? true; + + if (this.shouldWeStopCalculatingWithMetisToken(fromToken, toToken)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + if (!this.areSupportedBlockchains(fromBlockchain, toBlockchain)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + const wrongFantomUsdc = '0x04068DA6C83AFCFA0e13ba15A6696662335D5B75'; + if ( + compareAddresses(from.address, wrongFantomUsdc) || + compareAddresses(toToken.address, wrongFantomUsdc) + ) { + throw new RubicSdkError('Trade to this tokens is not allowed'); + } + + const hasDirectRoute = StargateCrossChainProvider.hasDirectRoute(from, toToken); + + const feeInfo = await this.getFeeInfo( + fromBlockchain, + options.providerAddress, + from, + useProxy + ); + const fromWithoutFee = getFromWithoutFee( + from, + feeInfo.rubicProxy?.platformFee?.percent + ); + + let transitTokenAmount = fromWithoutFee; + let srcChainTrade: EvmOnChainTrade | null = null; + let transitAmount: BigNumber = fromWithoutFee.tokenAmount; + + if (!hasDirectRoute || (useProxy && hasDirectRoute && from.isNative)) { + if (!useProxy) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + const transitToken = await this.getTransitToken(hasDirectRoute, from, toToken); + + if ( + fromToken.isNative && + !transitToken.isWrapped && + fromBlockchain !== BLOCKCHAIN_NAME.METIS + ) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + const trade = await ProxyCrossChainEvmTrade.getOnChainTrade( + fromWithoutFee, + transitToken, + options.slippageTolerance / 2, + true + ); + if (!trade) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + srcChainTrade = trade; + transitTokenAmount = srcChainTrade.to; + transitAmount = srcChainTrade.toTokenAmountMin.tokenAmount; + } + + const poolFee = await this.fetchPoolFees(transitTokenAmount, toToken, transitAmount); + const amountOutMin = transitAmount.minus(poolFee); + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: amountOutMin + }); + + const swapInDestination = false; + const dstChainTrade = swapInDestination + ? await this.getDstSwap(to, amountOutMin) + : null; + const dstSwapData = swapInDestination + ? ( + await dstChainTrade!.encodeDirect({ + supportFee: false, + fromAddress: options?.fromAddress || EvmWeb3Pure.EMPTY_ADDRESS + }) + ).data + : undefined; + + const layerZeroFeeWei = await this.getLayerZeroFee(transitTokenAmount, to, dstSwapData); + const layerZeroFeeAmount = Web3Pure.fromWei( + layerZeroFeeWei, + nativeTokensList[fromBlockchain].decimals + ); + const nativeToken = await PriceToken.createFromToken(nativeTokensList[from.blockchain]); + feeInfo.provider = { + cryptoFee: { + amount: layerZeroFeeAmount, + token: nativeToken + } + }; + + const gasData = + options.gasCalculation === 'enabled' + ? await StargateCrossChainTrade.getGasData( + from, + to, + feeInfo, + srcChainTrade, + dstChainTrade, + options.slippageTolerance, + options.providerAddress, + options.receiverAddress + ) + : null; + + return { + trade: new StargateCrossChainTrade( + { + from, + to, + slippageTolerance: options.slippageTolerance, + priceImpact: transitTokenAmount.calculatePriceImpactPercent(to), + gasData, + feeInfo, + srcChainTrade, + dstChainTrade, + cryptoFeeToken: nativeToken + }, + options.providerAddress, + await this.getRoutePath(from, to, srcChainTrade) + ), + tradeType: this.type + }; + } catch (error) { + console.error({ 'CALCULATE ERROR': error }); + return { + trade: null, + error: parseError(error), + tradeType: this.type + }; + } + } + + private async getLayerZeroFee( + from: PriceTokenAmount, + to: PriceTokenAmount, + dstSwapData?: string + ): Promise { + const fromBlockchain = from.blockchain as StargateCrossChainSupportedBlockchain; + const toBlockchain = to.blockchain as StargateCrossChainSupportedBlockchain; + const layerZeroTxData = await StargateCrossChainTrade.getLayerZeroSwapData( + from, + to, + to.stringWeiAmount, + undefined, + dstSwapData + ); + const web3Public = Injector.web3PublicService.getWeb3Public(from.blockchain); + const walletAddress = Injector.web3PrivateService.getWeb3Private(CHAIN_TYPE.EVM).address; + const dstConfig = dstSwapData + ? ['750000', '0', relayersAddresses[toBlockchain]] + : ['0', '0', walletAddress || EvmWeb3Pure.EMPTY_ADDRESS]; + const layerZeroFee = await web3Public.callContractMethod( + stargateContractAddress[fromBlockchain], + stargateRouterAbi, + 'quoteLayerZeroFee', + [ + stargateChainId[toBlockchain], + 1, + walletAddress || EvmWeb3Pure.EMPTY_ADDRESS, + layerZeroTxData.data, + dstConfig + ] + ); + return new BigNumber(`${layerZeroFee['0']!}`); + } + + protected async getFeeInfo( + fromBlockchain: Partial, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } + + private async fetchPoolFees( + fromToken: PriceTokenAmount, + toToken: PriceToken, + transitAmount: BigNumber + ): Promise { + const fromBlockchain = fromToken.blockchain as StargateCrossChainSupportedBlockchain; + const toBlockchain = toToken.blockchain as StargateCrossChainSupportedBlockchain; + const swapToMetisBlockchain = toBlockchain === BLOCKCHAIN_NAME.METIS; + const swapFromMetisBlockchain = fromBlockchain === BLOCKCHAIN_NAME.METIS; + + const fromSymbol = StargateCrossChainProvider.getSymbol( + fromToken.symbol, + fromBlockchain, + swapToMetisBlockchain + ); + const toSymbol = StargateCrossChainProvider.getSymbol( + toToken.symbol, + toBlockchain, + swapFromMetisBlockchain + ); + + let srcPoolId = stargatePoolId[fromSymbol as StargateBridgeToken]; + let dstPoolId = stargatePoolId[toSymbol as StargateBridgeToken]; + const dstChainId = stargateChainId[toBlockchain as StargateCrossChainSupportedBlockchain]; + + const sdDecimals = stargatePoolsDecimals[fromSymbol as StargateBridgeToken]; + const amountSD = Web3Pure.toWei(transitAmount, sdDecimals); + + // @TODO FIX STARGATE MULTIPLE POOLS + if ( + dstPoolId === stargatePoolId[stargateBridgeToken.mUSD] && + srcPoolId === stargatePoolId[stargateBridgeToken.USDT] + ) { + srcPoolId = stargatePoolId[stargateBridgeToken.mUSD]; + } + if ( + srcPoolId === stargatePoolId[stargateBridgeToken.mUSD] && + dstPoolId === stargatePoolId[stargateBridgeToken.USDT] + ) { + dstPoolId = stargatePoolId[stargateBridgeToken.mUSD]; + } + + try { + const { + 1: eqFee, + 2: eqReward, + 4: protocolFee + } = await Injector.web3PublicService + .getWeb3Public(fromBlockchain) + .callContractMethod<{ 1: string; 2: string; 4: string }>( + stargateFeeLibraryContractAddress[fromBlockchain], + feeLibraryAbi, + 'getFees', + [srcPoolId, dstPoolId, dstChainId, EvmWeb3Pure.EMPTY_ADDRESS, amountSD] + ); + + return Web3Pure.fromWei( + new BigNumber(eqFee).plus(protocolFee).minus(eqReward), + sdDecimals + ); + } catch (err) { + if (err instanceof Error) { + throw new RubicSdkError('Tokens are not supported.'); + } + throw new RubicSdkError('Unknown error.'); + } + } + + private async getPoolToken( + poolId: number, + fromBlockchain: EvmBlockchainName + ): Promise { + const web3Adapter = Injector.web3PublicService.getWeb3Public(fromBlockchain); + + const factoryAddress = await web3Adapter.callContractMethod( + stargateContractAddress[fromBlockchain as StargateCrossChainSupportedBlockchain], + stargateRouterAbi, + 'factory', + [] + ); + + const poolAddress = await web3Adapter.callContractMethod( + factoryAddress, + stargateFactoryAbi, + 'getPool', + [poolId] + ); + + if (compareAddresses(poolAddress, EvmWeb3Pure.EMPTY_ADDRESS)) { + throw new RubicSdkError('No possible pool'); + } + + const tokenAddress = await web3Adapter.callContractMethod( + poolAddress, + stargatePoolAbi, + 'token', + [] + ); + + return PriceToken.createToken({ + address: tokenAddress, + blockchain: fromBlockchain + }); + } + + private async getTransitToken( + hasDirectRoute: boolean, + fromToken: PriceTokenAmount, + toToken: PriceToken + ) { + if (hasDirectRoute && !fromToken.isNative) { + return fromToken; + } + + const toBlockchain = toToken.blockchain as StargateCrossChainSupportedBlockchain; + const toBlockchainDirection = stargatePoolMapping[toBlockchain]; + const swapFromMetisBlockchain = fromToken.blockchain === BLOCKCHAIN_NAME.METIS; + + if (!toBlockchainDirection) { + throw new RubicSdkError('Tokens are not supported.'); + } + + const toSymbol = StargateCrossChainProvider.getSymbol( + toToken.symbol, + toBlockchain, + swapFromMetisBlockchain + ) as StargateBridgeToken; + + const toSymbolDirection = toBlockchainDirection[toSymbol]; + if (!toSymbolDirection) { + throw new RubicSdkError('Tokens are not supported.'); + } + + const fromBlockchain = fromToken.blockchain as StargateCrossChainSupportedBlockchain; + const fromBlockchainDirection = toSymbolDirection[fromBlockchain]; + if (!fromBlockchainDirection) { + throw new RubicSdkError('Tokens are not supported.'); + } + + const possibleTransitSymbol: StargateBridgeToken | undefined = Object.values( + stargateBridgeToken + ).find(symbol => symbol === toSymbol); + if (!possibleTransitSymbol) { + throw new RubicSdkError('Tokens are not supported.'); + } + + try { + const poolToken = await this.getPoolToken( + stargatePoolId[possibleTransitSymbol as unknown as StargateBridgeToken], + fromBlockchain + ); + return poolToken; + } catch { + const poolToken = await this.getPoolToken( + stargatePoolId[fromBlockchainDirection[0] as unknown as StargateBridgeToken], + fromBlockchain + ); + return poolToken; + } + } + + private async getDstSwap( + fromToken: PriceTokenAmount, + tokenAmount: BigNumber + ): Promise { + return ProxyCrossChainEvmTrade.getOnChainTrade( + new PriceTokenAmount({ + ...fromToken.asStruct, + tokenAmount + }), + { + address: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', + blockchain: BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN + }, + 0.1 + ); + } + + public static getSymbol( + symbol: string, + blockchain: BlockchainName, + swapWithMetisBlockchain?: boolean + ): string { + if (blockchain === BLOCKCHAIN_NAME.ARBITRUM && symbol === 'AETH') { + return 'ETH'; + } + + if ( + swapWithMetisBlockchain && + (blockchain === BLOCKCHAIN_NAME.AVALANCHE || + blockchain === BLOCKCHAIN_NAME.ETHEREUM || + blockchain === BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN) && + symbol.toLowerCase() === 'usdt' + ) { + return 'm.USDT'; + } + + if (blockchain === BLOCKCHAIN_NAME.AVALANCHE && symbol === 'USDt') { + return 'USDT'; + } + if (blockchain === BLOCKCHAIN_NAME.FANTOM && symbol === 'USDC') { + return 'FUSDC'; + } + if (symbol.toUpperCase() === 'METIS') { + return symbol.toUpperCase(); + } + return symbol; + } + + protected async getRoutePath( + from: PriceTokenAmount, + to: PriceTokenAmount, + srcOnChainTrade: EvmOnChainTrade | null + ): Promise { + if (srcOnChainTrade) { + return [ + { + type: 'on-chain', + provider: srcOnChainTrade.type, + path: [srcOnChainTrade.from, srcOnChainTrade.to] + }, + { + type: 'cross-chain', + provider: CROSS_CHAIN_TRADE_TYPE.STARGATE, + path: [srcOnChainTrade.to, to] + } + ]; + } + + return [ + { + type: 'cross-chain', + provider: CROSS_CHAIN_TRADE_TYPE.STARGATE, + path: [from, to] + } + ]; + } + + // Не считаем трейды из Metis (metis/m.usdt) в Avalanche (metis) и в BNB chain (metis) и обратно + private shouldWeStopCalculatingWithMetisToken( + fromToken: PriceTokenAmount, + toToken: PriceToken + ): boolean { + return ( + (fromToken.blockchain === BLOCKCHAIN_NAME.METIS && + fromToken.symbol.toLowerCase() !== 'metis' && + toToken.symbol.toLowerCase() === 'metis') || + (fromToken.blockchain === BLOCKCHAIN_NAME.METIS && + (toToken.blockchain === BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN || + toToken.blockchain === BLOCKCHAIN_NAME.AVALANCHE) && + toToken.symbol.toLowerCase() === 'metis') || + (toToken.blockchain === BLOCKCHAIN_NAME.METIS && + (fromToken.blockchain === BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN || + fromToken.blockchain === BLOCKCHAIN_NAME.AVALANCHE) && + fromToken.symbol.toLowerCase() === 'metis') + ); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/stargate-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/stargate-cross-chain-trade.ts new file mode 100644 index 0000000..da00684 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/stargate-provider/stargate-cross-chain-trade.ts @@ -0,0 +1,439 @@ +import BigNumber from 'bignumber.js'; +import { BytesLike } from 'ethers'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { checkUnsupportedReceiverAddress } from 'src/features/common/utils/check-unsupported-receiver-address'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { gatewayRubicCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { OnChainSubtype } from 'src/features/cross-chain/calculation-manager/providers/common/models/on-chain-subtype'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { relayersAddresses } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/relayers-addresses'; +import { + StargateBridgeToken, + stargateBridgeToken +} from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-bridge-token'; +import { stargatePoolId } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pool-id'; +import { stargatePoolsDecimals } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/constants/stargate-pools-decimals'; +import { StargateCrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/stargate-provider/stargate-cross-chain-provider'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +import { evmCommonCrossChainAbi } from '../common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { stargateChainId } from './constants/stargate-chain-id'; +import { + stargateContractAddress, + stargateEthContractAddress +} from './constants/stargate-contract-address'; +import { StargateCrossChainSupportedBlockchain } from './constants/stargate-cross-chain-supported-blockchain'; +import { stargateRouterAbi } from './constants/stargate-router-abi'; +import { stargateRouterEthAbi } from './constants/stargate-router-eth-abi'; + +export class StargateCrossChainTrade extends EvmCrossChainTrade { + protected get methodName(): string { + return this.onChainTrade + ? 'swapAndStartBridgeTokensViaStargate' + : 'startBridgeTokensViaStargate'; + } + + /** @internal */ + public static async getGasData( + from: PriceTokenAmount, + toToken: PriceTokenAmount, + feeInfo: FeeInfo, + srcChainTrade: EvmOnChainTrade | null, + dstChainTrade: EvmOnChainTrade | null, + slippageTolerance: number, + providerAddress: string, + receiverAddress?: string + ): Promise { + try { + const trade = new StargateCrossChainTrade( + { + from, + to: toToken, + slippageTolerance, + priceImpact: null, + gasData: null, + feeInfo, + srcChainTrade, + dstChainTrade, + cryptoFeeToken: null + }, + providerAddress || EvmWeb3Pure.EMPTY_ADDRESS, + [] + ); + + return getCrossChainGasData(trade, receiverAddress); + } catch (_err) { + return null; + } + } + + public readonly feeInfo: FeeInfo; + + public readonly type = CROSS_CHAIN_TRADE_TYPE.STARGATE; + + public readonly isAggregator = false; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly slippageTolerance: number; + + public readonly gasData: GasData; + + public readonly priceImpact: number | null; + + public readonly toTokenAmountMin: BigNumber; + + public readonly onChainSubtype: OnChainSubtype; + + public readonly bridgeType = BRIDGE_TYPE.STARGATE; + + public get fromBlockchain(): StargateCrossChainSupportedBlockchain { + return this.from.blockchain as StargateCrossChainSupportedBlockchain; + } + + protected get fromContractAddress(): string { + return this.isProxyTrade + ? rubicProxyContractAddress[this.fromBlockchain].gateway + : stargateContractAddress[this.fromBlockchain]; + } + + private readonly onChainTrade: EvmOnChainTrade | null; + + private readonly dstChainTrade: EvmOnChainTrade | null; + + private readonly cryptoFeeToken: PriceToken | null; + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + slippageTolerance: number; + priceImpact: number | null; + gasData: GasData | null; + feeInfo: FeeInfo; + srcChainTrade: EvmOnChainTrade | null; + dstChainTrade: EvmOnChainTrade | null; + cryptoFeeToken: PriceToken | null; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.slippageTolerance = crossChainTrade.slippageTolerance; + this.priceImpact = crossChainTrade.priceImpact; + this.gasData = crossChainTrade.gasData; + this.feeInfo = crossChainTrade.feeInfo; + this.onChainTrade = crossChainTrade.srcChainTrade; + this.dstChainTrade = crossChainTrade.dstChainTrade; + this.toTokenAmountMin = this.to.tokenAmount.multipliedBy( + 1 - + (crossChainTrade.srcChainTrade + ? this.slippageTolerance / 2 + : this.slippageTolerance) + ); + this.onChainSubtype = { + from: this.onChainTrade?.type, + to: this.dstChainTrade?.type + }; + this.cryptoFeeToken = crossChainTrade.cryptoFeeToken; + } + + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + this.checkWalletConnected(); + checkUnsupportedReceiverAddress(options?.receiverAddress, this.walletAddress); + await this.checkTradeErrors(); + await this.checkAllowanceAndApprove(options); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + // eslint-disable-next-line no-useless-catch + try { + const { data, to } = await StargateCrossChainTrade.getLayerZeroSwapData( + this.from, + this.to, + Web3Pure.toWei(this.toTokenAmountMin, this.to.decimals), + options?.receiverAddress + ); + + const lzFeeWei = Web3Pure.toWei( + this.feeInfo.provider!.cryptoFee!.amount, + nativeTokensList[this.from.blockchain].decimals + ); + + const value = this.from.isNative + ? this.from.weiAmount.plus(lzFeeWei).toFixed() + : lzFeeWei; + + await this.web3Private.trySendTransaction(to, { + data, + value, + onTransactionHash, + gas: gasLimit, + gasPriceOptions + }); + + return transactionHash!; + } catch (err) { + throw err; + } + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + const fromToken = ( + this.onChainTrade ? this.onChainTrade.toTokenAmountMin : this.from + ) as PriceTokenAmount; + + const config = await StargateCrossChainTrade.getLayerZeroSwapData( + fromToken, + this.to, + Web3Pure.toWei(this.toTokenAmountMin, this.to.decimals), + receiverAddress + ); + + return { config, amount: this.to.stringWeiAmount }; + } + + public static async getLayerZeroSwapData( + from: PriceTokenAmount, + to: PriceTokenAmount, + tokenAmountMin: string = to.stringWeiAmount, + receiverAddress?: string, + dstData?: string + ): Promise { + const walletAddress = + Injector.web3PrivateService.getWeb3Private(CHAIN_TYPE.EVM).address || + EvmWeb3Pure.EMPTY_ADDRESS; + const fromBlockchain = from.blockchain as StargateCrossChainSupportedBlockchain; + const toBlockchain = to.blockchain as StargateCrossChainSupportedBlockchain; + const dstRelayer = relayersAddresses[toBlockchain]; + const destinationAddress = dstData ? dstRelayer : receiverAddress || walletAddress; + const isEthTrade = from.isNative && to.isNative; + const stargateRouterAddress = isEthTrade + ? stargateEthContractAddress[fromBlockchain]! + : stargateContractAddress[fromBlockchain]; + const dstChainId = stargateChainId[toBlockchain]; + const swapToMetisBlockchain = toBlockchain === BLOCKCHAIN_NAME.METIS; + const swapFromMetisBlockchain = fromBlockchain === BLOCKCHAIN_NAME.METIS; + + const fromSymbol = StargateCrossChainProvider.getSymbol( + from.symbol, + fromBlockchain, + swapToMetisBlockchain + ); + const toSymbol = StargateCrossChainProvider.getSymbol( + to.symbol, + toBlockchain, + swapFromMetisBlockchain + ); + + let srcPoolId = stargatePoolId[fromSymbol as StargateBridgeToken]; + let dstPoolId = stargatePoolId[toSymbol as StargateBridgeToken]; + + // @TODO FIX STARGATE MULTIPLE POOLS + if ( + dstPoolId === stargatePoolId[stargateBridgeToken.mUSD] && + srcPoolId === stargatePoolId[stargateBridgeToken.USDT] + ) { + srcPoolId = stargatePoolId[stargateBridgeToken.mUSD]; + } + if ( + srcPoolId === stargatePoolId[stargateBridgeToken.mUSD] && + dstPoolId === stargatePoolId[stargateBridgeToken.USDT] + ) { + dstPoolId = stargatePoolId[stargateBridgeToken.mUSD]; + } + + const dstConfig = dstData + ? ['750000', '0', relayersAddresses[toBlockchain]] + : ['0', '0', walletAddress]; + + const methodArguments = isEthTrade + ? [dstChainId, walletAddress, walletAddress, from.stringWeiAmount, tokenAmountMin] + : [ + dstChainId, + srcPoolId, + dstPoolId, + walletAddress, + from.stringWeiAmount, + tokenAmountMin, + dstConfig, + destinationAddress, + dstData || '0x' + ]; + const methodName = isEthTrade ? 'swapETH' : 'swap'; + const abi = isEthTrade ? stargateRouterEthAbi : stargateRouterAbi; + return EvmWeb3Pure.encodeMethodCall( + stargateRouterAddress, + abi, + methodName, + methodArguments + ); + } + + public async getContractParams(options: GetContractParamsOptions): Promise { + const { data } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress + ); + + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData(options, { + walletAddress: this.walletAddress, + fromTokenAmount: this.from, + toTokenAmount: this.to, + srcChainTrade: this.onChainTrade, + providerAddress: this.providerAddress, + type: `native:${this.type}`, + fromAddress: this.walletAddress, + dstChainTrade: this.dstChainTrade || undefined + }); + const swapData = + this.onChainTrade && + (await ProxyCrossChainEvmTrade.getSwapData(options, { + walletAddress: this.walletAddress, + contractAddress: rubicProxyContractAddress[this.from.blockchain].router, + fromTokenAmount: this.from, + toTokenAmount: this.onChainTrade.to, + onChainEncodeFn: this.onChainTrade.encode.bind(this.onChainTrade) + })); + + const dstSwapConfiguration: string | undefined = undefined; + // Uncomment when dst swap is ready + // if (dstSwapData) { + // const txId = bridgeData[0]; + // const reveivedToken = '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d'; + // + // dstSwapConfiguration = EvmWeb3Pure.encodeParameters( + // ['bytes32', 'bytes', 'address', 'address'], + // [txId, dstSwapData, reveivedToken, options.receiverAddress || this.walletAddress] + // ); + // } + + const providerData = this.getProviderData( + data, + dstSwapConfiguration, + options.receiverAddress + ); + + const methodArguments = swapData + ? [bridgeData, swapData, providerData] + : [bridgeData, providerData]; + + const lzWeiFee = Web3Pure.toWei( + this.feeInfo.provider!.cryptoFee!.amount, + nativeTokensList[this.from.blockchain].decimals + ); + const totalValue = this.from.isNative + ? this.from.weiAmount.plus(lzWeiFee).toFixed() + : lzWeiFee; + const value = this.getSwapValue(totalValue); + + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + const usdCryptoFee = this.cryptoFeeToken?.price.multipliedBy( + this.feeInfo.provider?.cryptoFee?.amount || 0 + ); + if (usdCryptoFee && usdCryptoFee.gt(0)) { + return fromUsd + .plus(usdCryptoFee.isNaN() ? 0 : usdCryptoFee) + .dividedBy(this.to.tokenAmount); + } + + return fromUsd.dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact ?? null, + slippage: this.slippageTolerance * 100, + routePath: this.routePath + }; + } + + private getProviderData( + _sourceData: BytesLike, + dstSwapData?: string, + receiverAddress?: string + ): unknown[] { + const swapFromMetisBlockchain = this.fromBlockchain === BLOCKCHAIN_NAME.METIS; + + const toSymbol = StargateCrossChainProvider.getSymbol( + this.to.symbol, + this.to.blockchain, + swapFromMetisBlockchain + ); + const pool = stargatePoolId[toSymbol as StargateBridgeToken]; + const targetPoolDecimals = + stargatePoolsDecimals[this.to.symbol as StargateBridgeToken] || + stargatePoolsDecimals[toSymbol as StargateBridgeToken]; + const amount = Web3Pure.toWei(this.toTokenAmountMin, targetPoolDecimals); + const fee = Web3Pure.toWei( + this.feeInfo.provider!.cryptoFee!.amount, + nativeTokensList[this.from.blockchain].decimals + ); + const destinationAddress = receiverAddress || this.walletAddress; + + return [ + pool, + amount, + dstSwapData ? '750000' : '0', + fee, + this.walletAddress, + dstSwapData + ? relayersAddresses[this.to.blockchain as StargateCrossChainSupportedBlockchain] + : destinationAddress, + dstSwapData || '0x' + ]; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/constants/mete-router-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/constants/mete-router-abi.ts new file mode 100644 index 0000000..63cc4d9 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/constants/mete-router-abi.ts @@ -0,0 +1,28 @@ +import { AbiItem } from 'web3-utils'; + +export const meteRouterAbi: AbiItem[] = [ + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'firstSwapCalldata', type: 'bytes' }, + { internalType: 'bytes', name: 'secondSwapCalldata', type: 'bytes' }, + { internalType: 'address[]', name: 'approvedTokens', type: 'address[]' }, + { internalType: 'address', name: 'firstDexRouter', type: 'address' }, + { internalType: 'address', name: 'secondDexRouter', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'bool', name: 'nativeIn', type: 'bool' }, + { internalType: 'address', name: 'relayRecipient', type: 'address' }, + { internalType: 'bytes', name: 'otherSideCalldata', type: 'bytes' } + ], + internalType: 'struct MetaRouteStructs.MetaRouteTransaction', + name: '_metarouteTransaction', + type: 'tuple' + } + ], + name: 'metaRoute', + outputs: [], + stateMutability: 'payable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-call-data-decode.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-call-data-decode.ts new file mode 100644 index 0000000..486d70f --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-call-data-decode.ts @@ -0,0 +1,4 @@ +export interface SymbiosisCallDataDecode { + relayRecipient: string; + otherSideCalldata: string; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-cross-chain-supported-blockchains.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-cross-chain-supported-blockchains.ts new file mode 100644 index 0000000..f91322d --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-cross-chain-supported-blockchains.ts @@ -0,0 +1,40 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const symbiosisCrossChainSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.BOBA, + BLOCKCHAIN_NAME.BOBA_BSC, + BLOCKCHAIN_NAME.TELOS, + BLOCKCHAIN_NAME.ZK_SYNC, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.POLYGON_ZKEVM, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.MANTLE, + BLOCKCHAIN_NAME.TRON, + BLOCKCHAIN_NAME.SCROLL, + BLOCKCHAIN_NAME.METIS, + BLOCKCHAIN_NAME.BITCOIN, + BLOCKCHAIN_NAME.BLAST, + BLOCKCHAIN_NAME.MERLIN, + BLOCKCHAIN_NAME.ROOTSTOCK, + BLOCKCHAIN_NAME.MODE, + BLOCKCHAIN_NAME.ZK_LINK, + BLOCKCHAIN_NAME.SEI, + BLOCKCHAIN_NAME.TAIKO, + // Testnets + BLOCKCHAIN_NAME.GOERLI, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN_TESTNET, + BLOCKCHAIN_NAME.FUJI, + BLOCKCHAIN_NAME.MUMBAI, + BLOCKCHAIN_NAME.SCROLL_SEPOLIA, + BLOCKCHAIN_NAME.ZETACHAIN, + BLOCKCHAIN_NAME.MANTA_PACIFIC +] as const; + +export type SymbiosisCrossChainSupportedBlockchain = + (typeof symbiosisCrossChainSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-error.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-error.ts new file mode 100644 index 0000000..2e7e284 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-error.ts @@ -0,0 +1,6 @@ +export interface SymbiosisError { + error: { + code: number; + message?: string; + }; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-swap-status.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-swap-status.ts new file mode 100644 index 0000000..c970f79 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-swap-status.ts @@ -0,0 +1,10 @@ +export const SYMBIOSIS_SWAP_STATUS = { + STUCKED: 'Stucked', + REVERTED: 'Reverted', + SUCCESS: 'Success', + PENDING: 'Pending', + NOT_FOUND: 'Not found' +} as const; + +export type SymbiosisSwapStatus = + (typeof SYMBIOSIS_SWAP_STATUS)[keyof typeof SYMBIOSIS_SWAP_STATUS]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-swapping-params.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-swapping-params.ts new file mode 100644 index 0000000..a1853d3 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-swapping-params.ts @@ -0,0 +1,14 @@ +import { + SymbiosisToken, + SymbiosisTokenAmount +} from 'src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-trade-data'; + +export type SymbiosisSwappingParams = { + tokenAmountIn: SymbiosisTokenAmount; + tokenOut: SymbiosisToken; + from: string; + to: string; + revertableAddress: string; + slippage: number; + deadline: number; +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-trade-data.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-trade-data.ts new file mode 100644 index 0000000..4a6d13d --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-trade-data.ts @@ -0,0 +1,29 @@ +import { TransactionRequest } from '@ethersproject/abstract-provider'; + +export type SymbiosisTradeType = 'dex' | '1inch' | 'open-ocean' | 'wrap' | 'izumi'; + +export interface SymbiosisToken { + chainId: number; + decimals: number; + address: string; + isNative: boolean; + symbol?: string; + name?: string; +} + +export interface SymbiosisTokenAmount extends SymbiosisToken { + amount: string; +} + +export interface SymbiosisTradeData { + fee: SymbiosisTokenAmount; + priceImpact: string; + tokenAmountOut: SymbiosisTokenAmount; + tx: TransactionRequest; + amountInUsd: SymbiosisTokenAmount; + approveTo: string; + route: SymbiosisToken[]; + inTradeType: SymbiosisTradeType; + outTradeType: SymbiosisTradeType; + rewards: SymbiosisTokenAmount[]; +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/symbiosis-cross-chain-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/symbiosis-cross-chain-provider.ts new file mode 100644 index 0000000..88d2c99 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/symbiosis-cross-chain-provider.ts @@ -0,0 +1,373 @@ +import BigNumber from 'bignumber.js'; +import { MinAmountError, NotSupportedTokensError, RubicSdkError } from 'src/common/errors'; +import { NoLinkedAccountError } from 'src/common/errors/swap/no-linked-account-erros'; +import { PriceToken, PriceTokenAmount, TokenAmount as RubicTokenAmount } from 'src/common/tokens'; +import { TokenStruct } from 'src/common/tokens/token'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Web3PrivateSupportedBlockchain } from 'src/core/blockchain/web3-private-service/models/web-private-supported-blockchain'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { SymbiosisApiService } from 'src/features/common/providers/symbiosis/services/symbiosis-api-service'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { SymbiosisError } from 'src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-error'; +import { SymbiosisSwappingParams } from 'src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-swapping-params'; +import { + SymbiosisToken, + SymbiosisTokenAmount +} from 'src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-trade-data'; +import { SymbiosisCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/symbiosis-provider/symbiosis-cross-chain-trade'; +import { oneinchApiParams } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/constants'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +import { + SymbiosisCrossChainSupportedBlockchain, + symbiosisCrossChainSupportedBlockchains +} from './models/symbiosis-cross-chain-supported-blockchains'; + +export class SymbiosisCrossChainProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.SYMBIOSIS; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is SymbiosisCrossChainSupportedBlockchain { + return symbiosisCrossChainSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public override areSupportedBlockchains( + fromBlockchain: BlockchainName, + toBlockchain: BlockchainName + ): boolean { + if (fromBlockchain === BLOCKCHAIN_NAME.BITCOIN) { + return false; + } + + return super.areSupportedBlockchains(fromBlockchain, toBlockchain); + } + + // eslint-disable-next-line complexity + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + const fromBlockchain = from.blockchain as SymbiosisCrossChainSupportedBlockchain; + const toBlockchain = toToken.blockchain as SymbiosisCrossChainSupportedBlockchain; + // @TODO remove after gas fix for metis + const useProxy = + from.blockchain === BLOCKCHAIN_NAME.METIS + ? false + : options?.useProxy?.[this.type] ?? true; + + // @TODO remove Tron check + if ( + !this.areSupportedBlockchains(fromBlockchain, toBlockchain) || + fromBlockchain === BLOCKCHAIN_NAME.TRON || + fromBlockchain === BLOCKCHAIN_NAME.BITCOIN + ) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + let disabledTrade = {} as SymbiosisCrossChainTrade; + + try { + const FAKE_WALLET_ADDRESS = '0xf78312D6aD7afc364422Dda14a24082104588542'; + const fromAddress = + options.fromAddress || + this.getWalletAddress(fromBlockchain as Web3PrivateSupportedBlockchain) || + FAKE_WALLET_ADDRESS || + oneinchApiParams.nativeAddress; + + const feeInfo = await this.getFeeInfo( + fromBlockchain, + options.providerAddress, + from, + useProxy + ); + const fromWithoutFee = getFromWithoutFee( + from, + feeInfo.rubicProxy?.platformFee?.percent + ); + + let tokenInAddress; + + if (from.isNative && from.blockchain === BLOCKCHAIN_NAME.METIS) { + tokenInAddress = '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000'; + } else if (from.isNative) { + tokenInAddress = ''; + } else { + tokenInAddress = from.address; + } + + const tokenIn: SymbiosisToken = { + chainId: blockchainId[fromBlockchain], + address: tokenInAddress, + decimals: from.decimals, + isNative: from.isNative, + symbol: from.symbol + }; + + const tokenOut: SymbiosisToken = { + chainId: + toBlockchain !== BLOCKCHAIN_NAME.TRON ? blockchainId[toBlockchain] : 728126428, + address: toToken.isNative ? '' : toToken.address, + decimals: toToken.decimals, + isNative: toToken.isNative, + symbol: toToken.symbol + }; + + const symbiosisTokenAmountIn: SymbiosisTokenAmount = { + ...tokenIn, + amount: fromWithoutFee.stringWeiAmount + }; + + const deadline = Math.floor(Date.now() / 1000) + 60 * options.deadline; + const slippageTolerance = options.slippageTolerance * 10000; + + const swapParams: SymbiosisSwappingParams = { + tokenAmountIn: symbiosisTokenAmountIn, + tokenOut, + from: fromAddress, + to: this.getSwapParamsToAddress(options.receiverAddress, fromAddress, toBlockchain), + revertableAddress: fromAddress, + slippage: slippageTolerance, + deadline + }; + + const mockTo = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: Web3Pure.fromWei(0, toToken.decimals) + }); + + disabledTrade = this.getEmptyTrade(from, mockTo, swapParams, feeInfo); + + const { rewards, tokenAmountOut, inTradeType, outTradeType, tx, approveTo, route } = + await SymbiosisApiService.getCrossChainSwapTx(swapParams); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: Web3Pure.fromWei(tokenAmountOut.amount, tokenAmountOut.decimals) + }); + + const gasData = + options.gasCalculation === 'enabled' + ? await SymbiosisCrossChainTrade.getGasData( + from, + to, + swapParams, + feeInfo, + approveTo, + options.providerAddress, + options.receiverAddress + ) + : null; + + return { + trade: new SymbiosisCrossChainTrade( + { + from, + to, + gasData, + priceImpact: from.calculatePriceImpactPercent(to), + slippage: options.slippageTolerance, + swapParams, + feeInfo, + transitAmount: from.tokenAmount, + tradeType: { in: inTradeType, out: outTradeType }, + contractAddresses: { + providerRouter: tx.to!, + providerGateway: approveTo + }, + ...(toBlockchain === BLOCKCHAIN_NAME.MANTLE && + rewards.length && { promotions: this.getPromotions(rewards) }) + }, + options.providerAddress, + await this.getRoutePath(from, to, route) + ), + tradeType: this.type + }; + } catch (err) { + let rubicSdkError = CrossChainProvider.parseError(err); + const symbiosisErr = err as SymbiosisError; + const symbiosisSdkError = this.handleMinAmountError(symbiosisErr); + if ( + err?.error?.message.includes( + 'estimateGas: execution reverted: TransferHelper::safeTransfer: transfer failed' + ) + ) { + rubicSdkError = new NoLinkedAccountError(); + } + return { + trade: symbiosisSdkError ? disabledTrade : null, + error: symbiosisSdkError || rubicSdkError, + tradeType: this.type + }; + } + } + + private getPromotions(rewards: SymbiosisTokenAmount[]): string[] { + return rewards.map( + promo => `${promo.symbol}_${Web3Pure.fromWei(promo.amount, promo.decimals).toFixed()}` + ); + } + + protected async getFeeInfo( + fromBlockchain: Exclude, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } + + private getTransferToken( + route: SymbiosisToken[], + from: PriceTokenAmount + ): TokenStruct | undefined { + const fromBlockchainId = blockchainId[from.blockchain]; + const fromRouting = route.filter(token => token.chainId === fromBlockchainId); + + const token = fromRouting.at(-1)!; + return fromRouting.length !== 1 + ? { + address: token.address, + decimals: token.decimals, + name: token.name!, + blockchain: from.blockchain, + symbol: token.symbol! + } + : undefined; + } + + protected async getRoutePath( + fromToken: PriceTokenAmount, + toToken: PriceTokenAmount, + route: SymbiosisToken[] + ): Promise { + const fromChainId = blockchainId[fromToken.blockchain]; + const toChainId = blockchainId[toToken.blockchain]; + + const transitFrom = [...route].reverse().find(el => el.chainId === fromChainId); + const transitTo = route.find(el => el.chainId === toChainId); + + const fromTokenAmount = transitFrom + ? await RubicTokenAmount.createToken({ + blockchain: fromToken.blockchain, + address: transitFrom.address, + weiAmount: new BigNumber(0) + }) + : fromToken; + + const toTokenAmount = transitTo + ? await RubicTokenAmount.createToken({ + blockchain: toToken.blockchain, + address: transitTo.address, + weiAmount: new BigNumber(0) + }) + : toToken; + + const routePath: RubicStep[] = []; + + if (transitFrom) { + routePath.push({ + type: 'on-chain', + provider: ON_CHAIN_TRADE_TYPE.SYMBIOSIS_SWAP, + path: [fromToken, fromTokenAmount] + }); + } + routePath.push({ + type: 'cross-chain', + provider: CROSS_CHAIN_TRADE_TYPE.SYMBIOSIS, + path: [fromTokenAmount, toTokenAmount] + }); + if (transitTo) { + routePath.push({ + type: 'on-chain', + provider: ON_CHAIN_TRADE_TYPE.SYMBIOSIS_SWAP, + path: [toTokenAmount, toToken] + }); + } + return routePath; + } + + private getSwapParamsToAddress( + receiverAddress: string | undefined, + fromAddress: string, + toBlockchain: BlockchainName + ): string { + if (toBlockchain === BLOCKCHAIN_NAME.BITCOIN && !receiverAddress) { + return 'bc1qvyf8ufqpeyfe6vshfxdrr970rkqfphgz28ulhr'; + } + + return receiverAddress || fromAddress; + } + + private handleMinAmountError(err: SymbiosisError): RubicSdkError | null { + const msg = err.error?.message || ''; + + if (msg.includes('too low')) { + const [, minAmount] = msg.toLowerCase().split('min amount: ') as [string, string]; + const minAmountBN = new BigNumber(minAmount); + const isFeeInUSDC = minAmountBN.gt(0.5); + const symbol = isFeeInUSDC ? 'USDC' : 'WETH'; + + return new MinAmountError(minAmountBN, symbol); + } + + return null; + } + + private getEmptyTrade( + from: PriceTokenAmount, + to: PriceTokenAmount, + swapParams: SymbiosisSwappingParams, + feeInfo: FeeInfo + ): SymbiosisCrossChainTrade { + return new SymbiosisCrossChainTrade( + { + from, + to: to, + gasData: null, + priceImpact: null, + slippage: 0, + swapParams, + feeInfo, + transitAmount: from.tokenAmount, + tradeType: { in: undefined, out: undefined }, + contractAddresses: { + providerRouter: '', + providerGateway: '' + } + }, + '', + [ + { + type: 'cross-chain', + provider: CROSS_CHAIN_TRADE_TYPE.SYMBIOSIS, + path: [from, to] + } + ] + ); + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/symbiosis-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/symbiosis-cross-chain-trade.ts new file mode 100644 index 0000000..d098d0b --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/symbiosis-provider/symbiosis-cross-chain-trade.ts @@ -0,0 +1,361 @@ +import BigNumber from 'bignumber.js'; +import { FailedToCheckForTransactionReceiptError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { TronWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/tron-web3-public'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Injector } from 'src/core/injector/injector'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { SymbiosisApiService } from 'src/features/common/providers/symbiosis/services/symbiosis-api-service'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { OnChainSubtype } from 'src/features/cross-chain/calculation-manager/providers/common/models/on-chain-subtype'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { SymbiosisSwappingParams } from 'src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-swapping-params'; +import { SymbiosisTradeType } from 'src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-trade-data'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +import { SymbiosisCrossChainSupportedBlockchain } from './models/symbiosis-cross-chain-supported-blockchains'; + +/** + * Calculated Symbiosis cross-chain trade. + */ +export class SymbiosisCrossChainTrade extends EvmCrossChainTrade { + private readonly swappingParams: SymbiosisSwappingParams; + + /** @internal */ + public static async getGasData( + from: PriceTokenAmount, + toToken: PriceTokenAmount, + swapParams: SymbiosisSwappingParams, + feeInfo: FeeInfo, + providerGateway: string, + providerAddress: string, + receiverAddress?: string + ): Promise { + const trade = new SymbiosisCrossChainTrade( + { + from, + to: toToken, + gasData: null, + priceImpact: 0, + slippage: 0, + feeInfo, + transitAmount: new BigNumber(NaN), + tradeType: { in: undefined, out: undefined }, + contractAddresses: { + providerRouter: '', + providerGateway: providerGateway + }, + swapParams + }, + providerAddress || EvmWeb3Pure.EMPTY_ADDRESS, + [] + ); + return getCrossChainGasData(trade, receiverAddress); + } + + public readonly type = CROSS_CHAIN_TRADE_TYPE.SYMBIOSIS; + + public readonly isAggregator = false; + + public readonly onChainSubtype: OnChainSubtype; + + public readonly bridgeType = BRIDGE_TYPE.SYMBIOSIS; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + /** @internal */ + public readonly transitAmount: BigNumber; + + public readonly feeInfo: FeeInfo; + + /** + * Overall price impact, fetched from symbiosis api. + */ + public readonly priceImpact: number | null; + + public readonly gasData: GasData | null; + + private readonly slippage: number; + + private readonly contractAddresses: { providerRouter: string; providerGateway: string }; + + private get fromBlockchain(): SymbiosisCrossChainSupportedBlockchain { + return this.from.blockchain as SymbiosisCrossChainSupportedBlockchain; + } + + protected get fromContractAddress(): string { + return this.isProxyTrade + ? rubicProxyContractAddress[this.fromBlockchain].gateway + : this.contractAddresses.providerGateway; + } + + protected get methodName(): string { + return 'startBridgeTokensViaGenericCrossChain'; + } + + private get tronWeb3Public(): TronWeb3Public { + return Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.TRON); + } + + protected get evmWeb3Private(): EvmWeb3Private { + return Injector.web3PrivateService.getWeb3Private('EVM'); + } + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + gasData: GasData | null; + priceImpact: number | null; + slippage: number; + feeInfo: FeeInfo; + transitAmount: BigNumber; + tradeType: { in?: SymbiosisTradeType; out?: SymbiosisTradeType }; + contractAddresses: { providerRouter: string; providerGateway: string }; + swapParams: SymbiosisSwappingParams; + promotions?: string[]; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.swappingParams = crossChainTrade.swapParams; + this.gasData = crossChainTrade.gasData; + this.priceImpact = crossChainTrade.priceImpact; + this.toTokenAmountMin = this.to.tokenAmount.multipliedBy(1 - crossChainTrade.slippage); + this.feeInfo = crossChainTrade.feeInfo; + this.slippage = crossChainTrade.slippage; + this.transitAmount = crossChainTrade.transitAmount; + this.onChainSubtype = SymbiosisCrossChainTrade.getSubtype( + crossChainTrade.tradeType, + crossChainTrade.to.blockchain + ); + this.contractAddresses = crossChainTrade.contractAddresses; + this.promotions = crossChainTrade?.promotions || super.promotions; + } + + protected async getContractParams(options: GetContractParamsOptions): Promise { + const { + data, + value: providerValue, + to + } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress + ); + + let receiverAddress = options.receiverAddress; + let toAddress = ''; + if (this.to.blockchain === BLOCKCHAIN_NAME.TRON) { + const tronHexReceiverAddress = await this.tronWeb3Public.convertTronAddressToHex( + options.receiverAddress! + ); + receiverAddress = `0x${tronHexReceiverAddress.slice(2)}`; + + const toTokenTronAddress = await this.tronWeb3Public.convertTronAddressToHex( + this.to.address + ); + toAddress = `0x${toTokenTronAddress.slice(2)}`; + } + + if (this.from.isNative && this.from.blockchain === BLOCKCHAIN_NAME.METIS) { + toAddress = '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000'; + } + + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData( + { ...options, receiverAddress }, + { + walletAddress: this.walletAddress, + fromTokenAmount: this.from, + toTokenAmount: this.to, + toAddress, + srcChainTrade: null, + providerAddress: this.providerAddress, + type: `native:${this.type}`, + fromAddress: this.walletAddress + } + ); + const providerData = await ProxyCrossChainEvmTrade.getGenericProviderData( + to!, + data! as string, + this.fromBlockchain as EvmBlockchainName, + this.contractAddresses.providerGateway, + '0' + ); + + const methodArguments = [bridgeData, providerData]; + + const value = this.getSwapValue(providerValue); + + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } + + /** + * Used for direct provider swaps. + * @param options Swap options + */ + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + await this.checkTradeErrors(); + await this.checkReceiverAddress( + options.receiverAddress, + !BlockchainsInfo.isEvmBlockchainName(this.to.blockchain) + ); + + await this.checkAllowanceAndApprove(options); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + try { + const { data, value, to } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress + ); + + await this.evmWeb3Private.trySendTransaction(to, { + data, + value, + onTransactionHash, + gas: gasLimit, + gasPriceOptions + }); + + return transactionHash!; + } catch (err) { + if (err instanceof FailedToCheckForTransactionReceiptError) { + return transactionHash!; + } + throw err; + } + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + return fromUsd.dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact ?? null, + slippage: this.slippage * 100, + routePath: this.routePath + }; + } + + private static getSubtype( + tradeType: { + in?: SymbiosisTradeType; + out?: SymbiosisTradeType; + }, + toBlockchain: BlockchainName + ): OnChainSubtype { + const mapping: Record = { + dex: ON_CHAIN_TRADE_TYPE.SYMBIOSIS_SWAP, + '1inch': ON_CHAIN_TRADE_TYPE.ONE_INCH, + 'open-ocean': ON_CHAIN_TRADE_TYPE.OPEN_OCEAN, + wrap: ON_CHAIN_TRADE_TYPE.WRAPPED, + izumi: ON_CHAIN_TRADE_TYPE.IZUMI, + default: undefined + }; + return { + from: mapping?.[tradeType?.in || 'default'], + to: + toBlockchain === BLOCKCHAIN_NAME.BITCOIN + ? ON_CHAIN_TRADE_TYPE.REN_BTC + : mapping?.[tradeType?.out || 'default'] + }; + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + const walletAddress = this.walletAddress; + const params: SymbiosisSwappingParams = { + ...this.swappingParams, + from: walletAddress, + to: receiverAddress || walletAddress, + revertableAddress: this.getRevertableAddress( + receiverAddress, + walletAddress, + this.to.blockchain + ) + }; + + const tradeData = await SymbiosisApiService.getCrossChainSwapTx(params); + + const config = { + data: tradeData.tx.data!.toString(), + value: tradeData.tx.value?.toString() || '0', + to: tradeData.tx.to! + }; + + return { config, amount: tradeData.tokenAmountOut.amount }; + } + + private getRevertableAddress( + receiverAddress: string | undefined, + walletAddress: string, + toBlockchain: BlockchainName + ): string { + if (toBlockchain === BLOCKCHAIN_NAME.BITCOIN) { + return walletAddress; + } + + return receiverAddress || walletAddress; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/constants/taiko-bridge-contract-address.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/constants/taiko-bridge-contract-address.ts new file mode 100644 index 0000000..5479d69 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/constants/taiko-bridge-contract-address.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const taikoBridgeContractAddress = { + [BLOCKCHAIN_NAME.HOLESKY]: { + nativeProvider: '0xf458747c6d6db57970dE80Da6474C0A3dfE94BF1', + erc20Provider: '0x095DDce4fd8818aD159D778E6a9898A2474933ca' + }, + [BLOCKCHAIN_NAME.TAIKO]: { + nativeProvider: '0x1670080000000000000000000000000000000001', + erc20Provider: '0x1670080000000000000000000000000000000002' + } +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/constants/taiko-gateway-abi.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/constants/taiko-gateway-abi.ts new file mode 100644 index 0000000..937f06a --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/constants/taiko-gateway-abi.ts @@ -0,0 +1,78 @@ +import { AbiItem } from 'web3-utils'; + +export const taikoNativeBridgeABI: AbiItem[] = [ + { + inputs: [ + { + components: [ + { internalType: 'uint128', name: 'id', type: 'uint128' }, + { internalType: 'address', name: 'from', type: 'address' }, + { internalType: 'uint64', name: 'srcChainId', type: 'uint64' }, + { internalType: 'uint64', name: 'destChainId', type: 'uint64' }, + { internalType: 'address', name: 'owner', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'address', name: 'refundTo', type: 'address' }, + { internalType: 'uint256', name: 'value', type: 'uint256' }, + { internalType: 'uint256', name: 'fee', type: 'uint256' }, + { internalType: 'uint256', name: 'gasLimit', type: 'uint256' }, + { internalType: 'bytes', name: 'data', type: 'bytes' }, + { internalType: 'string', name: 'memo', type: 'string' } + ], + internalType: 'struct IBridge.Message', + name: 'message', + type: 'tuple' + } + ], + name: 'sendMessage', + outputs: [ + { internalType: 'bytes32', name: 'msgHash', type: 'bytes32' }, + { + components: [ + { internalType: 'uint128', name: 'id', type: 'uint128' }, + { internalType: 'address', name: 'from', type: 'address' }, + { internalType: 'uint64', name: 'srcChainId', type: 'uint64' }, + { internalType: 'uint64', name: 'destChainId', type: 'uint64' }, + { internalType: 'address', name: 'owner', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'address', name: 'refundTo', type: 'address' }, + { internalType: 'uint256', name: 'value', type: 'uint256' }, + { internalType: 'uint256', name: 'fee', type: 'uint256' }, + { internalType: 'uint256', name: 'gasLimit', type: 'uint256' }, + { internalType: 'bytes', name: 'data', type: 'bytes' }, + { internalType: 'string', name: 'memo', type: 'string' } + ], + internalType: 'struct IBridge.Message', + name: '_message', + type: 'tuple' + } + ], + stateMutability: 'payable', + type: 'function' + } +]; + +export const taikoERC20BridgeABI: AbiItem[] = [ + { + stateMutability: 'payable', + type: 'function', + inputs: [ + { + name: 'opt', + internalType: 'struct ERC20Vault.BridgeTransferOp', + type: 'tuple', + components: [ + { name: 'destChainId', internalType: 'uint256', type: 'uint256' }, + { name: 'to', internalType: 'address', type: 'address' }, + { name: 'token', internalType: 'address', type: 'address' }, + { name: 'amount', internalType: 'uint256', type: 'uint256' }, + { name: 'gasLimit', internalType: 'uint256', type: 'uint256' }, + { name: 'fee', internalType: 'uint256', type: 'uint256' }, + { name: 'refundTo', internalType: 'address', type: 'address' }, + { name: 'memo', internalType: 'string', type: 'string' } + ] + } + ], + name: 'sendToken', + outputs: [] + } +]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/models/taiko-bridge-supported-blockchains.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/models/taiko-bridge-supported-blockchains.ts new file mode 100644 index 0000000..701ffd2 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/models/taiko-bridge-supported-blockchains.ts @@ -0,0 +1,8 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const taikoBridgeSupportedBlockchains = [ + BLOCKCHAIN_NAME.TAIKO, + BLOCKCHAIN_NAME.HOLESKY +] as const; + +export type TaikoBridgeSupportedBlockchain = (typeof taikoBridgeSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/taiko-bridge-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/taiko-bridge-provider.ts new file mode 100644 index 0000000..45e536c --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/taiko-bridge-provider.ts @@ -0,0 +1,94 @@ +import { NotSupportedTokensError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CbridgeCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-supported-blockchains'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; + +import { + TaikoBridgeSupportedBlockchain, + taikoBridgeSupportedBlockchains +} from './models/taiko-bridge-supported-blockchains'; +import { TaikoBridgeTrade } from './taiko-bridge-trade'; + +export class TaikoBridgeProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.TAIKO_BRIDGE; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is TaikoBridgeSupportedBlockchain { + return taikoBridgeSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public async calculate( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + const fromBlockchain = fromToken.blockchain as TaikoBridgeSupportedBlockchain; + const toBlockchain = toToken.blockchain as TaikoBridgeSupportedBlockchain; + + if (!this.areSupportedBlockchains(fromBlockchain, toBlockchain)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + try { + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: fromToken.tokenAmount + }); + + const gasData = + options.gasCalculation === 'enabled' + ? await TaikoBridgeTrade.getGasData(fromToken, to) + : null; + + return { + trade: new TaikoBridgeTrade( + { + from: fromToken, + to, + gasData + }, + options.providerAddress, + await this.getRoutePath(fromToken, to) + ), + tradeType: this.type + }; + } catch (err) { + const rubicSdkError = CrossChainProvider.parseError(err); + + return { + trade: null, + error: rubicSdkError, + tradeType: this.type + }; + } + } + + protected async getFeeInfo( + _fromBlockchain: CbridgeCrossChainSupportedBlockchain, + _providerAddress: string, + _percentFeeToken: PriceTokenAmount, + _useProxy: boolean + ): Promise { + return {}; + } + + protected async getRoutePath( + fromToken: PriceTokenAmount, + toToken: PriceTokenAmount + ): Promise { + return [{ type: 'cross-chain', provider: this.type, path: [fromToken, toToken] }]; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/taiko-bridge-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/taiko-bridge-trade.ts new file mode 100644 index 0000000..7c5bc9d --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/taiko-bridge/taiko-bridge-trade.ts @@ -0,0 +1,213 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { + taikoERC20BridgeABI, + taikoNativeBridgeABI +} from 'src/features/cross-chain/calculation-manager/providers/taiko-bridge/constants/taiko-gateway-abi'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; + +import { taikoBridgeContractAddress } from './constants/taiko-bridge-contract-address'; +import { TaikoBridgeSupportedBlockchain } from './models/taiko-bridge-supported-blockchains'; + +export class TaikoBridgeTrade extends EvmCrossChainTrade { + /** @internal */ + public static async getGasData( + from: PriceTokenAmount, + to: PriceTokenAmount + ): Promise { + const trade = new TaikoBridgeTrade( + { + from, + to, + gasData: null + }, + EvmWeb3Pure.EMPTY_ADDRESS, + [] + ); + + return getCrossChainGasData(trade); + } + + public readonly onChainSubtype = { from: undefined, to: undefined }; + + public readonly type = CROSS_CHAIN_TRADE_TYPE.TAIKO_BRIDGE; + + public readonly isAggregator = false; + + public readonly bridgeType = BRIDGE_TYPE.TAIKO_BRIDGE; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly gasData: GasData | null; + + /** + * id of taiko bridge tx, used to get trade status. + */ + public id: string | undefined; + + private get fromBlockchain(): TaikoBridgeSupportedBlockchain { + return this.from.blockchain as TaikoBridgeSupportedBlockchain; + } + + protected get fromContractAddress(): string { + return this.from.isNative + ? taikoBridgeContractAddress[this.fromBlockchain]!.nativeProvider + : taikoBridgeContractAddress[this.fromBlockchain]!.erc20Provider; + } + + public readonly feeInfo: FeeInfo = {}; + + public readonly onChainTrade = null; + + protected get methodName(): string { + return this.onChainTrade + ? 'swapAndStartBridgeTokensViaGenericCrossChain' + : 'startBridgeTokensViaGenericCrossChain'; + } + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + gasData: GasData | null; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.gasData = crossChainTrade.gasData; + this.toTokenAmountMin = crossChainTrade.to.tokenAmount; + } + + public async getContractParams(): Promise { + throw new Error('Method is not supported'); + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + return fromUsd.dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: null, + slippage: 0, + routePath: this.routePath + }; + } + + protected async getTransactionConfigAndAmount( + _receiverAddress?: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + let methodArguments; + let fee; + + const account = this.web3Private.address; + + if (this.fromBlockchain === BLOCKCHAIN_NAME.HOLESKY) { + if (this.from.isNative) { + methodArguments = [ + { + id: 0, + from: account, + srcChainId: blockchainId[BLOCKCHAIN_NAME.HOLESKY], + destChainId: blockchainId[BLOCKCHAIN_NAME.TAIKO], + owner: account, + to: account, + refundTo: account, + value: this.from.stringWeiAmount, + fee: '9000000', + gasLimit: '140000', + data: '0x', + memo: '' + } + ]; + + fee = '9000000'; + } else { + methodArguments = [ + { + destChainId: blockchainId[BLOCKCHAIN_NAME.TAIKO], + to: account, + token: this.from.address, + amount: this.from.stringWeiAmount, + gasLimit: '140000', + fee: '11459820715200000', + refundTo: account, + memo: '' + } + ]; + + fee = '11459820715200000'; + } + } else { + if (this.from.isNative) { + methodArguments = [ + { + id: 0, + from: account, + srcChainId: blockchainId[BLOCKCHAIN_NAME.TAIKO], + destChainId: blockchainId[BLOCKCHAIN_NAME.HOLESKY], + owner: account, + to: account, + refundTo: account, + value: this.from.stringWeiAmount, + fee: '34774829357400000', + gasLimit: '140000', + data: '0x', + memo: '' + } + ]; + + fee = '34774829357400000'; + } else { + methodArguments = [ + { + destChainId: blockchainId[BLOCKCHAIN_NAME.HOLESKY], + to: account, + token: this.from.address, + amount: this.from.stringWeiAmount, + gasLimit: '140000', + fee: '88242155100000', + refundTo: account, + memo: '' + } + ]; + + fee = '88242155100000'; + } + } + + const config = EvmWeb3Pure.encodeMethodCall( + this.from.isNative + ? taikoBridgeContractAddress[this.fromBlockchain].nativeProvider + : taikoBridgeContractAddress[this.fromBlockchain].erc20Provider, + this.from.isNative ? taikoNativeBridgeABI : taikoERC20BridgeABI, + this.from.isNative ? 'sendMessage' : 'sendToken', + methodArguments, + this.from.isNative ? this.from.weiAmount.plus(fee).toFixed() : fee + ); + + return { config, amount: this.to.stringWeiAmount }; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-contract-address.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-contract-address.ts new file mode 100644 index 0000000..f84ed47 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-contract-address.ts @@ -0,0 +1,117 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { UniversalContract } from 'src/features/cross-chain/calculation-manager/providers/common/models/universal-contract'; +import { XyCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-supported-blockchains'; + +export const xyContractAddress: Record = { + [BLOCKCHAIN_NAME.SCROLL]: { + providerGateway: '0x778C974568e376146dbC64fF12aD55B2d1c4133f', + providerRouter: '0x778C974568e376146dbC64fF12aD55B2d1c4133f', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.SCROLL].router + }, + [BLOCKCHAIN_NAME.ETHEREUM]: { + providerGateway: '0x4315f344a905dC21a08189A117eFd6E1fcA37D57', + providerRouter: '0x4315f344a905dC21a08189A117eFd6E1fcA37D57', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ETHEREUM].router + }, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: { + providerGateway: '0x7D26F09d4e2d032Efa0729fC31a4c2Db8a2394b1', + providerRouter: '0x7D26F09d4e2d032Efa0729fC31a4c2Db8a2394b1', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN].router + }, + [BLOCKCHAIN_NAME.POLYGON]: { + providerGateway: '0x0c988b66EdEf267D04f100A879db86cdb7B9A34F', + providerRouter: '0x0c988b66EdEf267D04f100A879db86cdb7B9A34F', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.POLYGON].router + }, + [BLOCKCHAIN_NAME.FANTOM]: { + providerGateway: '0xDa241399697fa3F6cD496EdAFab6191498Ec37F5', + providerRouter: '0xDa241399697fa3F6cD496EdAFab6191498Ec37F5', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.FANTOM].router + }, + [BLOCKCHAIN_NAME.CRONOS]: { + providerGateway: '0xF103b5B479d2A629F422C42bb35E7eEceE1ad55E', + providerRouter: '0xF103b5B479d2A629F422C42bb35E7eEceE1ad55E', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.CRONOS].router + }, + // [BLOCKCHAIN_NAME.THUNDER_CORE]: { + // providerGateway: '0xF103b5B479d2A629F422C42bb35E7eEceE1ad55E', + // providerRouter: '0xF103b5B479d2A629F422C42bb35E7eEceE1ad55E', + // rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.THUNDER_CORE] + // } + [BLOCKCHAIN_NAME.AVALANCHE]: { + providerGateway: '0x2C86f0FF75673D489b7D72D9986929a2b0Ed596C', + providerRouter: '0x2C86f0FF75673D489b7D72D9986929a2b0Ed596C', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.AVALANCHE].router + }, + // [BLOCKCHAIN_NAME.KUCOIN]: { + // providerGateway: '0x7e803b54295Cd113Bf48E7f069f0531575DA1139', + // providerRouter: '0x7e803b54295Cd113Bf48E7f069f0531575DA1139', + // rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.KUCOIN] + // }, + [BLOCKCHAIN_NAME.ARBITRUM]: { + providerGateway: '0x33383265290421C704c6b09F4BF27ce574DC4203', + providerRouter: '0x33383265290421C704c6b09F4BF27ce574DC4203', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ARBITRUM].router + }, + [BLOCKCHAIN_NAME.OPTIMISM]: { + providerGateway: '0x7a6e01880693093abACcF442fcbED9E0435f1030', + providerRouter: '0x7a6e01880693093abACcF442fcbED9E0435f1030', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.OPTIMISM].router + }, + [BLOCKCHAIN_NAME.ASTAR_EVM]: { + providerGateway: '0x5C6C12Fd8b1f7E60E5B60512712cFbE0192E795E', + providerRouter: '0x5C6C12Fd8b1f7E60E5B60512712cFbE0192E795E', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ASTAR_EVM].router + }, + [BLOCKCHAIN_NAME.MOONRIVER]: { + providerGateway: '0xc67Dd7054915a2B0aA3e48f35DA714Ff861e71BD', + providerRouter: '0xc67Dd7054915a2B0aA3e48f35DA714Ff861e71BD', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.MOONRIVER].router + }, + [BLOCKCHAIN_NAME.ZK_SYNC]: { + providerGateway: '0xe4e156167cc9C7AC4AbD8d39d203a5495F775547', + providerRouter: '0xe4e156167cc9C7AC4AbD8d39d203a5495F775547', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.ZK_SYNC].router + }, + [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: { + providerGateway: '0x3689D3B912d4D73FfcAad3a80861e7caF2d4F049', + providerRouter: '0x3689D3B912d4D73FfcAad3a80861e7caF2d4F049', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.POLYGON_ZKEVM].router + }, + [BLOCKCHAIN_NAME.LINEA]: { + providerGateway: '0x73Ce60416035B8D7019f6399778c14ccf5C9c7A1', + providerRouter: '0x73Ce60416035B8D7019f6399778c14ccf5C9c7A1', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.LINEA].router + }, + [BLOCKCHAIN_NAME.BASE]: { + providerGateway: '0x73Ce60416035B8D7019f6399778c14ccf5C9c7A1', + providerRouter: '0x73Ce60416035B8D7019f6399778c14ccf5C9c7A1', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.BASE].router + }, + [BLOCKCHAIN_NAME.MANTLE]: { + providerGateway: '0x73Ce60416035B8D7019f6399778c14ccf5C9c7A1', + providerRouter: '0x73Ce60416035B8D7019f6399778c14ccf5C9c7A1', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.MANTLE].router + }, + [BLOCKCHAIN_NAME.BLAST]: { + providerGateway: '0x73Ce60416035B8D7019f6399778c14ccf5C9c7A1', + providerRouter: '0x73Ce60416035B8D7019f6399778c14ccf5C9c7A1', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.BLAST].router + }, + [BLOCKCHAIN_NAME.XLAYER]: { + providerGateway: '0x73Ce60416035B8D7019f6399778c14ccf5C9c7A1', + providerRouter: '0x73Ce60416035B8D7019f6399778c14ccf5C9c7A1', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.XLAYER].router + }, + [BLOCKCHAIN_NAME.TAIKO]: { + providerGateway: '0x73Ce60416035B8D7019f6399778c14ccf5C9c7A1', + providerRouter: '0x73Ce60416035B8D7019f6399778c14ccf5C9c7A1', + rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.TAIKO].router + } + // [BLOCKCHAIN_NAME.KLAYTN]: { + // providerGateway: '0x52075Fd1fF67f03beABCb5AcdA9679b02d98cA37', + // providerRouter: '0x52075Fd1fF67f03beABCb5AcdA9679b02d98cA37', + // rubicRouter: rubicProxyContractAddress[BLOCKCHAIN_NAME.KLAYTN] + // } +}; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-status-code.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-status-code.ts new file mode 100644 index 0000000..8a73734 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-status-code.ts @@ -0,0 +1 @@ +export type XyStatusCode = '0' | '3' | '4' | '5' | '6' | '10' | '99'; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-supported-blockchains.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-supported-blockchains.ts new file mode 100644 index 0000000..830e034 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-supported-blockchains.ts @@ -0,0 +1,28 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const xySupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.CRONOS, + // BLOCKCHAIN_NAME.THUNDER_CORE + BLOCKCHAIN_NAME.AVALANCHE, + // BLOCKCHAIN_NAME.KUCOIN + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.ASTAR_EVM, + BLOCKCHAIN_NAME.MOONRIVER, + BLOCKCHAIN_NAME.ZK_SYNC, + // BLOCKCHAIN_NAME.KLAYTN + BLOCKCHAIN_NAME.POLYGON_ZKEVM, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.SCROLL, + BLOCKCHAIN_NAME.MANTLE, + BLOCKCHAIN_NAME.BLAST, + BLOCKCHAIN_NAME.XLAYER, + BLOCKCHAIN_NAME.TAIKO +] as const; + +export type XyCrossChainSupportedBlockchain = (typeof xySupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/xy-cross-chain-provider.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/xy-cross-chain-provider.ts new file mode 100644 index 0000000..bbd1f7b --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/xy-cross-chain-provider.ts @@ -0,0 +1,244 @@ +import BigNumber from 'bignumber.js'; +import { NotSupportedTokensError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, TokenAmount } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { + XY_AFFILIATE_ADDRESS, + XY_API_ENDPOINT, + XY_NATIVE_ADDRESS +} from 'src/features/common/providers/xy/constants/xy-api-params'; +import { XyBuildTxRequest } from 'src/features/common/providers/xy/models/xy-build-tx-request'; +import { XyQuoteRequest } from 'src/features/common/providers/xy/models/xy-quote-request'; +import { XyQuoteResponse } from 'src/features/common/providers/xy/models/xy-quote-response'; +import { XyQuote } from 'src/features/common/providers/xy/models/xy-quote-success-response'; +import { xyAnalyzeStatusCode } from 'src/features/common/providers/xy/utils/xy-utils'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { CalculationResult } from 'src/features/cross-chain/calculation-manager/providers/common/models/calculation-result'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { + XyCrossChainSupportedBlockchain, + xySupportedBlockchains +} from 'src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-supported-blockchains'; +import { XyCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/xy-provider/xy-cross-chain-trade'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +import { BRIDGE_TYPE, BridgeType } from '../common/models/bridge-type'; + +export class XyCrossChainProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.XY; + + public isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is XyCrossChainSupportedBlockchain { + return xySupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public async calculate( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + const fromBlockchain = fromToken.blockchain as XyCrossChainSupportedBlockchain; + const toBlockchain = toToken.blockchain as XyCrossChainSupportedBlockchain; + const useProxy = options?.useProxy?.[this.type] ?? true; + + if (!this.areSupportedBlockchains(fromBlockchain, toBlockchain)) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + try { + const receiverAddress = + options.receiverAddress || this.getWalletAddress(fromBlockchain); + + const feeInfo = await this.getFeeInfo( + fromBlockchain, + options.providerAddress, + fromToken, + useProxy + ); + + const fromWithoutFee = getFromWithoutFee( + fromToken, + feeInfo.rubicProxy?.platformFee?.percent + ); + + const slippageTolerance = options.slippageTolerance * 100; + + const requestParams: XyQuoteRequest = { + srcChainId: blockchainId[fromBlockchain], + srcQuoteTokenAddress: fromToken.isNative ? XY_NATIVE_ADDRESS : fromToken.address, + srcQuoteTokenAmount: fromWithoutFee.stringWeiAmount, + dstChainId: blockchainId[toBlockchain], + dstQuoteTokenAddress: toToken.isNative ? XY_NATIVE_ADDRESS : toToken.address, + slippage: slippageTolerance, + affiliate: XY_AFFILIATE_ADDRESS + }; + + const { success, routes, errorCode, errorMsg } = + await Injector.httpClient.get(`${XY_API_ENDPOINT}/quote`, { + params: { ...requestParams } + }); + + if (!success) { + xyAnalyzeStatusCode(errorCode, errorMsg); + } + + const { + srcSwapDescription, + bridgeDescription, + dstSwapDescription, + dstQuoteTokenAmount + } = routes[0]!; + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: Web3Pure.fromWei(dstQuoteTokenAmount, toToken.decimals) + }); + + const buildTxTransactionRequest: XyBuildTxRequest = { + ...requestParams, + ...(srcSwapDescription?.provider && { + srcSwapProvider: srcSwapDescription?.provider + }), + ...(bridgeDescription?.provider && { + srcBridgeTokenAddress: bridgeDescription.srcBridgeTokenAddress, + bridgeProvider: bridgeDescription.provider, + dstBridgeTokenAddress: bridgeDescription.dstBridgeTokenAddress + }), + ...(dstSwapDescription?.provider && { + dstSwapProvider: dstSwapDescription?.provider + }), + receiver: receiverAddress + }; + const bridgeType = ( + bridgeDescription.provider === 'yBridge' ? BRIDGE_TYPE.YPOOL : BRIDGE_TYPE.XY + ) as BridgeType; + const gasData = + options.gasCalculation === 'enabled' + ? await XyCrossChainTrade.getGasData( + fromToken, + to, + buildTxTransactionRequest, + feeInfo, + options.providerAddress + ) + : null; + + return { + trade: new XyCrossChainTrade( + { + from: fromToken, + to, + transactionRequest: buildTxTransactionRequest, + gasData, + priceImpact: fromToken.calculatePriceImpactPercent(to), + slippage: options.slippageTolerance, + feeInfo, + onChainTrade: null, + bridgeType: bridgeType + }, + options.providerAddress, + await this.getRoutePath(fromToken, to, { + srcSwapDescription, + bridgeDescription, + dstSwapDescription, + dstQuoteTokenAmount + }) + ), + tradeType: this.type + }; + } catch (err) { + const rubicSdkError = CrossChainProvider.parseError(err); + + return { + trade: null, + error: rubicSdkError, + tradeType: this.type + }; + } + } + + protected async getFeeInfo( + fromBlockchain: XyCrossChainSupportedBlockchain, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } + + protected async getRoutePath( + fromToken: PriceTokenAmount, + toToken: PriceTokenAmount, + quote: XyQuote + ): Promise { + const transitFromAddress = quote.srcSwapDescription?.dstTokenAddress; + const transitToAddress = quote.dstSwapDescription?.srcTokenAddress; + + const fromTokenAmount = transitFromAddress + ? await TokenAmount.createToken({ + blockchain: fromToken.blockchain, + address: compareAddresses(transitFromAddress, XY_NATIVE_ADDRESS) + ? EvmWeb3Pure.EMPTY_ADDRESS + : transitFromAddress, + weiAmount: new BigNumber(0) + }) + : fromToken; + + const toTokenAmount = transitToAddress + ? await TokenAmount.createToken({ + blockchain: toToken.blockchain, + address: compareAddresses(transitToAddress, XY_NATIVE_ADDRESS) + ? EvmWeb3Pure.EMPTY_ADDRESS + : transitToAddress, + weiAmount: new BigNumber(0) + }) + : toToken; + + const routePath: RubicStep[] = []; + + if (transitFromAddress) { + routePath.push({ + type: 'on-chain', + // @TODO provider: ON_CHAIN_TRADE_TYPE.XY_DEX, + provider: ON_CHAIN_TRADE_TYPE.ONE_INCH, + path: [fromToken, fromTokenAmount] + }); + } + routePath.push({ + type: 'cross-chain', + provider: CROSS_CHAIN_TRADE_TYPE.XY, + path: [fromTokenAmount, toTokenAmount] + }); + if (transitToAddress) { + routePath.push({ + type: 'on-chain', + // @TODO provider: ON_CHAIN_TRADE_TYPE.XY_DEX, + provider: ON_CHAIN_TRADE_TYPE.ONE_INCH, + path: [toTokenAmount, toToken] + }); + } + return routePath; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/xy-cross-chain-trade.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/xy-cross-chain-trade.ts new file mode 100644 index 0000000..e26d3b1 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/providers/xy-provider/xy-cross-chain-trade.ts @@ -0,0 +1,241 @@ +import BigNumber from 'bignumber.js'; +import { BytesLike } from 'ethers'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { Cache } from 'src/common/utils/decorators'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { XY_API_ENDPOINT } from 'src/features/common/providers/xy/constants/xy-api-params'; +import { XyBuildTxRequest } from 'src/features/common/providers/xy/models/xy-build-tx-request'; +import { XyBuildTxResponse } from 'src/features/common/providers/xy/models/xy-build-tx-response'; +import { xyAnalyzeStatusCode } from 'src/features/common/providers/xy/utils/xy-utils'; +import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { + BRIDGE_TYPE, + BridgeType +} from 'src/features/cross-chain/calculation-manager/providers/common/models/bridge-type'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { xyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-contract-address'; +import { XyCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-supported-blockchains'; +import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +/** + * Calculated XY cross-chain trade. + */ +export class XyCrossChainTrade extends EvmCrossChainTrade { + /** @internal */ + public static async getGasData( + from: PriceTokenAmount, + toToken: PriceTokenAmount, + transactionRequest: XyBuildTxRequest, + feeInfo: FeeInfo, + providerAddress: string + ): Promise { + try { + const trade = new XyCrossChainTrade( + { + from, + to: toToken, + transactionRequest, + gasData: null, + priceImpact: 0, + slippage: 0, + feeInfo, + onChainTrade: null, + bridgeType: BRIDGE_TYPE.XY + }, + providerAddress || EvmWeb3Pure.EMPTY_ADDRESS, + [] + ); + + return getCrossChainGasData(trade); + } catch (_err) { + return null; + } + } + + public readonly type = CROSS_CHAIN_TRADE_TYPE.XY; + + public readonly isAggregator = false; + + public readonly onChainSubtype = { + from: undefined, + to: undefined + }; + + public readonly bridgeType: BridgeType; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly toTokenAmountMin: BigNumber; + + public readonly priceImpact: number | null; + + public readonly gasData: GasData | null; + + private readonly transactionRequest: XyBuildTxRequest; + + private get fromBlockchain(): XyCrossChainSupportedBlockchain { + return this.from.blockchain as XyCrossChainSupportedBlockchain; + } + + protected get fromContractAddress(): string { + return this.isProxyTrade + ? rubicProxyContractAddress[this.fromBlockchain].gateway + : xyContractAddress[this.fromBlockchain].providerGateway; + } + + public readonly feeInfo: FeeInfo; + + private readonly slippage: number; + + protected get methodName(): string { + return 'startBridgeTokensViaGenericCrossChain'; + } + + private readonly onChainTrade: EvmOnChainTrade | null; + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + transactionRequest: XyBuildTxRequest; + gasData: GasData | null; + priceImpact: number | null; + slippage: number; + feeInfo: FeeInfo; + onChainTrade: EvmOnChainTrade | null; + bridgeType: BridgeType; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.transactionRequest = crossChainTrade.transactionRequest; + this.gasData = crossChainTrade.gasData; + this.priceImpact = crossChainTrade.priceImpact; + this.slippage = crossChainTrade.slippage; + this.toTokenAmountMin = this.to.tokenAmount.multipliedBy(1 - crossChainTrade.slippage); + this.feeInfo = crossChainTrade.feeInfo; + this.onChainTrade = crossChainTrade.onChainTrade; + this.bridgeType = crossChainTrade.bridgeType; + } + + public async getContractParams(options: GetContractParamsOptions): Promise { + const receiverAddress = options?.receiverAddress || this.walletAddress; + const { + data, + value: providerValue, + to: providerRouter + } = await this.setTransactionConfig(false, options?.useCacheData || false, receiverAddress); + + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData(options, { + walletAddress: receiverAddress, + fromTokenAmount: this.from, + toTokenAmount: this.to, + srcChainTrade: null, + providerAddress: this.providerAddress, + type: `native:${this.type}`, + fromAddress: this.walletAddress + }); + + const providerData = await ProxyCrossChainEvmTrade.getGenericProviderData( + providerRouter, + data!, + this.fromBlockchain, + providerRouter, + '0' + ); + + const methodArguments = [bridgeData, providerData]; + + const value = this.getSwapValue(providerValue); + + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + return fromUsd.dividedBy(this.to.tokenAmount); + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + const params: XyBuildTxRequest = { + ...this.transactionRequest, + ...(receiverAddress && { receiver: receiverAddress }) + }; + + const { success, tx, route, errorCode, errorMsg } = + await this.getResponseFromApiToTransactionRequest(params); + + if (!success) { + xyAnalyzeStatusCode(errorCode, errorMsg); + } + + return { config: tx!, amount: route.dstQuoteTokenAmount }; + } + + @Cache({ + maxAge: 15_000 + }) + private async getResponseFromApiToTransactionRequest( + params: XyBuildTxRequest + ): Promise { + return Injector.httpClient.get(`${XY_API_ENDPOINT}/buildTx`, { + params: { ...params } + }); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact ?? null, + slippage: this.slippage * 100, + routePath: this.routePath + }; + } + + protected getProviderData(_sourceData: BytesLike): unknown[] { + return [ + this.to.address, + Web3Pure.toWei(this.toTokenAmountMin, this.to.decimals), + this.slippage * 10_000 + ]; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/utils/compare-cross-chain-trades.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/utils/compare-cross-chain-trades.ts new file mode 100644 index 0000000..01588b3 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/utils/compare-cross-chain-trades.ts @@ -0,0 +1,73 @@ +import BigNumber from 'bignumber.js'; +import { MaxAmountError, MinAmountError } from 'src/common/errors'; +import { WrappedCrossChainTradeOrNull } from 'src/features/cross-chain/calculation-manager/models/wrapped-cross-chain-trade-or-null'; + +/** + * Compares two cross chain trades for sorting algorithm. + */ + +// eslint-disable-next-line complexity +export function compareCrossChainTrades( + nextWrappedTrade: WrappedCrossChainTradeOrNull, + prevWrappedTrade: WrappedCrossChainTradeOrNull, + nativePriceForNextTrade?: BigNumber, + nativePriceForPrevTrade?: BigNumber, + compareWithoutTokenPrice?: boolean +): number { + if ( + prevWrappedTrade?.error instanceof MinAmountError && + nextWrappedTrade?.error instanceof MinAmountError + ) { + return prevWrappedTrade.error.minAmount.lte(nextWrappedTrade.error.minAmount) ? 1 : -1; + } + if ( + prevWrappedTrade?.error instanceof MaxAmountError && + nextWrappedTrade?.error instanceof MaxAmountError + ) { + return prevWrappedTrade.error.maxAmount.gte(nextWrappedTrade.error.maxAmount) ? 1 : -1; + } + + if (!prevWrappedTrade || !prevWrappedTrade?.trade || prevWrappedTrade.error) { + if ( + nextWrappedTrade?.trade || + nextWrappedTrade?.error instanceof MinAmountError || + nextWrappedTrade?.error instanceof MaxAmountError + ) { + return -1; + } + return 1; + } + + if ( + !nextWrappedTrade || + nextWrappedTrade.error || + nextWrappedTrade?.trade?.to?.tokenAmount.lte(0) + ) { + return 1; + } + + if (compareWithoutTokenPrice) { + const prevTradeToTokenAmount = prevWrappedTrade?.trade.to.tokenAmount; + const nextTradeToTokenAmount = nextWrappedTrade?.trade?.to.tokenAmount; + + if (!nextTradeToTokenAmount) { + return 1; + } + if (!prevTradeToTokenAmount) { + return -1; + } + return prevTradeToTokenAmount.lte(nextTradeToTokenAmount) ? -1 : 1; + } else { + const fromUsd = prevWrappedTrade?.trade.getUsdPrice(nativePriceForPrevTrade); + + const toUsd = nextWrappedTrade?.trade?.getUsdPrice(nativePriceForNextTrade); + + if (!toUsd || !toUsd.isFinite()) { + return 1; + } + if (!fromUsd || !fromUsd.isFinite()) { + return -1; + } + return fromUsd.lte(toUsd) ? -1 : 1; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/utils/convert-gas-price.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/utils/convert-gas-price.ts new file mode 100644 index 0000000..1cdfbdf --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/utils/convert-gas-price.ts @@ -0,0 +1,15 @@ +import BigNumber from 'bignumber.js'; +import { + GasPrice, + GasPriceBN +} from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/gas-price'; + +export function convertGasDataToBN(gasData: GasPrice): GasPriceBN { + return Object.entries(gasData).reduce( + (acc, [key, value]) => ({ + ...acc, + [key]: new BigNumber(value) + }), + {} + ); +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/utils/decode-method.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/utils/decode-method.ts new file mode 100644 index 0000000..c26fce9 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/utils/decode-method.ts @@ -0,0 +1,58 @@ +import BigNumber from 'bignumber.js'; +import Web3 from 'web3'; +import { AbiItem } from 'web3-utils'; + +interface DecodedData { + name: string; + params: { name: string; value: string; type: string }[]; +} + +export class MethodDecoder { + public static decodeMethod(abiItem: AbiItem, data: string): DecodedData { + const abiCoder = new Web3().eth.abi; + const inputs = abiItem.inputs!; + const decoded = abiCoder.decodeParameters(inputs, data.slice(10)); + + const decodedData: DecodedData = { + name: abiItem.name!, + params: [] + }; + + for (let i = 0; i < decoded.__length__; i++) { + const param = decoded[i]; + let parsedParam = param; + const isUint = inputs[i]!.type.indexOf('uint') === 0; + const isInt = inputs[i]!.type.indexOf('int') === 0; + const isAddress = inputs[i]!.type.indexOf('address') === 0; + + if (isUint || isInt) { + const isArray = Array.isArray(param); + + if (isArray) { + parsedParam = param.map(val => new BigNumber(val).toFixed()); + } else { + parsedParam = new BigNumber(param).toFixed(); + } + } + + // Addresses returned by web3 are randomly cased so we need to standardize and lowercase all + if (isAddress) { + const isArray = Array.isArray(param); + + if (isArray) { + parsedParam = param.map(_ => _.toLowerCase()); + } else { + parsedParam = param.toLowerCase(); + } + } + + decodedData.params.push({ + name: inputs[i]!.name, + value: parsedParam, + type: inputs[i]!.type + }); + } + + return decodedData; + } +} diff --git a/SDK-mstr/src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data.ts b/SDK-mstr/src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data.ts new file mode 100644 index 0000000..40b41f6 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data.ts @@ -0,0 +1,47 @@ +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { EvmCrossChainTrade } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/models/gas-data'; +import { convertGasDataToBN } from 'src/features/cross-chain/calculation-manager/utils/convert-gas-price'; + +export const getCrossChainGasData: ( + trade: EvmCrossChainTrade, + receiverAddress?: string +) => Promise = async (trade: EvmCrossChainTrade, receiverAddress?: string) => { + try { + const fromBlockchain = trade.from.blockchain; + const walletAddress = + Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address; + if (!walletAddress) { + return null; + } + + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const tx = await trade.encode({ + receiverAddress: receiverAddress, + fromAddress: walletAddress, + useCacheData: true, + skipAmountCheck: true + }); + + const [gasLimit, gasDetails] = await Promise.all([ + web3Public.getEstimatedGasByData(walletAddress, tx.to!, { + data: tx.data!, + value: String(tx.value) + }), + Injector.gasPriceApi.getGasPrice(fromBlockchain).then(gas => convertGasDataToBN(gas)) + ]); + + if (!gasLimit?.isFinite()) { + return null; + } + + const increasedGasLimit = Web3Pure.calculateGasMargin(gasLimit, 1.2); + return { + gasLimit: increasedGasLimit, + ...gasDetails + }; + } catch { + return null; + } +}; diff --git a/SDK-mstr/src/features/cross-chain/cbridge-manager/cross-chain-cbridge-manager.ts b/SDK-mstr/src/features/cross-chain/cbridge-manager/cross-chain-cbridge-manager.ts new file mode 100644 index 0000000..4b2df8c --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/cbridge-manager/cross-chain-cbridge-manager.ts @@ -0,0 +1,120 @@ +import BigNumber from 'bignumber.js'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { Injector } from 'src/core/injector/injector'; +import { CbridgeCrossChainApiService } from 'src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-api-service'; +import { cbridgeContractAbi } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-contract-abi'; +import { cbridgeContractAddress } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-contract-address'; +import { CbridgeCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-supported-blockchains'; +import { + CbridgeStatusResponse, + TRANSFER_HISTORY_STATUS, + TRANSFER_HISTORY_STATUS_CODE +} from 'src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-status-response'; +import Web3 from 'web3'; +import { TransactionReceipt } from 'web3-eth'; + +export class CrossChainCbridgeManager { + public static async getTransferId( + sourceTransaction: string, + fromBlockchain: CbridgeCrossChainSupportedBlockchain + ): Promise { + const transactionRecipient = await Injector.web3PublicService + .getWeb3Public(fromBlockchain) + .getTransactionReceipt(sourceTransaction); + const transferLog = transactionRecipient.logs.find(log => + compareAddresses( + log.topics[0]!, + '0x89d8051e597ab4178a863a5190407b98abfeff406aa8db90c59af76612e58f01' + ) + )!; + const abiCoder = new Web3().eth.abi; + const inputs = cbridgeContractAbi.find( + abiItem => abiItem?.type === 'event' && abiItem?.name === 'Send' + )?.inputs; + const decodedParams = abiCoder.decodeParameters(inputs!, transferLog.data); + const { transferId } = decodedParams; + if (transferId.includes('0x')) { + return transferId.slice(2); + } + return transferId; + } + + public static async makeRefund( + fromBlockchain: CbridgeCrossChainSupportedBlockchain, + sourceTransaction: string, + estimateAmount: string, + onTransactionHash: (hash: string) => void + ): Promise { + try { + const useTestnet = BlockchainsInfo.isTestBlockchainName(fromBlockchain); + + const transferId = await CrossChainCbridgeManager.getTransferId( + sourceTransaction, + fromBlockchain + ); + const statusResponse = await CbridgeCrossChainApiService.fetchTradeStatus(transferId, { + useTestnet + }); + if ( + TRANSFER_HISTORY_STATUS_CODE[statusResponse.status] === + TRANSFER_HISTORY_STATUS.TRANSFER_TO_BE_REFUNDED + ) { + await CbridgeCrossChainApiService.withdrawLiquidity(transferId, estimateAmount, { + useTestnet + }); + await new Promise(resolve => setTimeout(resolve, 10_000)); + return CrossChainCbridgeManager.transferRefund( + fromBlockchain, + statusResponse, + onTransactionHash + ); + } + if ( + TRANSFER_HISTORY_STATUS_CODE[statusResponse.status] === + TRANSFER_HISTORY_STATUS.TRANSFER_REFUND_TO_BE_CONFIRMED + ) { + return CrossChainCbridgeManager.transferRefund( + fromBlockchain, + statusResponse, + onTransactionHash + ); + } + return null; + } catch (err) { + console.debug(err); + return null; + } + } + + private static async transferRefund( + fromBlockchain: CbridgeCrossChainSupportedBlockchain, + statusResponse: CbridgeStatusResponse, + onTransactionHash: (hash: string) => void + ): Promise { + const wdmsg = `0x${Buffer.from(statusResponse.wd_onchain!, 'base64').toString('hex')}`; + const sigs = statusResponse.sorted_sigs.map( + sign => `0x${Buffer.from(sign, 'base64').toString('hex')}` + ); + const signers = statusResponse.signers.map( + signer => `0x${Buffer.from(signer, 'base64').toString('hex')}` + ); + const powers = statusResponse.powers.map(power => { + const decodedPower = Buffer.from(power, 'base64').toString('hex'); + return new BigNumber(decodedPower, 16).toFixed(); + }); + + return Injector.web3PrivateService + .getWeb3Private(CHAIN_TYPE.EVM) + .tryExecuteContractMethod( + cbridgeContractAddress[fromBlockchain].providerRouter, + cbridgeContractAbi, + 'withdraw', + [wdmsg, sigs, signers, powers], + { + onTransactionHash + } + ); + } +} diff --git a/SDK-mstr/src/features/cross-chain/status-manager/constants/celer-cross-chain-event-statuses-abi.ts b/SDK-mstr/src/features/cross-chain/status-manager/constants/celer-cross-chain-event-statuses-abi.ts new file mode 100644 index 0000000..5ee1b1c --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/constants/celer-cross-chain-event-statuses-abi.ts @@ -0,0 +1,28 @@ +import { AbiItem } from 'web3-utils'; + +export const celerCrossChainEventStatusesAbi: AbiItem[] = [ + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'bytes32', name: 'id', type: 'bytes32' }, + { + components: [ + { internalType: 'address', name: 'srcInputToken', type: 'address' }, + { internalType: 'uint256', name: 'srcInputAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'dstChainID', type: 'uint256' }, + { internalType: 'address', name: 'dstOutputToken', type: 'address' }, + { internalType: 'uint256', name: 'dstMinOutputAmount', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'address', name: 'integrator', type: 'address' }, + { internalType: 'address', name: 'router', type: 'address' } + ], + indexed: false, + internalType: 'struct BridgeBase.BaseCrossChainParams', + name: 'parameters', + type: 'tuple' + } + ], + name: 'CrossChainRequestSent', + type: 'event' + } +]; diff --git a/SDK-mstr/src/features/cross-chain/status-manager/constants/multichain-status-mapping.ts b/SDK-mstr/src/features/cross-chain/status-manager/constants/multichain-status-mapping.ts new file mode 100644 index 0000000..96a8f30 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/constants/multichain-status-mapping.ts @@ -0,0 +1,16 @@ +import { + TX_STATUS, + TxStatus +} from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; + +type MultichainStatus = 0 | 3 | 8 | 9 | 10 | 12 | 14; + +export const MULTICHAIN_STATUS_MAPPING: Record = { + 0: TX_STATUS.PENDING, + 3: TX_STATUS.FAIL, + 8: TX_STATUS.PENDING, + 9: TX_STATUS.PENDING, + 10: TX_STATUS.SUCCESS, + 12: TX_STATUS.PENDING, + 14: TX_STATUS.FAIL +} as const; diff --git a/SDK-mstr/src/features/cross-chain/status-manager/cross-chain-status-manager.ts b/SDK-mstr/src/features/cross-chain/status-manager/cross-chain-status-manager.ts new file mode 100644 index 0000000..b6cd285 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/cross-chain-status-manager.ts @@ -0,0 +1,711 @@ +import { + L1ToL2MessageStatus, + L1TransactionReceipt, + L2ToL1MessageReader, + L2ToL1MessageStatus, + L2TransactionReceipt +} from '@arbitrum/sdk'; +import { JsonRpcProvider } from '@ethersproject/providers'; +import { RubicSdkError } from 'src/common/errors'; +import { + BLOCKCHAIN_NAME, + TEST_EVM_BLOCKCHAIN_NAME +} from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { + TX_STATUS, + TxStatus +} from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; +import { Injector } from 'src/core/injector/injector'; +import { DlnApiService } from 'src/features/common/providers/dln/dln-api-service'; +import { RANGO_SWAP_STATUS } from 'src/features/common/providers/rango/models/rango-api-status-types'; +import { RangoCommonParser } from 'src/features/common/providers/rango/services/rango-parser'; +import { XY_API_ENDPOINT } from 'src/features/common/providers/xy/constants/xy-api-params'; +import { TxStatusData } from 'src/features/common/status-manager/models/tx-status-data'; +import { getBridgersTradeStatus } from 'src/features/common/status-manager/utils/get-bridgers-trade-status'; +import { getSrcTxStatus } from 'src/features/common/status-manager/utils/get-src-tx-status'; +import { + CROSS_CHAIN_TRADE_TYPE, + CrossChainTradeType +} from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type'; +import { BridgersCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/constants/bridgers-cross-chain-supported-blockchain'; +import { CbridgeCrossChainApiService } from 'src/features/cross-chain/calculation-manager/providers/cbridge/cbridge-cross-chain-api-service'; +import { CbridgeCrossChainSupportedBlockchain } from 'src/features/cross-chain/calculation-manager/providers/cbridge/constants/cbridge-supported-blockchains'; +import { + TRANSFER_HISTORY_STATUS, + TRANSFER_HISTORY_STATUS_CODE, + XFER_STATUS, + XFER_STATUS_CODE +} from 'src/features/cross-chain/calculation-manager/providers/cbridge/models/cbridge-status-response'; +import { + LIFI_SWAP_STATUS, + LifiSwapStatus +} from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-swap-status'; +import { SquidrouterCrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/squidrouter-provider/squidrouter-cross-chain-provider'; +import { SYMBIOSIS_SWAP_STATUS } from 'src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-swap-status'; +import { CrossChainCbridgeManager } from 'src/features/cross-chain/cbridge-manager/cross-chain-cbridge-manager'; +import { MULTICHAIN_STATUS_MAPPING } from 'src/features/cross-chain/status-manager/constants/multichain-status-mapping'; +import { CHANGENOW_API_STATUS } from 'src/features/cross-chain/status-manager/models/changenow-api-response'; +import { CrossChainStatus } from 'src/features/cross-chain/status-manager/models/cross-chain-status'; +import { CrossChainTradeData } from 'src/features/cross-chain/status-manager/models/cross-chain-trade-data'; +import { MultichainStatusApiResponse } from 'src/features/cross-chain/status-manager/models/multichain-status-api-response'; +import { RubicBackendPsStatus } from 'src/features/cross-chain/status-manager/models/rubic-backend-ps-status'; +import { ScrollApiResponse } from 'src/features/cross-chain/status-manager/models/scroll-api-response'; +import { SquidrouterApiResponse } from 'src/features/cross-chain/status-manager/models/squidrouter-api-response'; +import { SQUIDROUTER_TRANSFER_STATUS } from 'src/features/cross-chain/status-manager/models/squidrouter-transfer-status.enum'; +import { + BtcStatusResponse, + DE_BRIDGE_API_STATE_STATUS, + GetDstTxDataFn, + SymbiosisApiResponse +} from 'src/features/cross-chain/status-manager/models/statuses-api'; +import { XyApiResponse } from 'src/features/cross-chain/status-manager/models/xy-api-response'; + +import { ChangeNowCrossChainApiService } from '../calculation-manager/providers/changenow-provider/services/changenow-cross-chain-api-service'; +import { MesonCcrApiService } from '../calculation-manager/providers/meson-provider/services/meson-cross-chain-api-service'; +import { OrbiterApiService } from '../calculation-manager/providers/orbiter-bridge/services/orbiter-api-service'; +import { RangoCrossChainApiService } from '../calculation-manager/providers/rango-provider/services/rango-cross-chain-api-service'; +import { TAIKO_API_STATUS, TaikoApiResponse } from './models/taiko-api-response'; + +/** + * Contains methods for getting cross-chain trade statuses. + */ +export class CrossChainStatusManager { + private readonly httpClient = Injector.httpClient; + + private readonly getDstTxStatusFnMap: Record = { + [CROSS_CHAIN_TRADE_TYPE.LIFI]: this.getLifiDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.SYMBIOSIS]: this.getSymbiosisDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.DEBRIDGE]: this.getDebridgeDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.BRIDGERS]: this.getBridgersDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.MULTICHAIN]: this.getMultichainDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.XY]: this.getXyDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.CELER_BRIDGE]: this.getCelerBridgeDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.CHANGENOW]: this.getChangenowDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.STARGATE]: this.getLayerZeroDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.ARBITRUM]: this.getArbitrumBridgeDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.SQUIDROUTER]: this.getSquidrouterDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.SCROLL_BRIDGE]: this.getScrollBridgeDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.TAIKO_BRIDGE]: this.getTaikoBridgeDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.RANGO]: this.getRangoDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.PULSE_CHAIN_BRIDGE]: this.getPulseChainDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.ORBITER_BRIDGE]: this.getOrbiterDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.LAYERZERO]: this.getLayerZeroDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.ARCHON_BRIDGE]: this.getLayerZeroDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.MESON]: this.getMesonDstSwapStatus + }; + + /** + * Returns cross-chain trade statuses on the source and target networks. + * The result consists of statuses of the source and target transactions and destination tx hash. + * @example + * ```ts + * const tradeData = { + * fromBlockchain: BLOCKCHAIN_NAME.FANTOM, + * toBlockchain: BLOCKCHAIN_NAME.BSC, + * txTimestamp: 1658241570024, + * srxTxHash: '0xd2263ca82ac0fce606cb75df27d7f0dc94909d41a58c37563bd6772496cb8924' + * }; + * const tradeType = CROSS_CHAIN_TRADE_TYPE.VIA; + * const crossChainStatus = await sdk.crossChainStatusManager.getCrossChainStatus(tradeData, tradeType); + * console.log('Source transaction status', crossChainStatus.srcTxStatus); + * console.log('Destination transaction status', crossChainStatus.dstTxStatus); + * console.log('Destination transaction hash', crossChainStatus.dstTxHash); + * ``` + * @param data Data needed to calculate statuses. + * @param tradeType Cross-chain trade type. + * @returns Object with transaction statuses and hash. + */ + public async getCrossChainStatus( + data: CrossChainTradeData, + tradeType: CrossChainTradeType + ): Promise { + const { fromBlockchain, srcTxHash } = data; + let srcTxStatus = await getSrcTxStatus(fromBlockchain, srcTxHash); + + const dstTxData = await this.getDstTxData(srcTxStatus, data, tradeType); + if (dstTxData.status === TX_STATUS.FAIL && srcTxStatus === TX_STATUS.PENDING) { + srcTxStatus = TX_STATUS.FAIL; + } + + return { + srcTxStatus, + dstTxStatus: dstTxData.status, + dstTxHash: dstTxData.hash, + ...(dstTxData.extraInfo && { extraInfo: dstTxData.extraInfo }) + }; + } + + /** + * Get destination transaction status and hash based on source transaction status, + * source transaction receipt, trade data and type. + * @param srcTxStatus Source transaction status. + * @param tradeData Trade data. + * @param tradeType Cross-chain trade type. + * @returns Cross-chain transaction status and hash. + */ + private async getDstTxData( + srcTxStatus: TxStatus, + tradeData: CrossChainTradeData, + tradeType: CrossChainTradeType + ): Promise { + if (srcTxStatus === TX_STATUS.FAIL) { + return { hash: null, status: TX_STATUS.FAIL }; + } + + if (srcTxStatus === TX_STATUS.PENDING) { + return { hash: null, status: TX_STATUS.PENDING }; + } + + const getDstTxStatusFn = this.getDstTxStatusFnMap[tradeType]; + if (!getDstTxStatusFn) { + throw new RubicSdkError('Unsupported cross chain provider'); + } + + return getDstTxStatusFn.call(this, tradeData); + } + + /** + * Get Stargate trade dst transaction status and hash. + * @param data Trade data. + * @returns Cross-chain transaction status and hash. + */ + private async getLayerZeroDstSwapStatus(data: CrossChainTradeData): Promise { + const lzPackage = await import('@layerzerolabs/scan-client'); + const client = lzPackage.createClient('mainnet'); + const scanResponse = await client.getMessagesBySrcTxHash(data.srcTxHash); + const targetTrade = scanResponse.messages.find( + item => item.srcTxHash.toLocaleLowerCase() === data.srcTxHash.toLocaleLowerCase() + ); + const txStatusData: TxStatusData = { + status: TX_STATUS.PENDING, + hash: null + }; + + if (targetTrade?.dstTxHash) { + txStatusData.hash = targetTrade.dstTxHash; + } + + if (targetTrade?.status === 'DELIVERED') { + txStatusData.status = TX_STATUS.SUCCESS; + } + + if (targetTrade?.status === 'INFLIGHT') { + txStatusData.status = TX_STATUS.PENDING; + } + + if (targetTrade?.status === 'FAILED') { + txStatusData.status = TX_STATUS.FAIL; + } + + return txStatusData; + } + + /** + * Get Symbiosis trade dst transaction status and hash. + * @param data Trade data. + * @returns Cross-chain transaction status and hash. + */ + private async getSymbiosisDstSwapStatus(data: CrossChainTradeData): Promise { + const symbiosisTxIndexingTimeSpent = Date.now() > data.txTimestamp + 30000; + const symbiosisApi = Object.keys(TEST_EVM_BLOCKCHAIN_NAME).includes(data.fromBlockchain) + ? 'api.testnet' + : 'api-v2'; + + if (symbiosisTxIndexingTimeSpent) { + try { + const srcChainId = blockchainId[data.fromBlockchain]; + const toBlockchainId = blockchainId[data.toBlockchain]; + const { + status: { text: dstTxStatus }, + tx, + transitTokenSent + } = await Injector.httpClient.get( + `https://${symbiosisApi}.symbiosis.finance/crosschain/v1/tx/${srcChainId}/${data.srcTxHash}` + ); + + let dstTxData: TxStatusData = { + status: TX_STATUS.PENDING, + hash: tx?.hash || null + }; + + const targetTokenNetwork = tx?.chainId; + + if ( + dstTxStatus === SYMBIOSIS_SWAP_STATUS.PENDING || + dstTxStatus === SYMBIOSIS_SWAP_STATUS.NOT_FOUND + ) { + return { ...dstTxData, status: TX_STATUS.PENDING }; + } + + if (dstTxStatus === SYMBIOSIS_SWAP_STATUS.STUCKED) { + return { ...dstTxData, status: TX_STATUS.REVERT }; + } + + if (dstTxStatus === SYMBIOSIS_SWAP_STATUS.REVERTED || transitTokenSent) { + return { ...dstTxData, status: TX_STATUS.FALLBACK }; + } + + if ( + dstTxStatus === SYMBIOSIS_SWAP_STATUS.SUCCESS && + targetTokenNetwork === toBlockchainId + ) { + if (data.toBlockchain !== BLOCKCHAIN_NAME.BITCOIN) { + dstTxData.status = TX_STATUS.SUCCESS; + } else { + dstTxData = await this.getBitcoinStatus(tx!.hash); + } + } + + return dstTxData; + } catch (error) { + console.debug('[Symbiosis Trade] Error retrieving dst tx status', error); + return { + status: TX_STATUS.PENDING, + hash: null + }; + } + } + + return { + status: TX_STATUS.PENDING, + hash: null + }; + } + + /** + * Get Li-fi trade dst transaction status and hash. + * @param data Trade data. + * @returns Cross-chain transaction status and hash. + */ + private async getLifiDstSwapStatus(data: CrossChainTradeData): Promise { + try { + const params = { + ...(data.lifiBridgeType && { bridge: data.lifiBridgeType }), + fromChain: blockchainId[data.fromBlockchain], + toChain: blockchainId[data.toBlockchain], + txHash: data.srcTxHash + }; + const { status, receiving } = await Injector.httpClient.get<{ + status: LifiSwapStatus; + receiving: { txHash: string }; + }>('https://li.quest/v1/status', { params }); + const dstTxData: TxStatusData = { + status: TX_STATUS.UNKNOWN, + hash: receiving?.txHash || null + }; + + if (status === LIFI_SWAP_STATUS.DONE) { + dstTxData.status = TX_STATUS.SUCCESS; + } + + if (status === LIFI_SWAP_STATUS.FAILED) { + dstTxData.status = TX_STATUS.FAIL; + } + + if (status === LIFI_SWAP_STATUS.INVALID) { + dstTxData.status = TX_STATUS.UNKNOWN; + } + + if (status === LIFI_SWAP_STATUS.NOT_FOUND || status === LIFI_SWAP_STATUS.PENDING) { + dstTxData.status = TX_STATUS.PENDING; + } + + return dstTxData; + } catch (error) { + console.debug('[Li-fi Trade] error retrieving tx status', error); + return { + status: TX_STATUS.PENDING, + hash: null + }; + } + } + + /** + * Get DeBridge trade dst transaction status. + * @param data Trade data. + * @returns Cross-chain transaction status and hash. + */ + private async getDebridgeDstSwapStatus(data: CrossChainTradeData): Promise { + try { + const { orderIds } = await DlnApiService.fetchCrossChainOrdersByHash(data.srcTxHash); + + if (!orderIds.length) { + return { + status: TX_STATUS.PENDING, + hash: null + }; + } + + const orderId = orderIds[0]; + const dstTxData: TxStatusData = { + status: TX_STATUS.PENDING, + hash: null + }; + + const { status } = await DlnApiService.fetchCrossChainStatus(orderId); + + if ( + status === DE_BRIDGE_API_STATE_STATUS.FULFILLED || + status === DE_BRIDGE_API_STATE_STATUS.SENTUNLOCK || + status === DE_BRIDGE_API_STATE_STATUS.CLAIMEDUNLOCK + ) { + const { fulfilledDstEventMetadata } = + await DlnApiService.fetchCrossChainEventMetaData(orderId); + + dstTxData.hash = fulfilledDstEventMetadata.transactionHash.stringValue; + dstTxData.status = TX_STATUS.SUCCESS; + } else if ( + status === DE_BRIDGE_API_STATE_STATUS.ORDERCANCELLED || + status === DE_BRIDGE_API_STATE_STATUS.SENTORDERCANCEL || + status === DE_BRIDGE_API_STATE_STATUS.CLAIMEDORDERCANCEL + ) { + dstTxData.status = TX_STATUS.FAIL; + } + + return dstTxData; + } catch { + return { + status: TX_STATUS.PENDING, + hash: null + }; + } + } + + /** + * Get Bridgers trade dst transaction status. + * @param data Trade data. + * @returns Cross-chain transaction status. + */ + private getBridgersDstSwapStatus(data: CrossChainTradeData): Promise { + return getBridgersTradeStatus( + data.srcTxHash, + data.fromBlockchain as BridgersCrossChainSupportedBlockchain, + 'rubic' + ); + } + + /** + * @internal + * Get transaction status in bitcoin network; + * @param hash Bitcoin transaction hash. + */ + private async getBitcoinStatus(hash: string): Promise { + let bitcoinTransactionStatus: BtcStatusResponse; + const dstTxData: TxStatusData = { + status: TX_STATUS.PENDING, + hash: null + }; + try { + const btcStatusApi = 'https://blockchain.info/rawtx/'; + bitcoinTransactionStatus = await this.httpClient.get( + `${btcStatusApi}${hash}` + ); + dstTxData.hash = bitcoinTransactionStatus?.hash || null; + } catch { + return { + status: TX_STATUS.PENDING, + hash: null + }; + } + + const isCompleted = bitcoinTransactionStatus?.block_index !== undefined; + if (isCompleted) { + dstTxData.status = TX_STATUS.SUCCESS; + } + + return dstTxData; + } + + private async getMultichainDstSwapStatus(data: CrossChainTradeData): Promise { + try { + const { + info: { status, swaptx } + } = await this.httpClient.get( + `https://bridgeapi.anyswap.exchange/v2/history/details?params=${data.srcTxHash}` + ); + + return { + status: MULTICHAIN_STATUS_MAPPING?.[status] || TX_STATUS.PENDING, + hash: swaptx || null + }; + } catch { + return { + status: TX_STATUS.PENDING, + hash: null + }; + } + } + + private async getXyDstSwapStatus(data: CrossChainTradeData): Promise { + try { + const { success, tx } = await this.httpClient.get( + `${XY_API_ENDPOINT}/crossChainStatus?srcChainId=${ + blockchainId[data.fromBlockchain] + }&srcTxHash=${data.srcTxHash}` + ); + + if (success && tx) { + return { status: TX_STATUS.SUCCESS, hash: tx }; + } + return { status: TX_STATUS.PENDING, hash: null }; + } catch { + return { status: TX_STATUS.PENDING, hash: null }; + } + } + + private async getCelerBridgeDstSwapStatus(data: CrossChainTradeData): Promise { + try { + const transferId = await CrossChainCbridgeManager.getTransferId( + data.srcTxHash, + data.fromBlockchain as CbridgeCrossChainSupportedBlockchain + ); + const useTestnet = BlockchainsInfo.isTestBlockchainName(data.fromBlockchain); + const swapData = await CbridgeCrossChainApiService.fetchTradeStatus(transferId, { + useTestnet + }); + + const transformedStatus = TRANSFER_HISTORY_STATUS_CODE[swapData.status as number]; + + switch (transformedStatus) { + case TRANSFER_HISTORY_STATUS.TRANSFER_UNKNOWN: + case TRANSFER_HISTORY_STATUS.TRANSFER_SUBMITTING: + case TRANSFER_HISTORY_STATUS.TRANSFER_WAITING_FOR_SGN_CONFIRMATION: + case TRANSFER_HISTORY_STATUS.TRANSFER_REQUESTING_REFUND: + case TRANSFER_HISTORY_STATUS.TRANSFER_CONFIRMING_YOUR_REFUND: + default: + return { status: TX_STATUS.PENDING, hash: null }; + case TRANSFER_HISTORY_STATUS.TRANSFER_REFUNDED: + case TRANSFER_HISTORY_STATUS.TRANSFER_COMPLETED: + return { + status: TX_STATUS.SUCCESS, + hash: swapData.dst_block_tx_link.split('/').at(-1)! + }; + case TRANSFER_HISTORY_STATUS.TRANSFER_FAILED: + return { + status: TX_STATUS.FAIL, + hash: null + }; + case TRANSFER_HISTORY_STATUS.TRANSFER_WAITING_FOR_FUND_RELEASE: + case TRANSFER_HISTORY_STATUS.TRANSFER_TO_BE_REFUNDED: + return XFER_STATUS_CODE[swapData.refund_reason] === XFER_STATUS.OK_TO_RELAY + ? { + status: TX_STATUS.PENDING, + hash: null + } + : { + status: TX_STATUS.REVERT, + hash: null + }; + } + } catch { + return { status: TX_STATUS.PENDING, hash: null }; + } + } + + private async getChangenowDstSwapStatus(data: CrossChainTradeData): Promise { + if (!data.changenowId) { + throw new RubicSdkError('Must provide changenow trade id'); + } + try { + const { status, payoutHash } = await ChangeNowCrossChainApiService.getTxStatus( + data.changenowId + ); + + if ( + status === CHANGENOW_API_STATUS.FINISHED || + status === CHANGENOW_API_STATUS.REFUNDED + ) { + return { status: TX_STATUS.SUCCESS, hash: payoutHash }; + } + if (status === CHANGENOW_API_STATUS.FAILED) { + return { status: TX_STATUS.FAIL, hash: null }; + } + return { status: TX_STATUS.PENDING, hash: null }; + } catch { + return { status: TX_STATUS.PENDING, hash: null }; + } + } + + public async getArbitrumBridgeDstSwapStatus(data: CrossChainTradeData): Promise { + const rpcProviders = Injector.web3PublicService.rpcProvider; + const l1Provider = new JsonRpcProvider( + rpcProviders[BLOCKCHAIN_NAME.ETHEREUM]!.rpcList[0]!, + 1 + ); + const l2Provider = new JsonRpcProvider( + rpcProviders[BLOCKCHAIN_NAME.ARBITRUM]!.rpcList[0]!, + 42161 + ); + // L1 to L2 deposit + if (data.fromBlockchain === BLOCKCHAIN_NAME.ETHEREUM) { + try { + const sourceTx = await l1Provider.getTransactionReceipt(data.srcTxHash); + const l1TxReceipt = new L1TransactionReceipt(sourceTx); + + const [l1ToL2Msg] = await l1TxReceipt.getL1ToL2Messages(l2Provider); + const response = await l1ToL2Msg!.getSuccessfulRedeem(); + + switch (response.status) { + case L1ToL2MessageStatus.FUNDS_DEPOSITED_ON_L2: + return { status: TX_STATUS.REVERT, hash: null }; + case L1ToL2MessageStatus.EXPIRED: + case L1ToL2MessageStatus.CREATION_FAILED: + return { status: TX_STATUS.FAIL, hash: null }; + case L1ToL2MessageStatus.REDEEMED: + return { + status: TX_STATUS.SUCCESS, + hash: response.l2TxReceipt.transactionHash + }; + case L1ToL2MessageStatus.NOT_YET_CREATED: + default: + return { status: TX_STATUS.PENDING, hash: null }; + } + } catch { + return { status: TX_STATUS.PENDING, hash: null }; + } + } + // L2 to L1 withdraw + try { + const targetReceipt = await l2Provider.getTransactionReceipt(data.srcTxHash); + const l2TxReceipt = new L2TransactionReceipt(targetReceipt); + const [event] = l2TxReceipt.getL2ToL1Events(); + if (!event) { + return { status: TX_STATUS.PENDING, hash: null }; + } + + const messageReader = new L2ToL1MessageReader(l1Provider, event); + + const status = await messageReader.status(l2Provider); + switch (status) { + case L2ToL1MessageStatus.CONFIRMED: + return { status: TX_STATUS.READY_TO_CLAIM, hash: null }; + case L2ToL1MessageStatus.EXECUTED: + return { status: TX_STATUS.SUCCESS, hash: null }; + case L2ToL1MessageStatus.UNCONFIRMED: + default: + return { status: TX_STATUS.PENDING, hash: null }; + } + } catch (error) { + return { status: TX_STATUS.PENDING, hash: null }; + } + } + + private async getSquidrouterDstSwapStatus(data: CrossChainTradeData): Promise { + try { + const { status, toChain } = await this.httpClient.get( + `${SquidrouterCrossChainProvider.apiEndpoint}status?transactionId=${data.srcTxHash}`, + { headers: { 'x-integrator-id': 'rubic-api' } } + ); + + if ( + status === SQUIDROUTER_TRANSFER_STATUS.DEST_EXECUTED || + status === SQUIDROUTER_TRANSFER_STATUS.EXPRESS_EXECUTED + ) { + return { status: TX_STATUS.SUCCESS, hash: toChain!.transactionId! }; + } + + if (status === SQUIDROUTER_TRANSFER_STATUS.DEST_ERROR) { + return { status: TX_STATUS.FAIL, hash: null }; + } + + return { status: TX_STATUS.PENDING, hash: null }; + } catch { + return { status: TX_STATUS.PENDING, hash: null }; + } + } + + public async getScrollBridgeDstSwapStatus(data: CrossChainTradeData): Promise { + const response = await Injector.httpClient.post( + 'https://alpha-api.scroll.io/bridgehistory/api/txsbyhashes', + { + txs: [data.srcTxHash] + } + ); + const sourceTx = response!.data!.result[0]!; + const targetHash = sourceTx?.finalizeTx?.hash; + if (targetHash) { + return { status: TX_STATUS.SUCCESS, hash: targetHash }; + } + + return { status: TX_STATUS.PENDING, hash: null }; + } + + public async getTaikoBridgeDstSwapStatus(data: CrossChainTradeData): Promise { + if (!data.taikoTransactionId) { + throw new RubicSdkError('Must provide Taiko transaction ID'); + } + if (!data.sender) { + throw new RubicSdkError('Must specify sender account'); + } + const { items } = await Injector.httpClient.get( + `https://relayer.katla.taiko.xyz/events?address=${data.sender}&msgHash=${data.taikoTransactionId}&event=MessageSent` + ); + + if (!items[0]) { + throw new RubicSdkError('Taiko Relayer did not find transaction with such ID'); + } + + const { status, data: taikoData } = items[0]; + + if (status === TAIKO_API_STATUS.DONE) { + return { status: TX_STATUS.SUCCESS, hash: taikoData.Raw.transactionHash }; + } + + return { status: TX_STATUS.PENDING, hash: null }; + } + + public async getPulseChainDstSwapStatus(data: CrossChainTradeData): Promise { + try { + const network = + data.fromBlockchain === BLOCKCHAIN_NAME.ETHEREUM ? 'ethereum' : 'ethereum'; + const result = await Injector.httpClient.get( + `https://api.rubic.exchange/api/v2/trades/crosschain/pulsechain_bridge_status?tx_hash=${data.srcTxHash}&network=${network}` + ); + + if (result.status === 'SUCCESS') { + return { status: TX_STATUS.SUCCESS, hash: result.dest_transaction }; + } + return { status: TX_STATUS.PENDING, hash: null }; + } catch { + return { status: TX_STATUS.PENDING, hash: null }; + } + } + + private async getRangoDstSwapStatus(data: CrossChainTradeData): Promise { + if (!data.rangoRequestId) { + throw new RubicSdkError('Must provide rangoRequestId'); + } + const { srcTxHash, rangoRequestId } = data; + const params = RangoCommonParser.getTxStatusQueryParams(srcTxHash, rangoRequestId!); + + const { bridgeData, status: txStatus } = await RangoCrossChainApiService.getTxStatus( + params + ); + + let status: TxStatus; + + if (txStatus === RANGO_SWAP_STATUS.SUCCESS) { + status = TX_STATUS.SUCCESS; + } else if (txStatus === RANGO_SWAP_STATUS.RUNNING) { + status = TX_STATUS.PENDING; + } else { + status = TX_STATUS.FAIL; + } + + const hash = bridgeData!.destTxHash; + + return { hash, status }; + } + + private async getOrbiterDstSwapStatus(data: CrossChainTradeData): Promise { + const txStatusData = await OrbiterApiService.getTxStatus(data.srcTxHash); + + return txStatusData; + } + + private async getMesonDstSwapStatus(data: CrossChainTradeData): Promise { + return MesonCcrApiService.fetchTxStatus(data.srcTxHash); + } +} diff --git a/SDK-mstr/src/features/cross-chain/status-manager/models/celer-transfer-status.enum.ts b/SDK-mstr/src/features/cross-chain/status-manager/models/celer-transfer-status.enum.ts new file mode 100644 index 0000000..12bb8dc --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/models/celer-transfer-status.enum.ts @@ -0,0 +1,12 @@ +export const CELER_TRANSFER_STATUS = { + XS_UNKNOWN: 'XS_UNKNOWN', + XS_WAITING_FOR_SGN_CONFIRMATION: 'XS_WAITING_FOR_SGN_CONFIRMATION', + XS_WAITING_FOR_FUND_RELEASE: 'XS_WAITING_FOR_FUND_RELEASE', + XS_COMPLETED: 'XS_COMPLETED', + XS_TO_BE_REFUND: 'XS_TO_BE_REFUND', + XS_REFUND_TO_BE_CONFIRMED: 'XS_REFUND_TO_BE_CONFIRMED', + XS_REFUNDED: 'XS_REFUNDED' +} as const; + +export type CelerTransferStatus = + (typeof CELER_TRANSFER_STATUS)[keyof typeof CELER_TRANSFER_STATUS]; diff --git a/SDK-mstr/src/features/cross-chain/status-manager/models/changenow-api-response.ts b/SDK-mstr/src/features/cross-chain/status-manager/models/changenow-api-response.ts new file mode 100644 index 0000000..0315c58 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/models/changenow-api-response.ts @@ -0,0 +1,18 @@ +export const CHANGENOW_API_STATUS = { + NEW: 'new', + WAITING: 'waiting', + CONFIRMING: 'confirming', + EXCHANGING: 'exchanging', + SENDING: 'sending', + FINISHED: 'finished', + FAILED: 'failed', + REFUNDED: 'refunded', + VERIFYING: 'verifying' +} as const; + +export type ChangenowApiStatus = (typeof CHANGENOW_API_STATUS)[keyof typeof CHANGENOW_API_STATUS]; + +export interface ChangenowStatusResponse { + status: ChangenowApiStatus; + payoutHash: string | null; +} diff --git a/SDK-mstr/src/features/cross-chain/status-manager/models/cross-chain-status.ts b/SDK-mstr/src/features/cross-chain/status-manager/models/cross-chain-status.ts new file mode 100644 index 0000000..06efeaa --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/models/cross-chain-status.ts @@ -0,0 +1,27 @@ +import { TxStatus } from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; + +/** + * Object representing status of cross-chain trade. + * Consists of source transaction status, destination transaction status and destination transaction hash. + */ +export interface CrossChainStatus { + /** + * Status of source transaction. + */ + srcTxStatus: TxStatus; + + /** + * Status of destination transaction. + */ + dstTxStatus: TxStatus; + + /** + * Transaction hash on destination chain. + */ + dstTxHash: string | null; + + /* Contains additional info for specific providers */ + extraInfo?: { + mesonSwapId: string; + }; +} diff --git a/SDK-mstr/src/features/cross-chain/status-manager/models/cross-chain-trade-data.ts b/SDK-mstr/src/features/cross-chain/status-manager/models/cross-chain-trade-data.ts new file mode 100644 index 0000000..95d3b1c --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/models/cross-chain-trade-data.ts @@ -0,0 +1,62 @@ +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3PublicSupportedBlockchain } from 'src/core/blockchain/web3-public-service/models/web3-public-storage'; + +/** + * Data required to obtain status of cross-chain trade. + */ +export interface CrossChainTradeData { + /** + * Source blockchain. + */ + fromBlockchain: Web3PublicSupportedBlockchain; + + /** + * Destination blockchain. + */ + toBlockchain: BlockchainName; + + /** + * Trade timestamp. + */ + txTimestamp: number; + + /** + * Source transaction hash. + */ + srcTxHash: string; + + /** + * Sender address. + */ + sender?: string; + + /** + * Li-fi bridge type. + */ + lifiBridgeType?: string; + + /** + * Via action uuid. + */ + viaUuid?: string; + + /** + * Rango request id. + */ + rangoRequestId?: string; + + /** + * Celer bridge transaction ID. + */ + celerTransactionId?: string; + + /** + * Changenow trade id. + */ + changenowId?: string; + + /** + * Taiko bridge transaction ID. + */ + taikoTransactionId?: string; +} diff --git a/SDK-mstr/src/features/cross-chain/status-manager/models/multichain-status-api-response.ts b/SDK-mstr/src/features/cross-chain/status-manager/models/multichain-status-api-response.ts new file mode 100644 index 0000000..bacc3a7 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/models/multichain-status-api-response.ts @@ -0,0 +1,27 @@ +import { MULTICHAIN_STATUS_MAPPING } from 'src/features/cross-chain/status-manager/constants/multichain-status-mapping'; + +interface Info { + bind: string; + confirmations: string; + formatfee: number; + formatswapvalue: number; + formatvalue: number; + from: string; + fromChainID: string; + historyType: string; + label: string; + pairid: string; + status: keyof typeof MULTICHAIN_STATUS_MAPPING; + statusmsg: string; + swapinfo: unknown; + swaptx: string; + time: number; + timestamp: number; + toChainID: string; + txid: string; +} + +export interface MultichainStatusApiResponse { + info: Partial> & Required>; + msg: string; +} diff --git a/SDK-mstr/src/features/cross-chain/status-manager/models/rubic-backend-ps-status.ts b/SDK-mstr/src/features/cross-chain/status-manager/models/rubic-backend-ps-status.ts new file mode 100644 index 0000000..3dc137b --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/models/rubic-backend-ps-status.ts @@ -0,0 +1,6 @@ +export type RubicBackendPsStatus = + | { + status: 'SUCCESS'; + dest_transaction: string; + } + | { status: 'PENDING' }; diff --git a/SDK-mstr/src/features/cross-chain/status-manager/models/scroll-api-response.ts b/SDK-mstr/src/features/cross-chain/status-manager/models/scroll-api-response.ts new file mode 100644 index 0000000..b88135a --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/models/scroll-api-response.ts @@ -0,0 +1,18 @@ +interface BaseScrollTransaction { + readonly amount: string; + readonly blockNumber: number; + readonly blockTimestamp: string; + readonly hash: string; + readonly isL1: boolean; + readonly to: string; +} + +interface ApiScrollTransaction extends BaseScrollTransaction { + readonly finalizeTx?: BaseScrollTransaction; +} + +export interface ScrollApiResponse { + data: { + readonly result: (ApiScrollTransaction | null)[]; + }; +} diff --git a/SDK-mstr/src/features/cross-chain/status-manager/models/squidrouter-api-response.ts b/SDK-mstr/src/features/cross-chain/status-manager/models/squidrouter-api-response.ts new file mode 100644 index 0000000..0867666 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/models/squidrouter-api-response.ts @@ -0,0 +1,18 @@ +import { SquidrouterTransferStatus } from 'src/features/cross-chain/status-manager/models/squidrouter-transfer-status.enum'; + +export interface SquidrouterApiResponse { + readonly id: string; + readonly status: SquidrouterTransferStatus; + readonly toChain: { + readonly transactionId?: string; + }; + readonly error: + | { + readonly message: string; + readonly txHash: string; + } + | { + readonly errorType: string; + readonly message: string; + }[]; +} diff --git a/SDK-mstr/src/features/cross-chain/status-manager/models/squidrouter-transfer-status.enum.ts b/SDK-mstr/src/features/cross-chain/status-manager/models/squidrouter-transfer-status.enum.ts new file mode 100644 index 0000000..f593058 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/models/squidrouter-transfer-status.enum.ts @@ -0,0 +1,11 @@ +export const SQUIDROUTER_TRANSFER_STATUS = { + SRC_GATEWAY_CALLED: 'source_gateway_called', + DEST_GATEWAY_APPROVED: 'destination_gateway_approved', + DEST_EXECUTED: 'destination_executed', + EXPRESS_EXECUTED: 'express_executed', + DEST_ERROR: 'error', + ERROR_FETCHING_STATUS: 'error_fetching_status' +} as const; + +export type SquidrouterTransferStatus = + (typeof SQUIDROUTER_TRANSFER_STATUS)[keyof typeof SQUIDROUTER_TRANSFER_STATUS]; diff --git a/SDK-mstr/src/features/cross-chain/status-manager/models/statuses-api.ts b/SDK-mstr/src/features/cross-chain/status-manager/models/statuses-api.ts new file mode 100644 index 0000000..062afef --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/models/statuses-api.ts @@ -0,0 +1,78 @@ +import { TxStatusData } from 'src/features/common/status-manager/models/tx-status-data'; +import { SymbiosisToken } from 'src/features/cross-chain/calculation-manager/providers/symbiosis-provider/models/symbiosis-trade-data'; +import { CelerTransferStatus } from 'src/features/cross-chain/status-manager/models/celer-transfer-status.enum'; +import { CrossChainTradeData } from 'src/features/cross-chain/status-manager/models/cross-chain-trade-data'; + +export interface DeBridgeFilteredListApiResponse { + orderIds: [string]; +} + +export interface DeBridgeOrderApiStatusResponse { + orderId: string; + status: DeBridgeApiStateStatus; +} + +export interface DeBridgeOrderApiResponse { + fulfilledDstEventMetadata: { + transactionHash: { + stringValue: string; + }; + }; +} + +export const DE_BRIDGE_API_STATE_STATUS = { + FULFILLED: 'Fulfilled', + SENTUNLOCK: 'SentUnlock', + CLAIMEDUNLOCK: 'ClaimedUnlock', + ORDERCANCELLED: 'OrderCancelled', + SENTORDERCANCEL: 'SentOrderCancel', + CLAIMEDORDERCANCEL: 'ClaimedOrderCancel', + CREATED: 'Created' +} as const; + +export type DeBridgeApiStateStatus = + (typeof DE_BRIDGE_API_STATE_STATUS)[keyof typeof DE_BRIDGE_API_STATE_STATUS]; + +export interface SymbiosisApiResponse { + status: { + code: string; + text: string; + }; + tx: { + hash: string; + chainId: number; + } | null; + transitTokenSent: SymbiosisToken | null; +} + +export interface BtcStatusResponse { + block_height: number | undefined; + block_index: number | undefined; + double_spend: boolean; + fee: number; + hash: string; + inputs: unknown[]; + lock_time: number; + out: unknown[]; + relayed_by: string; + size: number; + time: number; + tx_index: number; + ver: number; + vin_sz: number; + vout_sz: number; + weight: number; +} + +export interface CelerXtransferStatusResponse { + err: string; + txSearchInfo: { + transfer: { + xfer_id: string; + dst_tx_hash: string; + xfer_status: CelerTransferStatus; + }[]; + }[]; +} + +export type GetDstTxDataFn = (data: CrossChainTradeData) => Promise; diff --git a/SDK-mstr/src/features/cross-chain/status-manager/models/taiko-api-response.ts b/SDK-mstr/src/features/cross-chain/status-manager/models/taiko-api-response.ts new file mode 100644 index 0000000..abc26a0 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/models/taiko-api-response.ts @@ -0,0 +1,34 @@ +export const TAIKO_API_EVENT_TYPE = { + SEND_ETH: 0, + SEND_ERC20: 1 +} as const; + +export type TaikoApiEventType = (typeof TAIKO_API_EVENT_TYPE)[keyof typeof TAIKO_API_EVENT_TYPE]; + +export const TAIKO_API_STATUS = { + NEW: 0, + RETRIABLE: 1, + DONE: 2, + FAILED: 3 +}; + +export type TaikoApiStatus = (typeof TAIKO_API_STATUS)[keyof typeof TAIKO_API_STATUS]; + +interface TaikoTransaction { + readonly id: number; + readonly name: string; + readonly status: TaikoApiStatus; + readonly eventType: TaikoApiEventType; + readonly msgHash: string; + readonly data: TaikoData; +} + +interface TaikoData { + readonly Raw: { + readonly transactionHash: string; + }; +} + +export interface TaikoApiResponse { + readonly items: TaikoTransaction[]; +} diff --git a/SDK-mstr/src/features/cross-chain/status-manager/models/xy-api-response.ts b/SDK-mstr/src/features/cross-chain/status-manager/models/xy-api-response.ts new file mode 100644 index 0000000..16ba71f --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/status-manager/models/xy-api-response.ts @@ -0,0 +1,13 @@ +export interface XyApiResponse { + success: boolean; + msg: string; + status: + | 'Done' + | 'Processing' + | 'Not Found' + | 'Receive bridge token' + | 'Receive synapse bridge token' + | 'Pending refund' + | 'Refunded'; + tx: string | null; +} diff --git a/SDK-mstr/src/features/cross-chain/symbiosis-manager/cross-chain-symbiosis-manager.ts b/SDK-mstr/src/features/cross-chain/symbiosis-manager/cross-chain-symbiosis-manager.ts new file mode 100644 index 0000000..6428927 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/symbiosis-manager/cross-chain-symbiosis-manager.ts @@ -0,0 +1,88 @@ +import { TransactionRequest } from '@ethersproject/providers'; +import { RubicSdkError } from 'src/common/errors'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { Injector } from 'src/core/injector/injector'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { SymbiosisRevertResponse } from 'src/features/cross-chain/symbiosis-manager/models/symbiosis-revert-api'; +import { SymbiosisStuckedResponse } from 'src/features/cross-chain/symbiosis-manager/models/symbiosis-stucked-api'; +import { TransactionReceipt } from 'web3-eth'; + +export class CrossChainSymbiosisManager { + private get web3Private(): EvmWeb3Private { + return Injector.web3PrivateService.getWeb3Private(CHAIN_TYPE.EVM); + } + + private get walletAddress(): string { + return this.web3Private.address; + } + + public async getUserTrades(fromAddress?: string): Promise { + fromAddress ||= this.walletAddress; + if (!fromAddress) { + throw new RubicSdkError('`fromAddress` parameter or wallet address must not be empty'); + } + + return this.getSymbiosisStuckedTrades(fromAddress); + } + + private getSymbiosisStuckedTrades(fromAddress: string): Promise { + return Injector.httpClient + .get( + `https://api-v2.symbiosis.finance/crosschain/v1/stucked/${fromAddress}` + ) + .then(response => response.filter(trade => Boolean(trade.hash))) + .catch(() => []); + } + + public async revertTrade( + revertTransactionHash: string, + options: SwapTransactionOptions = {} + ): Promise { + const stuckedTrades = await this.getUserTrades(); + const stuckedTrade = stuckedTrades.find( + trade => trade.hash.toLowerCase() === revertTransactionHash.toLowerCase() + ); + if (!stuckedTrade) { + throw new RubicSdkError('No request with provided transaction hash'); + } + + const transactionRequest = await this.getRevertTransactionRequest(stuckedTrade); + + const blockchain = Object.entries(blockchainId).find( + ([_, id]) => id === stuckedTrade.chainId + )![0] as BlockchainName; + await this.web3Private.checkBlockchainCorrect(blockchain); + + const { onConfirm, gasLimit, gasPriceOptions } = options; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + }; + + return this.web3Private.trySendTransaction(transactionRequest.to!, { + data: transactionRequest.data!.toString(), + value: transactionRequest.value?.toString() || '0', + onTransactionHash, + gas: gasLimit, + gasPriceOptions + }); + } + + private async getRevertTransactionRequest( + stuckedTrade: SymbiosisStuckedResponse + ): Promise { + return ( + await Injector.httpClient.post( + `https://api-v2.symbiosis.finance/crosschain/v1/revert`, + { + transactionHash: stuckedTrade.hash, + chainId: stuckedTrade.chainId + } + ) + ).tx; + } +} diff --git a/SDK-mstr/src/features/cross-chain/symbiosis-manager/models/symbiosis-revert-api.ts b/SDK-mstr/src/features/cross-chain/symbiosis-manager/models/symbiosis-revert-api.ts new file mode 100644 index 0000000..2e73a7f --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/symbiosis-manager/models/symbiosis-revert-api.ts @@ -0,0 +1,5 @@ +import { TransactionRequest } from '@ethersproject/abstract-provider'; + +export type SymbiosisRevertResponse = { + tx: TransactionRequest; +}; diff --git a/SDK-mstr/src/features/cross-chain/symbiosis-manager/models/symbiosis-stucked-api.ts b/SDK-mstr/src/features/cross-chain/symbiosis-manager/models/symbiosis-stucked-api.ts new file mode 100644 index 0000000..e067dd8 --- /dev/null +++ b/SDK-mstr/src/features/cross-chain/symbiosis-manager/models/symbiosis-stucked-api.ts @@ -0,0 +1,4 @@ +export type SymbiosisStuckedResponse = { + hash: string; + chainId: number; +}; diff --git a/SDK-mstr/src/features/deflation-token-manager/constants/simulator-contract-abi.ts b/SDK-mstr/src/features/deflation-token-manager/constants/simulator-contract-abi.ts new file mode 100644 index 0000000..23695e3 --- /dev/null +++ b/SDK-mstr/src/features/deflation-token-manager/constants/simulator-contract-abi.ts @@ -0,0 +1,15 @@ +import { AbiItem } from 'web3-utils'; + +export const simulatorContractAbi: AbiItem[] = [ + { + inputs: [ + { internalType: 'address', name: '_dex', type: 'address' }, + { internalType: 'address', name: '_checkToken', type: 'address' }, + { internalType: 'bytes', name: '_data', type: 'bytes' } + ], + name: 'simulateTransferWithSwap', + outputs: [], + stateMutability: 'payable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/deflation-token-manager/constants/simulator-contract-address.ts b/SDK-mstr/src/features/deflation-token-manager/constants/simulator-contract-address.ts new file mode 100644 index 0000000..1cd62dc --- /dev/null +++ b/SDK-mstr/src/features/deflation-token-manager/constants/simulator-contract-address.ts @@ -0,0 +1,21 @@ +import { + BLOCKCHAIN_NAME, + EVM_BLOCKCHAIN_NAME, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; + +export const simulatorContractAddress: Record = Object.values( + EVM_BLOCKCHAIN_NAME +).reduce((acc, blockchain) => { + let contractAddress = '0x32d43423E6f2293729303fB56C52f853f5683333'; + if (blockchain === BLOCKCHAIN_NAME.POLYGON) { + contractAddress = '0xf746908a3eb1a6a16cab7cb40bbe47b897b2ebcb'; + } + if (blockchain === BLOCKCHAIN_NAME.CELO) { + contractAddress = '0xf5454E6Da76E2af9824b8D88F2Af103159A396aA'; + } + return { + ...acc, + [blockchain]: contractAddress + }; +}, {} as Record); diff --git a/SDK-mstr/src/features/deflation-token-manager/deflation-token-manager.ts b/SDK-mstr/src/features/deflation-token-manager/deflation-token-manager.ts new file mode 100644 index 0000000..1184541 --- /dev/null +++ b/SDK-mstr/src/features/deflation-token-manager/deflation-token-manager.ts @@ -0,0 +1,182 @@ +import BigNumber from 'bignumber.js'; +import { BigNumber as EthersBigNumber } from 'ethers'; +import { DeflationTokenError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, Token } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { TokenBaseStruct } from 'src/common/tokens/models/token-base-struct'; +import { Cache } from 'src/common/utils/decorators'; +import { notNull } from 'src/common/utils/object'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EVM_BLOCKCHAIN_NAME, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { + DeflationManagerSupportedBlockchain, + deflationManagerSupportedBlockchains +} from 'src/features/deflation-token-manager/models/deflation-manager-supported-blockchain'; +import { IsDeflationToken } from 'src/features/deflation-token-manager/models/is-deflation-token'; +import { UniswapV2TradeProviders } from 'src/features/on-chain/calculation-manager/constants/trade-providers/uniswap-v2-trade-providers'; + +import { UniswapV2AbstractTrade } from '../on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { simulatorContractAbi } from './constants/simulator-contract-abi'; +import { simulatorContractAddress } from './constants/simulator-contract-address'; + +const DEADLINE = 9999999999; +const SIMULATOR_CALLER = '0x0000000000000000000000000000000000000001'; +const ERROR_SELECTOR = '0x02f19474'; + +const NATIVE_TOKEN_AMOUNT: Record = Object.values( + EVM_BLOCKCHAIN_NAME +).reduce((acc, blockchain) => { + let tokenAmount = 0.5; + if (blockchain === BLOCKCHAIN_NAME.POLYGON || blockchain === BLOCKCHAIN_NAME.AVALANCHE) { + tokenAmount = 10; + } else if (blockchain === BLOCKCHAIN_NAME.FANTOM) { + tokenAmount = 230; + } + return { + ...acc, + [blockchain]: tokenAmount + }; +}, {} as Record); + +/** + * Contains method to check token for deflation. + */ +export class DeflationTokenManager { + public static isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is DeflationManagerSupportedBlockchain { + return deflationManagerSupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === blockchain + ); + } + + public async checkToken(token: Token): Promise { + const isDeflationToken = await this.isDeflationToken(token); + if (isDeflationToken.isDeflation) { + throw new DeflationTokenError(token, isDeflationToken.percent); + } + } + + @Cache + public async isDeflationToken(token: Token): Promise { + if ( + !DeflationTokenManager.isSupportedBlockchain(token.blockchain) || + EvmWeb3Pure.isNativeAddress(token.address) + ) { + return { isDeflation: false }; + } + const evmToken = new Token({ + ...token, + blockchain: token.blockchain as EvmBlockchainName + }); + + const bestTrade = await this.findUniswapV2Trade(evmToken); + if (!bestTrade) { + return { isDeflation: false }; + } + + try { + await this.simulateTransferWithSwap(bestTrade, evmToken); + } catch (error) { + if (error?.data?.includes(ERROR_SELECTOR)) { + return this.parseError(error.data); + } + } + return { isDeflation: false }; + } + + private async findUniswapV2Trade( + evmToken: Token + ): Promise { + const uniswapV2Providers = UniswapV2TradeProviders.map(ProviderClass => { + const provider = new ProviderClass(); + return provider.blockchain === evmToken.blockchain ? provider : null; + }).filter(notNull); + + const nativeToken = nativeTokensList[evmToken.blockchain] as Token; + const from = new PriceTokenAmount({ + ...nativeToken, + price: new BigNumber(NaN), + tokenAmount: new BigNumber(NATIVE_TOKEN_AMOUNT[evmToken.blockchain]) + }); + const to = new PriceToken({ + ...evmToken, + price: new BigNumber(NaN) + }); + + const uniswapV2Trades = await Promise.allSettled( + uniswapV2Providers.map(uniswapV2Provider => + uniswapV2Provider.calculate(from, to, { + slippageTolerance: 1, + deadlineMinutes: DEADLINE, + gasCalculation: 'disabled' + }) + ) + ); + return uniswapV2Trades + .map(trade => (trade.status === 'fulfilled' ? trade.value : null)) + .filter(notNull)[0]; + } + + private async simulateTransferWithSwap( + uniswapV2Trade: UniswapV2AbstractTrade, + token: TokenBaseStruct + ): Promise { + const { data } = await uniswapV2Trade.encodeDirect({ + fromAddress: SIMULATOR_CALLER, + receiverAddress: simulatorContractAddress[token.blockchain], + supportFee: true + }); + + const web3Public = Injector.web3PublicService.getWeb3Public(token.blockchain); + const simulatorAddress = simulatorContractAddress[token.blockchain]; + const value = Web3Pure.toWei(NATIVE_TOKEN_AMOUNT[token.blockchain]); + + await web3Public.staticCallContractMethod( + simulatorAddress, + simulatorContractAbi, + 'simulateTransferWithSwap', + [uniswapV2Trade.dexContractAddress, token.address, data], + { + value, + from: SIMULATOR_CALLER + } + ); + } + + private parseError(errorData: string): IsDeflationToken { + const decoded = EvmWeb3Pure.decodeData<{ + isWhitelisted: boolean; + amountReceived: EthersBigNumber; + amountExpected: EthersBigNumber; + }>( + 'AmntReceived_AmntExpected_TransferSwap', + [ + ['bool', 'isWhitelisted'], + ['uint256', 'amountReceived'], + ['uint256', 'amountExpected'] + ], + errorData + ); + + const received = new BigNumber(decoded.amountReceived.toHexString()); + const expected = new BigNumber(decoded.amountExpected.toHexString()); + const percent = new BigNumber(1).minus(received.dividedBy(expected)).multipliedBy(100); + + if (percent.eq(0)) { + return { isDeflation: false }; + } + return { + isDeflation: true, + percent, + isWhitelisted: decoded.isWhitelisted + }; + } +} diff --git a/SDK-mstr/src/features/deflation-token-manager/models/deflation-manager-supported-blockchain.ts b/SDK-mstr/src/features/deflation-token-manager/models/deflation-manager-supported-blockchain.ts new file mode 100644 index 0000000..b6cb3d8 --- /dev/null +++ b/SDK-mstr/src/features/deflation-token-manager/models/deflation-manager-supported-blockchain.ts @@ -0,0 +1,27 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const deflationManagerSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.MOONRIVER, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.AURORA, + BLOCKCHAIN_NAME.TELOS, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.CRONOS, + BLOCKCHAIN_NAME.OKE_X_CHAIN, + BLOCKCHAIN_NAME.GNOSIS, + BLOCKCHAIN_NAME.FUSE, + BLOCKCHAIN_NAME.MOONBEAM, + BLOCKCHAIN_NAME.CELO, + BLOCKCHAIN_NAME.BOBA, + BLOCKCHAIN_NAME.KAVA, + BLOCKCHAIN_NAME.BITGERT, + BLOCKCHAIN_NAME.METIS +] as const; + +export type DeflationManagerSupportedBlockchain = + (typeof deflationManagerSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/deflation-token-manager/models/is-deflation-token.ts b/SDK-mstr/src/features/deflation-token-manager/models/is-deflation-token.ts new file mode 100644 index 0000000..043088a --- /dev/null +++ b/SDK-mstr/src/features/deflation-token-manager/models/is-deflation-token.ts @@ -0,0 +1,11 @@ +import BigNumber from 'bignumber.js'; + +export type IsDeflationToken = + | { + isDeflation: false; + } + | { + isDeflation: true; + percent: BigNumber; + isWhitelisted: boolean; + }; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/algebra-trade-providers.ts b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/algebra-trade-providers.ts new file mode 100644 index 0000000..5d5dd90 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/algebra-trade-providers.ts @@ -0,0 +1,20 @@ +import { CamelotArbitrumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/camelot-arbitrum-provider'; +import { BerachainTestnetAlgebraProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/berachain-testnet-algebra-provider'; +import { BlastFenixProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/blast-fenix-provider'; +import { ModeAlgebraProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/mode-algebra-provider'; +import { AlgebraProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/algebra-provider'; +import { QuickSwapV3Provider } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/quick-swap-v3-provider'; +import { QuickSwapV3PolygonZKEVMProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/quick-swap-v3-provider'; + +import { AlgebraIntegralProvider } from '../../providers/dexes/arthera-testnet/algebra-integral/algebra-integral-provider'; + +export const AlgebraTradeProviders = [ + AlgebraProvider, + AlgebraIntegralProvider, + QuickSwapV3Provider, + QuickSwapV3PolygonZKEVMProvider, + CamelotArbitrumProvider, + BerachainTestnetAlgebraProvider, + ModeAlgebraProvider, + BlastFenixProvider +] as const; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/bridgers-trade-providers.ts b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/bridgers-trade-providers.ts new file mode 100644 index 0000000..d0558e0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/bridgers-trade-providers.ts @@ -0,0 +1,3 @@ +import { BridgersProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/tron/bridgers/bridgers-provider'; + +export const BridgersTradeProviders = [BridgersProvider] as const; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/curve-trade-providers.ts b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/curve-trade-providers.ts new file mode 100644 index 0000000..f14f908 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/curve-trade-providers.ts @@ -0,0 +1,23 @@ +import { CurveArbitrumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/curve-arbitrum/curve-arbitrum-provider'; +import { CurveAvalancheProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/curve-avalanche/curve-avalanche-provider'; +import { CurveCeloProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/celo/curve-celo/curve-celo-provider'; +import { CurveEthereumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/curve-ethereum/curve-ethereum-provider'; +import { CurveFantomProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/curve-fantom/curve-fantom-provider'; +import { CurveGnosisProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/gnosis/curve-gnosis/curve-gnosis-provider'; +import { CurveKavaProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/curve-kava/curve-kava-provider'; +import { CurveMoonbeamProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/moonbeam/curve-moonbeam/curve-moonbeam-provider'; +import { CurveOptimismProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/optimism/curve-optimism/curve-optimism-provider'; +import { CurvePolygonProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/curve-polygon/curve-polygon-provider'; + +export const CurveTradeProviders = [ + CurveArbitrumProvider, + CurveAvalancheProvider, + CurveCeloProvider, + CurveEthereumProvider, + CurveFantomProvider, + CurveKavaProvider, + CurveGnosisProvider, + CurveMoonbeamProvider, + CurveOptimismProvider, + CurvePolygonProvider +] as const; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/izumi-trade-providers.ts b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/izumi-trade-providers.ts new file mode 100644 index 0000000..d3bacb9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/izumi-trade-providers.ts @@ -0,0 +1,40 @@ +import { IzumiBaseProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/base/izumi-base/izumi-base-provider'; +import { IzumiBlastProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/blast/izumi-blast/izumi-blast-provider'; +import { IzumiBscProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/izumi-bsc/izumi-bsc-provider'; +import { IzumiLineaProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/linea/izumi-linea/izumi-linea-provider'; +import { IzumiMantaPacificProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/manta-pacific/izumi-manta-pacific/izumi-manta-pacific-provider'; +import { IzumiMantleProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/mantle/izumi-mantle/izumi-mantle-provider'; +import { MerlinSwapMerlinProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/merlin/merlin-swap/merlin-swap-merlin-provider'; +import { IzumiModeProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/mode/izumi-mode/izumi-mode-provider'; +import { IzumiScrollProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/scroll/izumi-scroll/izumi-scroll-provider'; +import { IzumiTaikoProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/taiko/izumi-taiko/izumi-taiko-provider'; +import { IzumiXlayerProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/xlayer/izumi-xlayer/izumi-xlayer-provider'; +import { IzumiZetachainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/zetachain/izumi-zetachain/izumi-zetachain-provider'; +import { IzumiZkfairProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/zkfair/izumi-zkfair/izumi-zkfair-provider'; +import { IzumiZkLinkProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/zklink/izumi-zklink/izumi-zklink-provider'; +import { IzumiZksyncProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/zksync/izumi-zksync/izumi-zksync-provider'; + +import { IzumiArbitrumProvider } from '../../providers/dexes/arbitrum/izumi-arbitrum/izumi-arbitrum-provider'; +import { IzumiKromaProvider } from '../../providers/dexes/kroma/izumi-kroma/izumi-kroma-provider'; + +export const izumiTradeProviders = [ + IzumiBscProvider, + IzumiZksyncProvider, + IzumiMantleProvider, + IzumiBaseProvider, + IzumiLineaProvider, + IzumiMantaPacificProvider, + IzumiZetachainProvider, + IzumiKromaProvider, + IzumiArbitrumProvider, + MerlinSwapMerlinProvider, + IzumiZkfairProvider, + IzumiZkLinkProvider, + IzumiModeProvider, + IzumiTaikoProvider, + // IzumiBlastProvider + IzumiBlastProvider, + // IzumiMerlinProvider, + IzumiScrollProvider, + IzumiXlayerProvider +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/pancake-router-providers.ts b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/pancake-router-providers.ts new file mode 100644 index 0000000..a32e8bb --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/pancake-router-providers.ts @@ -0,0 +1,8 @@ +import { PancakeRouterBscProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-router-bsc/pancake-router-bsc-provider'; +import { PancakeRouterEthereumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/pancake-router-ethereum/pancake-router-ethereum-provider'; + +export const pancakeRouterProviders = [ + PancakeRouterBscProvider, + PancakeRouterEthereumProvider + // PancakeRouterPolygonZkEvmProvider +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/sync-swap-trade-providers.ts b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/sync-swap-trade-providers.ts new file mode 100644 index 0000000..ed19007 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/sync-swap-trade-providers.ts @@ -0,0 +1,9 @@ +import { LineaSyncSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/linea/sync-swap/linea-sync-swap-provider'; +import { ScrollSyncSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/scroll/sync-swap/scroll-sync-swap-provider'; +import { ZkSyncSyncSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/zksync/sync-swap/zksync-sync-swap-provider'; + +export const syncSwapTradeProviders = [ + ZkSyncSyncSwapProvider, + LineaSyncSwapProvider, + ScrollSyncSwapProvider +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/typed-trade-providers.ts b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/typed-trade-providers.ts new file mode 100644 index 0000000..1243d4f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/typed-trade-providers.ts @@ -0,0 +1,37 @@ +import { Mutable } from 'src/common/utils/types'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { AlgebraTradeProviders } from 'src/features/on-chain/calculation-manager/constants/trade-providers/algebra-trade-providers'; +import { BridgersTradeProviders } from 'src/features/on-chain/calculation-manager/constants/trade-providers/bridgers-trade-providers'; +import { izumiTradeProviders } from 'src/features/on-chain/calculation-manager/constants/trade-providers/izumi-trade-providers'; +import { pancakeRouterProviders } from 'src/features/on-chain/calculation-manager/constants/trade-providers/pancake-router-providers'; +import { syncSwapTradeProviders } from 'src/features/on-chain/calculation-manager/constants/trade-providers/sync-swap-trade-providers'; +import { UniswapV2TradeProviders } from 'src/features/on-chain/calculation-manager/constants/trade-providers/uniswap-v2-trade-providers'; +import { UniswapV3TradeProviders } from 'src/features/on-chain/calculation-manager/constants/trade-providers/uniswap-v3-trade-providers'; +import { OnChainTypedTradeProviders } from 'src/features/on-chain/calculation-manager/models/on-chain-typed-trade-provider'; + +import { vooiTradeProviders } from './vooi-trade-providers'; + +export const typedTradeProviders: OnChainTypedTradeProviders = [ + ...UniswapV2TradeProviders, + ...UniswapV3TradeProviders, + ...AlgebraTradeProviders, + ...BridgersTradeProviders, + // ...CurveTradeProviders, Removed because hack + ...pancakeRouterProviders, + ...izumiTradeProviders, + ...vooiTradeProviders, + ...syncSwapTradeProviders +].reduce( + (acc, ProviderClass) => { + const provider = new ProviderClass(); + acc[provider.blockchain][provider.type] = provider; + return acc; + }, + Object.values(BLOCKCHAIN_NAME).reduce( + (acc, blockchain) => ({ + ...acc, + [blockchain]: {} + }), + {} as Mutable + ) +); diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/uniswap-v2-trade-providers.ts b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/uniswap-v2-trade-providers.ts new file mode 100644 index 0000000..d4120b1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/uniswap-v2-trade-providers.ts @@ -0,0 +1,155 @@ +import { SushiSwapArbitrumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/sushi-swap-arbitrum/sushi-swap-arbitrum-provider'; +import { ArthSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/astar-evm/arth-swap/arth-swap-provider'; +import { TrisolarisAuroraProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/aurora/trisolaris-aurora/trisolaris-aurora-provider'; +import { WannaSwapAuroraProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/aurora/wanna-swap-aurora/wanna-swap-aurora-provider'; +import { JoeProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/joe-provider'; +import { PangolinProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/pangolin-provider'; +import { SushiSwapAvalancheProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/sushi-swap-avalanche-provider'; +import { AerodromeProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-provider'; +import { BaseSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/base/base-swap/base-swap-provider'; +import { UniSwapV2BlastProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/blast/uni-swap-v2-blast/uni-swap-v2-blast-provider'; +import { OolongSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/boba/oolong-swap/oolong-swap-provider'; +import { SushiSwapBscProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/sushi-swap-bsc-provider'; +import { PancakeSwapTestnetProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/bsct/pancake-swap-testnet/pancake-swap-testnet-provider'; +import { TraderJoeBsctProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/bsct/trader-joe-bsct/trader-joe-bsct-provider'; +import { CroSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/cro-swap/cro-swap-provider'; +import { CrodexProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/crodex/crodex-provider'; +import { CronaSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/crona-swap/crona-swap-provider'; +import { SushiSwapEthereumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/sushi-swap-ethereum-provider'; +import { UniSwapV2EthereumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/uni-swap-v2-ethereum-provider'; +import { VerseProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/verse/verse-provider'; +import { SushiSwapEthereumPowProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/sushi-swap-ethereum-pow/sushi-swap-ethereum-pow-provider'; +import { UniSwapV2EthereumPowProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v2-ethereum-pow/uni-swap-v2-ethereum-pow-provider'; +import { SoulSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/soul-swap/soul-swap-provider'; +import { SpiritSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/spirit-swap-provider'; +import { SpookySwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/spooky-swap-provider'; +import { SushiSwapFantomProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/sushi-swap-fantom-provider'; +import { JoeFujiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/fuji/joe-fuji/joe-fuji-provider'; +import { PangolinFujiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/fuji/pangolin-fuji/pangolin-fuji-provider'; +import { UniSwapV2GoerliProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/goerli/uni-swap-v2-goerli/uni-swap-v2-goerli-provider'; +import { SushiSwapHarmonyProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/sushi-swap-harmony-provider'; +import { TradeHarmonySwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/trader-harmony/trader-harmony-provider'; +import { ViperSwapHarmonyProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/viper-swap-harmony/viper-swap-harmony-provider'; +import { AscentHorizenEonProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/ascent-horizon-eon/ascent-horizen-eon-provider'; +import { SpookySwapHorizenEonProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/spooky-swap-horizen-eon/spooky-swap-horizen-eon-provider'; +import { ElkProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/elk/elk-provider'; +import { JupiterSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/jupiter-swap/jupiter-swap-provider'; +import { PhotonSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/photon-swap/photon-swap-provider'; +import { SurfdexProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/surfdex/surfdex-provider'; +import { ClaimSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/klaytn/claim-swap/claim-swap-provider'; +import { NetSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/metis/net-swap/net-swap-provider'; +import { EddyFinanceModeProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/mode/eddy-finance-mode/eddy-finance-mode-provider'; +import { SolarbeamProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/solarbeam-provider'; +import { SushiSwapMoonriverProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/sushi-swap-moonriver-provider'; +import { QuickSwapMumbaiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/mumbai/quick-swap-mumbai/quick-swap-mumbai-provider'; +import { YuzuSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/oasis/yuzu-swap/yuzu-swap-provider'; +import { QuickSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/quick-swap-provider'; +import { SushiSwapPolygonProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/sushi-swap-polygon-provider'; +import { PulseXV1Provider } from 'src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v1/pulsex-v1-provider'; +import { PulseXV2Provider } from 'src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v2/pulsex-v2-provider'; +import { PegasysProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/syscoin/pegasys/pegasys-provider'; +import { ApeSwapTelosProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/ape-swap/ape-swap-telos-provider'; +import { OmnidexProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/omnidex/omnidex-provider'; +import { SushiSwapTelosProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/sushi-swap-telos/sushi-swap-telos-provider'; +import { ZappyProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/zappy/trisolaris-aurora-provider'; +import { AstroSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/velas/astro-swap/astro-swap-provider'; +import { WagyuSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/velas/wagyu-swap/wagyu-swap-provider'; +import { EddyFinanceProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/zetachain/eddy-finance/eddy-finance-provider'; +import { MuteSwapProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/mute-swap-provider'; + +import { SushiSwapZetachainProvider } from '../../providers/dexes/zetachain/sushi-swap-zetachain/sushi-swap-zetachain-provider'; + +export const UniswapV2TradeProviders = [ + // ethereum + UniSwapV2EthereumProvider, + SushiSwapEthereumProvider, + VerseProvider, + // bsc + SushiSwapBscProvider, + // polygon + QuickSwapProvider, + SushiSwapPolygonProvider, + // avalanche + JoeProvider, + PangolinProvider, + SushiSwapAvalancheProvider, + // moonriver + SolarbeamProvider, + SushiSwapMoonriverProvider, + // fantom + SpiritSwapProvider, + SpookySwapProvider, + SoulSwapProvider, + SushiSwapFantomProvider, + // harmony + SushiSwapHarmonyProvider, + ViperSwapHarmonyProvider, + TradeHarmonySwapProvider, + // arbitrum + SushiSwapArbitrumProvider, + // aurora + TrisolarisAuroraProvider, + WannaSwapAuroraProvider, + // telos + SushiSwapTelosProvider, + ZappyProvider, + ApeSwapTelosProvider, + OmnidexProvider, + // Boba + OolongSwapProvider, + // Ethereum PoW + UniSwapV2EthereumPowProvider, + SushiSwapEthereumPowProvider, + // Kava + JupiterSwapProvider, + PhotonSwapProvider, + ElkProvider, + SurfdexProvider, + // Oasis + YuzuSwapProvider, + // Metis + NetSwapProvider, + // Klaytn + ClaimSwapProvider, + // Velas + WagyuSwapProvider, + AstroSwapProvider, + // Syscoin + PegasysProvider, + // Cronos + CronaSwapProvider, + CroSwapProvider, + CrodexProvider, + // Astar EVM + ArthSwapProvider, + // ZkSync + MuteSwapProvider, + // Pulsechain + PulseXV1Provider, + PulseXV2Provider, + // Base + BaseSwapProvider, + AerodromeProvider, + // BSC Testnet + PancakeSwapTestnetProvider, + TraderJoeBsctProvider, + // Goerli + UniSwapV2GoerliProvider, + // Mumbai + QuickSwapMumbaiProvider, + // Fuji + JoeFujiProvider, + PangolinFujiProvider, + // Scroll + // UniSwapV2ScrollSepoliaProvider + // horizen eon + SpookySwapHorizenEonProvider, + AscentHorizenEonProvider, + // Blast + UniSwapV2BlastProvider, + // ZetaChain + EddyFinanceProvider, + SushiSwapZetachainProvider, + // Mode + EddyFinanceModeProvider +] as const; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/uniswap-v3-trade-providers.ts b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/uniswap-v3-trade-providers.ts new file mode 100644 index 0000000..c6f9fc4 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/uniswap-v3-trade-providers.ts @@ -0,0 +1,16 @@ +import { UniSwapV3ArbitrumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-provider'; +import { UniSwapV3EthereumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/uni-swap-v3-ethereum-provider'; +import { UniSwapV3EthereumPowProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-provider'; +import { FusionXProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/fusionx-provider'; +import { UniSwapV3PolygonProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-provider'; +import { UniSwapV3ScrollSepoliaProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/uni-swap-v3-scroll-sepolia-provider'; + +export const UniswapV3TradeProviders = [ + UniSwapV3EthereumProvider, + UniSwapV3PolygonProvider, + UniSwapV3ArbitrumProvider, + UniSwapV3EthereumPowProvider, + // HorizondexProvider, // disabled due to risk of hacking + FusionXProvider, + UniSwapV3ScrollSepoliaProvider +] as const; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/vooi-trade-providers.ts b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/vooi-trade-providers.ts new file mode 100644 index 0000000..aa0803c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/constants/trade-providers/vooi-trade-providers.ts @@ -0,0 +1,3 @@ +import { VooiLineaProvider } from '../../providers/dexes/linea/vooi/vooi-provider'; + +export const vooiTradeProviders = [VooiLineaProvider]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-manager-aggregators-types.ts b/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-manager-aggregators-types.ts new file mode 100644 index 0000000..060eeba --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-manager-aggregators-types.ts @@ -0,0 +1,29 @@ +import { OneInchProvider } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/one-inch-provider'; +import { DlnOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/dln-on-chain-provider'; +import { PiteasProvider } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-provider'; +import { XyDexProvider } from 'src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/xy-dex-provider'; +import { ZrxProvider } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-provider'; + +import { LifiProvider } from '../providers/aggregators/lifi/lifi-provider'; +import { NativeRouterProvider } from '../providers/aggregators/native-router/native-router-provider'; +import { OdosOnChainProvider } from '../providers/aggregators/odos/odos-on-chain-provider'; +import { OkuSwapOnChainProvider } from '../providers/aggregators/okuswap/okuswap-on-chain-provider'; +import { OpenOceanProvider } from '../providers/aggregators/open-ocean/open-ocean-provider'; +import { RangoOnChainProvider } from '../providers/aggregators/rango/rango-on-chain-provider'; +import { ZetaSwapProvider } from '../providers/aggregators/zetaswap/zetaswap-provider'; + +export const AGGREGATORS_ON_CHAIN = { + LIFI: LifiProvider, + OPEN_OCEAN: OpenOceanProvider, + RANGO: RangoOnChainProvider, + ODOS: OdosOnChainProvider, + DLN: DlnOnChainProvider, + OKU_SWAP: OkuSwapOnChainProvider, + PITEAS: PiteasProvider, + XY: XyDexProvider, + ZRX: ZrxProvider, + ONE_INCH: OneInchProvider, + ZETA_SWAP: ZetaSwapProvider, + NATIVE_ROUTER: NativeRouterProvider + // SYMBIOSIS: SymbiosisOnChainProvider +} as const; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-manager-calculation-options.ts b/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-manager-calculation-options.ts new file mode 100644 index 0000000..9bff2d2 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-manager-calculation-options.ts @@ -0,0 +1,7 @@ +import { OnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +export interface OnChainManagerCalculationOptions extends OnChainCalculationOptions { + readonly timeout?: number; + readonly disabledProviders?: OnChainTradeType[]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-reactively-calculated-trade-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-reactively-calculated-trade-data.ts new file mode 100644 index 0000000..739603a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-reactively-calculated-trade-data.ts @@ -0,0 +1,21 @@ +import { WrappedOnChainTradeOrNull } from 'src/features/on-chain/calculation-manager/models/wrapped-on-chain-trade-or-null'; + +/** + * On-chain providers data. + */ +export interface OnChainReactivelyCalculatedTradeData { + /** + * Total amount of providers to calculate. + */ + total: number; + + /** + * Calculated amount of providers at current moment. + */ + calculated: number; + + /** + * Last calculated trade. + */ + wrappedTrade: WrappedOnChainTradeOrNull; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-trade-error.ts b/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-trade-error.ts new file mode 100644 index 0000000..0c16cb0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-trade-error.ts @@ -0,0 +1,7 @@ +import { RubicSdkError } from 'src/common/errors'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +export interface OnChainTradeError { + type: OnChainTradeType; + error: RubicSdkError; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-typed-trade-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-typed-trade-provider.ts new file mode 100644 index 0000000..e6da405 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/models/on-chain-typed-trade-provider.ts @@ -0,0 +1,10 @@ +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { OnChainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/on-chain-provider'; + +/** + * Record of on-chain trades types and their corresponding instant trade providers. + */ +export type OnChainTypedTradeProviders = Readonly< + Record>> +>; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/models/required-on-chain-manager-calculation-options.ts b/SDK-mstr/src/features/on-chain/calculation-manager/models/required-on-chain-manager-calculation-options.ts new file mode 100644 index 0000000..2727e36 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/models/required-on-chain-manager-calculation-options.ts @@ -0,0 +1,7 @@ +import { OnChainManagerCalculationOptions } from 'src/features/on-chain/calculation-manager/models/on-chain-manager-calculation-options'; +import { MarkRequired } from 'ts-essentials'; + +export type RequiredOnChainManagerCalculationOptions = MarkRequired< + OnChainManagerCalculationOptions, + 'timeout' | 'disabledProviders' | 'providerAddress' | 'useProxy' | 'withDeflation' +>; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/models/wrapped-on-chain-trade-or-null.ts b/SDK-mstr/src/features/on-chain/calculation-manager/models/wrapped-on-chain-trade-or-null.ts new file mode 100644 index 0000000..ceee829 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/models/wrapped-on-chain-trade-or-null.ts @@ -0,0 +1,22 @@ +import { RubicSdkError } from 'src/common/errors'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { OnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade'; + +export type WrappedOnChainTradeOrNull = { + /** + * Calculated cross-chain trade. + * Sometimes trade can be calculated even if error was thrown. + * Equals `null` in case error is critical and trade cannot be calculated. + */ + trade: OnChainTrade | null; + + /** + * Type of calculated trade. + */ + tradeType: OnChainTradeType; + + /** + * Error, thrown during calculation. + */ + error?: RubicSdkError; +} | null; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/on-chain-manager.ts b/SDK-mstr/src/features/on-chain/calculation-manager/on-chain-manager.ts new file mode 100644 index 0000000..3eb445c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/on-chain-manager.ts @@ -0,0 +1,466 @@ +import { forkJoin, from, map, merge, Observable, of, startWith, switchMap } from 'rxjs'; +import { fromPromise } from 'rxjs/internal/observable/innerFrom'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, Token } from 'src/common/tokens'; +import { notNull } from 'src/common/utils/object'; +import { combineOptions } from 'src/common/utils/options'; +import pTimeout from 'src/common/utils/p-timeout'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { ProviderAddress } from 'src/core/sdk/models/provider-address'; +import { getPriceTokensFromInputTokens } from 'src/features/common/utils/get-price-tokens-from-input-tokens'; +import { defaultProviderAddresses } from 'src/features/cross-chain/calculation-manager/constants/default-provider-addresses'; +import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider'; +import { DeflationTokenManager } from 'src/features/deflation-token-manager/deflation-token-manager'; +import { IsDeflationToken } from 'src/features/deflation-token-manager/models/is-deflation-token'; +import { typedTradeProviders } from 'src/features/on-chain/calculation-manager/constants/trade-providers/typed-trade-providers'; +import { OnChainManagerCalculationOptions } from 'src/features/on-chain/calculation-manager/models/on-chain-manager-calculation-options'; +import { OnChainReactivelyCalculatedTradeData } from 'src/features/on-chain/calculation-manager/models/on-chain-reactively-calculated-trade-data'; +import { OnChainTradeError } from 'src/features/on-chain/calculation-manager/models/on-chain-trade-error'; +import { OnChainTypedTradeProviders } from 'src/features/on-chain/calculation-manager/models/on-chain-typed-trade-provider'; +import { RequiredOnChainManagerCalculationOptions } from 'src/features/on-chain/calculation-manager/models/required-on-chain-manager-calculation-options'; +import { WrappedOnChainTradeOrNull } from 'src/features/on-chain/calculation-manager/models/wrapped-on-chain-trade-or-null'; +import { LifiCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-calculation-options'; +import { EvmWrapTrade } from 'src/features/on-chain/calculation-manager/providers/common/evm-wrap-trade/evm-wrap-trade'; +import { + OnChainCalculationOptions, + RequiredOnChainCalculationOptions +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { OnChainProxyService } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-proxy-service/on-chain-proxy-service'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { OnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade'; +import { OnChainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/on-chain-provider'; + +import { AGGREGATORS_ON_CHAIN } from './models/on-chain-manager-aggregators-types'; +import { AggregatorOnChainProvider } from './providers/common/on-chain-aggregator/aggregator-on-chain-provider-abstract'; + +/** + * Contains methods to calculate on-chain trades. + */ +export class OnChainManager { + private static readonly defaultCalculationTimeout = 20_000; + + /** + * List of all on-chain trade providers, combined by blockchains. + */ + private readonly tradeProviders: OnChainTypedTradeProviders = typedTradeProviders; + + private readonly deflationTokenManager = new DeflationTokenManager(); + + private readonly AGGREGATORS = AGGREGATORS_ON_CHAIN; + + private readonly LIFI_DISABLED_PROVIDERS: OnChainTradeType[] = []; + + public constructor(private readonly providerAddress: ProviderAddress) {} + + public calculateTradeReactively( + fromToken: + | Token + | { + address: string; + blockchain: BlockchainName; + } + | PriceToken, + fromAmount: string | number, + toToken: Token | string | PriceToken, + options?: OnChainManagerCalculationOptions + ): Observable { + if (toToken instanceof Token && fromToken.blockchain !== toToken.blockchain) { + throw new RubicSdkError('Blockchains of from and to tokens must be same'); + } + + return from(getPriceTokensFromInputTokens(fromToken, fromAmount.toString(), toToken)).pipe( + switchMap(({ from, to }) => + forkJoin([of(from), of(to), this.getFullOptions(from, to, options)]) + ), + switchMap(([from, to, fullOptions]) => { + if ((from.isNative && to.isWrapped) || (from.isWrapped && to.isNative)) { + return this.getWrappedWrapTrade(from, to, fullOptions); + } + + const nativeProviders = Object.entries(this.tradeProviders[from.blockchain]).filter( + ([type]) => !fullOptions.disabledProviders.includes(type as OnChainTradeType) + ) as [OnChainTradeType, OnChainProvider][]; + + const aggregatorsTrades = this.getAggregatorsCalculationPromises( + from, + to, + fullOptions + ); + + const totalTrades = [...nativeProviders, ...aggregatorsTrades].length; + + return merge( + ...nativeProviders.map(([_, provider]) => + fromPromise( + this.getProviderCalculationPromise(provider, from, to, fullOptions) + ) + ), + ...aggregatorsTrades + ).pipe( + map((wrappedTrade, index) => ({ + total: totalTrades, + calculated: index + 1, + wrappedTrade: wrappedTrade || null + })), + startWith({ + total: totalTrades, + calculated: 0, + wrappedTrade: null + }) + ); + }) + ); + } + + /** + * Calculates on-chain trades, sorted by output amount. + * + * @example + * ```ts + * const blockchain = BLOCKCHAIN_NAME.ETHEREUM; + * // ETH + * const fromTokenAddress = '0x0000000000000000000000000000000000000000'; + * const fromAmount = 1; + * // USDT + * const toTokenAddress = '0xdac17f958d2ee523a2206206994597c13d831ec7'; + * + * const trades = await sdk.onChainManager.calculateTrade( + * { blockchain, address: fromTokenAddress }, + * fromAmount, + * toTokenAddress + * ); + * const bestTrade = trades[0]; + * + * trades.forEach(trade => { + * if (trade instanceof OnChainTrade) { + * console.log(trade.type, `to amount: ${trade.to.tokenAmount.toFormat(3)}`) + * } + * }) + * ``` + * + * @param fromToken Token to sell. + * @param fromAmount Amount to sell. + * @param toToken Token to get. + * @param options Additional options. + * @returns List of calculated on-chain trades. + */ + public async calculateTrade( + fromToken: + | Token + | { + address: string; + blockchain: BlockchainName; + } + | PriceToken, + fromAmount: string | number, + toToken: Token | string | PriceToken, + options?: OnChainManagerCalculationOptions + ): Promise { + if (toToken instanceof Token && fromToken.blockchain !== toToken.blockchain) { + throw new RubicSdkError('Blockchains of from and to tokens must be same'); + } + + const { from, to } = await getPriceTokensFromInputTokens( + fromToken, + fromAmount.toString(), + toToken + ); + + const fullOptions = await this.getFullOptions(from, to, options); + return this.calculateTradeFromTokens(from, to, fullOptions); + } + + private async getFullOptions( + from: PriceTokenAmount, + to: PriceToken, + options?: OnChainManagerCalculationOptions + ): Promise { + const chainType = BlockchainsInfo.getChainType(from.blockchain) as keyof ProviderAddress; + + const [isDeflationFrom, isDeflationTo] = await Promise.all([ + this.isDeflationToken(from), + this.isDeflationToken(to) + ]); + let useProxy: boolean; + if (options?.useProxy === false) { + useProxy = options.useProxy; + } else { + useProxy = + OnChainProxyService.isSupportedBlockchain(from.blockchain) && + (!isDeflationFrom.isDeflation || isDeflationFrom.isWhitelisted); + } + + return combineOptions( + { ...options, useProxy }, + { + timeout: OnChainManager.defaultCalculationTimeout, + disabledProviders: [], + providerAddress: + options?.providerAddress || + this.providerAddress?.[chainType]?.onChain || + defaultProviderAddresses.onChain, + useProxy, + withDeflation: { + from: isDeflationFrom, + to: isDeflationTo + } + } + ); + } + + private async calculateTradeFromTokens( + from: PriceTokenAmount, + to: PriceToken, + options: RequiredOnChainManagerCalculationOptions + ): Promise { + if ((from.isNative && to.isWrapped) || (from.isWrapped && to.isNative)) { + return [ + { + trade: OnChainManager.getWrapTrade(from, to, options), + tradeType: ON_CHAIN_TRADE_TYPE.WRAPPED + } + ]; + } + + const dexesProviders = Object.entries(this.tradeProviders[from.blockchain]).filter( + ([type]) => !options.disabledProviders.includes(type as OnChainTradeType) + ) as [OnChainTradeType, OnChainProvider][]; + const dexesTradesPromise = this.calculateDexes(from, to, dexesProviders, options); + const aggregatorsTradesPromises = this.getAggregatorsCalculationPromises(from, to, options); + + const allTrades = ( + await Promise.all([dexesTradesPromise, ...aggregatorsTradesPromises]) + ).flat(); + + return allTrades.filter(notNull).sort((tradeA, tradeB) => { + if (tradeA instanceof OnChainTrade || tradeB instanceof OnChainTrade) { + if (tradeA instanceof OnChainTrade && tradeB instanceof OnChainTrade) { + return tradeA.to.tokenAmount.comparedTo(tradeB.to.tokenAmount); + } + return tradeA instanceof OnChainTrade ? 1 : -1; + } + return 0; + }); + } + + private isDeflationToken(token: Token): Promise { + return this.deflationTokenManager.isDeflationToken(token); + } + + private async calculateDexes( + from: PriceTokenAmount, + to: PriceToken, + dexesProviders: [OnChainTradeType, OnChainProvider][], + options: RequiredOnChainManagerCalculationOptions + ): Promise { + return Promise.all( + dexesProviders.map(([type, provider]) => + pTimeout(provider.calculate(from, to, options), options.timeout) + .then(trade => { + if (trade) { + return { tradeType: type, trade }; + } + return null; + }) + .catch(error => { + console.debug( + `[RUBIC_SDK] Trade calculation error occurred for ${type} trade provider.`, + error + ); + return { tradeType: type, trade: null, error }; + }) + ) + ); + } + + private async calculateLifiTrade( + from: PriceTokenAmount, + to: PriceToken, + options: RequiredOnChainManagerCalculationOptions + ): Promise { + try { + const disabledProviders = [ + ...this.LIFI_DISABLED_PROVIDERS, + ...options.disabledProviders + ]; + + const calculationOptions: LifiCalculationOptions = { + ...options, + slippageTolerance: options?.slippageTolerance!, + gasCalculation: options.gasCalculation === 'disabled' ? 'disabled' : 'calculate', + disabledProviders + }; + + const lifiAggregator = new this.AGGREGATORS.LIFI(); + + const lifiCalculationCall = lifiAggregator.calculate( + from as PriceTokenAmount, + to as PriceTokenAmount, + calculationOptions + ); + + return pTimeout(lifiCalculationCall, options.timeout); + } catch (err) { + console.debug('[RUBIC_SDK] Trade calculation error occurred for lifi.', err); + return { type: ON_CHAIN_TRADE_TYPE.LIFI, error: err }; + } + } + + public static getWrapTrade( + from: PriceTokenAmount, + to: PriceToken, + options: OnChainCalculationOptions + ): EvmOnChainTrade { + const fromToken = from as PriceTokenAmount; + const toToken = to as PriceToken; + return new EvmWrapTrade( + { + from: fromToken, + to: new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: from.weiAmount + }), + slippageTolerance: 0, + path: [from, to], + gasFeeInfo: null, + useProxy: false, + proxyFeeInfo: undefined, + fromWithoutFee: fromToken, + + withDeflation: { + from: { isDeflation: false }, + to: { isDeflation: false } + } + }, + options.providerAddress! + ); + } + + private async getProviderCalculationPromise( + provider: OnChainProvider, + from: PriceTokenAmount, + to: PriceToken, + options: RequiredOnChainManagerCalculationOptions + ): Promise { + try { + const wrappedTrade = await pTimeout( + provider.calculate(from, to, options), + options.timeout + ); + if (!wrappedTrade) { + return null; + } + + return { + trade: wrappedTrade, + tradeType: provider.type + }; + } catch (err: unknown) { + console.debug( + `[RUBIC_SDK] Trade calculation error occurred for ${provider.type} trade provider.`, + err + ); + return { + trade: null, + tradeType: provider.type, + error: CrossChainProvider.parseError(err) + }; + } + } + + private getAggregatorsCalculationPromises( + from: PriceTokenAmount, + to: PriceToken, + options: RequiredOnChainManagerCalculationOptions + ): Array> { + const availableAggregators = Object.values(this.AGGREGATORS) + .map(AggregatorClass => new AggregatorClass()) + .filter(aggregator => { + return !this.isDisabledAggregator(options.disabledProviders, aggregator.tradeType); + }); + + return availableAggregators.map(aggregator => { + const promise = this.getCalcPromise(from, to, options, aggregator); + return this.handleTradePromise(promise, aggregator); + }); + } + + private getCalcPromise( + from: PriceTokenAmount, + to: PriceToken, + options: RequiredOnChainManagerCalculationOptions, + aggregator: AggregatorOnChainProvider + ): Promise { + if (aggregator.tradeType === ON_CHAIN_TRADE_TYPE.LIFI) { + return this.calculateLifiTrade(from, to, options); + } + return pTimeout( + aggregator.calculate(from, to, options as RequiredOnChainCalculationOptions), + options.timeout + ); + } + + private handleTradePromise( + promise: Promise, + aggregator: AggregatorOnChainProvider + ): Promise { + return promise + .then(wrappedTrade => { + if ('error' in wrappedTrade) { + return { trade: null, tradeType: wrappedTrade.type, error: wrappedTrade.error }; + } + + if (!wrappedTrade) { + return null; + } + + return { trade: wrappedTrade, tradeType: aggregator.tradeType }; + }) + .catch(err => { + console.debug( + `[RUBIC_SDK] Trade calculation error occurred for ${aggregator.tradeType} trade provider.`, + err + ); + + return { + trade: null, + tradeType: aggregator.tradeType, + error: CrossChainProvider.parseError(err) + }; + }); + } + + private isDisabledAggregator( + disabledProviders: OnChainTradeType[], + provider: OnChainTradeType + ): boolean { + return disabledProviders.map(provider => provider.toUpperCase()).includes(provider); + } + + private getWrappedWrapTrade( + fromToken: PriceTokenAmount, + toToken: PriceToken, + fullOptions: RequiredOnChainManagerCalculationOptions + ) { + const wrappedTrade: WrappedOnChainTradeOrNull = { + error: undefined, + trade: null, + tradeType: ON_CHAIN_TRADE_TYPE.WRAPPED + }; + try { + wrappedTrade.trade = OnChainManager.getWrapTrade(fromToken, toToken, fullOptions); + } catch (err: unknown) { + wrappedTrade.error = err as RubicSdkError; + } + return of({ + total: 1, + calculated: 1, + wrappedTrade + }); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/arbitrum-protocols.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/arbitrum-protocols.ts new file mode 100644 index 0000000..332974b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/arbitrum-protocols.ts @@ -0,0 +1,35 @@ +export const oneinchArbitrumProtocols = [ + 'ARBITRUM_BALANCER_V2', + 'ARBITRUM_ONE_INCH_LIMIT_ORDER', + 'ARBITRUM_ONE_INCH_LIMIT_ORDER_V2', + 'ARBITRUM_ONE_INCH_LIMIT_ORDER_V3', + 'ARBITRUM_DODO', + 'ARBITRUM_DODO_V2', + 'ARBITRUM_SUSHISWAP', + 'ARBITRUM_DXSWAP', + 'ARBITRUM_UNISWAP_V3', + 'ARBITRUM_CURVE', + 'ARBITRUM_CURVE_V2', + 'ARBITRUM_GMX', + 'ARBITRUM_SYNAPSE', + 'ARBITRUM_SADDLE', + 'ARBITRUM_KYBERSWAP_ELASTIC', + 'ARBITRUM_KYBER_DMM_STATIC', + 'ARBITRUM_AAVE_V3', + 'ARBITRUM_ELK', + 'ARBITRUM_WOOFI_V2', + 'ARBITRUM_CAMELOT', + 'ARBITRUM_TRADERJOE', + 'ARBITRUM_TRADERJOE_V2', + 'ARBITRUM_SWAPFISH', + 'ARBITRUM_PMM6', + 'ARBITRUM_ZYBER', + 'ARBITRUM_ZYBER_STABLE', + 'ARBITRUM_SOLIDLIZARD', + 'ARBITRUM_ZYBER_V3', + 'ARBITRUM_MYCELIUM', + 'ARBITRUM_TRIDENT', + 'ARBITRUM_SHELL_OCEAN', + 'ARBITRUM_RAMSES', + 'ARBITRUM_TRADERJOE_V2_1' +] as const; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/constants.ts new file mode 100644 index 0000000..50ed3ca --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/constants.ts @@ -0,0 +1,3 @@ +export const oneinchApiParams = { + nativeAddress: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/one-inch-supported-blockchains.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/one-inch-supported-blockchains.ts new file mode 100644 index 0000000..a1e6555 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/one-inch-supported-blockchains.ts @@ -0,0 +1,18 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const oneInchSupportedBlockchains = [ + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.KLAYTN, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.PULSECHAIN, + BLOCKCHAIN_NAME.ZK_SYNC, + BLOCKCHAIN_NAME.GNOSIS +] as const; + +export type OneInchSupportedBlockchains = (typeof oneInchSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-calculation-options.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-calculation-options.ts new file mode 100644 index 0000000..067f6c0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-calculation-options.ts @@ -0,0 +1,7 @@ +import { RequiredOnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { MarkRequired } from 'ts-essentials'; + +export type OneinchCalculationOptions = MarkRequired< + RequiredOnChainCalculationOptions, + 'disableMultihops' | 'fromAddress' | 'wrappedAddress' +>; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-quote-request.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-quote-request.ts new file mode 100644 index 0000000..b997150 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-quote-request.ts @@ -0,0 +1,8 @@ +export interface OneinchQuoteRequest { + params: { + src: string; + dst: string; + amount: string; + connectorTokens?: string; + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-quote-response.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-quote-response.ts new file mode 100644 index 0000000..63c6404 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-quote-response.ts @@ -0,0 +1,8 @@ +export interface OneinchQuoteResponse { + fromToken: object; + toToken: object; + dstAmount: string; + fromTokenAmount: string; + protocols: [{ fromTokenAddress: string; toTokenAddress: string }[][]]; + gas: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-swap-request.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-swap-request.ts new file mode 100644 index 0000000..c72d16f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-swap-request.ts @@ -0,0 +1,18 @@ +export interface OneinchSwapRequest { + params: { + src: string; + dst: string; + amount: string; + from: string; + slippage: string; + protocols?: string; + fee?: string; + connectorTokens?: string; + permit?: string; + receiver?: string; + referrer?: string; + disableEstimate: boolean; + compatibility?: boolean; + burnChi?: boolean; + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-swap-response.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-swap-response.ts new file mode 100644 index 0000000..f6624ff --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-swap-response.ts @@ -0,0 +1,13 @@ +export interface OneinchSwapResponse { + tx: { + from: string; + to: string; + data: string; + value: string; + gasPrice: string; + gas: number; + }; + dstAmount: string; + error?: number; + protocols: [{ fromTokenAddress: string; toTokenAddress: string }[][]]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-tokens-response.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-tokens-response.ts new file mode 100644 index 0000000..f7965ac --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-tokens-response.ts @@ -0,0 +1,3 @@ +export interface OneinchTokensResponse { + tokens: Record; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-trade-struct.ts new file mode 100644 index 0000000..e298bc0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-trade-struct.ts @@ -0,0 +1,8 @@ +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export interface OneinchTradeStruct extends EvmOnChainTradeStruct { + dexContractAddress: string; + disableMultihops: boolean; + data: string | null; + availableProtocols: string | undefined; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/one-inch-api-service.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/one-inch-api-service.ts new file mode 100644 index 0000000..03b00af --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/one-inch-api-service.ts @@ -0,0 +1,35 @@ +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Injector } from 'src/core/injector/injector'; + +export class OneInchApiService { + private static apiKey = 'sndfje3u4b3fnNSDNFUSDNVSunw345842hrnfd3b4nt4'; + + public static oneInchHttpGetRequest( + path: string, + blockchain: BlockchainName, + options?: {} + ): Promise { + return Injector.httpClient.get( + `https://x-api.rubic.exchange/api/swap/v6.0/${blockchainId[blockchain]}/${path}`, + { + ...options, + headers: { apikey: this.apiKey } + } + ); + } + + public static oneInchHttpGetApproveRequest( + path: string, + blockchain: BlockchainName, + options?: {} + ): Promise { + return Injector.httpClient.get( + `https://x-api.rubic.exchange/api/${path}/${blockchainId[blockchain]}`, + { + ...options, + headers: { apikey: this.apiKey } + } + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/one-inch-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/one-inch-provider.ts new file mode 100644 index 0000000..5ec2d87 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/one-inch-provider.ts @@ -0,0 +1,287 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, Token } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { combineOptions } from 'src/common/utils/options'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { Web3PrivateSupportedBlockchain } from 'src/core/blockchain/web3-private-service/models/web-private-supported-blockchain'; +import { createTokenNativeAddressProxy } from 'src/features/common/utils/token-native-address-proxy'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { OnChainTradeError } from 'src/features/on-chain/calculation-manager/models/on-chain-trade-error'; +import { oneinchArbitrumProtocols } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/arbitrum-protocols'; +import { oneinchApiParams } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/constants'; +import { + OneInchSupportedBlockchains, + oneInchSupportedBlockchains +} from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/one-inch-supported-blockchains'; +import { OneinchCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-calculation-options'; +import { OneinchQuoteRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-quote-request'; +import { OneinchQuoteResponse } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-quote-response'; +import { OneinchSwapRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-swap-request'; +import { OneinchSwapResponse } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-swap-response'; +import { OneinchTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-trade-struct'; +import { OneInchApiService } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/one-inch-api-service'; +import { OneInchTrade } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/one-inch-trade'; +import { LifiTrade } from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/lifi-trade'; +import { LifiTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-trade-struct'; +import { RequiredOnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { GasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { OnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade'; +import { getGasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info'; +import { getGasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-price-info'; +import { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options'; + +import { AggregatorOnChainProvider } from '../../common/on-chain-aggregator/aggregator-on-chain-provider-abstract'; + +export class OneInchProvider extends AggregatorOnChainProvider { + private readonly defaultOptions: Omit = { + ...evmProviderDefaultOptions, + disableMultihops: false, + wrappedAddress: oneinchApiParams.nativeAddress + }; + + public readonly tradeType = ON_CHAIN_TRADE_TYPE.ONE_INCH; + + protected isSupportedBlockchain(blockchain: BlockchainName): boolean { + return oneInchSupportedBlockchains.some(item => item === blockchain); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredOnChainCalculationOptions + ): Promise { + if (!this.isSupportedBlockchain(from.blockchain)) { + throw new RubicSdkError('Blockchain is not supported'); + } + + const fromBlockchain = from.blockchain as OneInchSupportedBlockchains; + + const fromAddress = + options?.useProxy || this.defaultOptions.useProxy + ? rubicProxyContractAddress[from.blockchain].gateway + : this.getWalletAddress(from.blockchain as Web3PrivateSupportedBlockchain); + const fullOptions = combineOptions(options, { + ...this.defaultOptions, + fromAddress + }); + + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract(from, fullOptions); + + const fromTokenClone = createTokenNativeAddressProxy( + fromWithoutFee, + oneinchApiParams.nativeAddress + ); + const toTokenClone = createTokenNativeAddressProxy(toToken, oneinchApiParams.nativeAddress); + + const [dexContractAddress, { toTokenAmountInWei, path, data }] = await Promise.all([ + this.loadContractAddress(fromBlockchain), + this.getTradeInfo(fromTokenClone, toTokenClone, fromWithoutFee, fullOptions) + ]); + path[0] = from; + path[path.length - 1] = toToken; + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: toTokenAmountInWei + }); + const availableProtocols = this.getAvailableProtocols(fromBlockchain); + + const oneinchTradeStruct: OneinchTradeStruct = { + dexContractAddress, + from, + to, + slippageTolerance: fullOptions.slippageTolerance, + disableMultihops: fullOptions.disableMultihops, + path, + gasFeeInfo: null, + data, + useProxy: fullOptions.useProxy, + proxyFeeInfo, + fromWithoutFee, + withDeflation: fullOptions.withDeflation, + usedForCrossChain: fullOptions.usedForCrossChain, + availableProtocols + }; + + return new OneInchTrade(oneinchTradeStruct, fullOptions.providerAddress); + } + + private async getTradeInfo( + from: PriceTokenAmount, + toToken: Token, + fromWithoutFee: PriceTokenAmount, + options: OneinchCalculationOptions + ): Promise<{ + toTokenAmountInWei: BigNumber; + estimatedGas: BigNumber; + path: Token[]; + data: string | null; + }> { + const fakeAddress = '0xe388Ed184958062a2ea29B7fD049ca21244AE02e'; + const isDefaultWrappedAddress = options.wrappedAddress === oneinchApiParams.nativeAddress; + const isNative = from.isNative || from.address === oneinchApiParams.nativeAddress; + const fromTokenAddress = + isNative && !isDefaultWrappedAddress ? options.wrappedAddress : from.address; + const toTokenAddress = toToken.address; + const availableProtocols = this.getAvailableProtocols( + from.blockchain as OneInchSupportedBlockchains + ); + const quoteTradeParams: OneinchQuoteRequest = { + params: { + src: fromTokenAddress, + dst: toTokenAddress, + amount: from.stringWeiAmount, + ...(options.disableMultihops && { + connectorTokens: `${fromTokenAddress},${toTokenAddress}` + }), + ...(availableProtocols && { protocols: availableProtocols }) + } + }; + + let oneInchTrade: OneinchSwapResponse | OneinchQuoteResponse; + let estimatedGas: BigNumber; + let toTokenAmount: string; + let data: string | null = null; + let path = [] as Token[]; + + try { + if (!options.fromAddress) { + throw new Error('Address is not set'); + } + + if (options.gasCalculation !== 'disabled') { + await OneInchTrade.checkIfNeedApproveAndThrowError( + from, + toToken, + fromWithoutFee, + options.fromAddress, + options.useProxy + ); + } + + const swapTradeParams: OneinchSwapRequest = { + params: { + ...quoteTradeParams.params, + slippage: (options.slippageTolerance * 100).toString(), + from: + this.getWalletAddress(from.blockchain as Web3PrivateSupportedBlockchain) || + fakeAddress, + disableEstimate: options.gasCalculation === 'disabled' + } + }; + oneInchTrade = await OneInchApiService.oneInchHttpGetRequest( + 'swap', + from.blockchain, + swapTradeParams + ); + + estimatedGas = new BigNumber(oneInchTrade.tx.gas); + toTokenAmount = oneInchTrade.dstAmount; + data = oneInchTrade.tx.data; + } catch (_err) { + oneInchTrade = await OneInchApiService.oneInchHttpGetRequest( + 'quote', + from.blockchain, + quoteTradeParams + ); + if (oneInchTrade.hasOwnProperty('errors') || !oneInchTrade.dstAmount) { + throw new RubicSdkError('1inch quote error'); + } + + estimatedGas = new BigNumber(oneInchTrade.gas); + toTokenAmount = oneInchTrade.dstAmount; + } + + if (oneInchTrade?.protocols?.length) { + path = await this.extractPath(from, toToken, oneInchTrade); + } + + return { toTokenAmountInWei: new BigNumber(toTokenAmount), estimatedGas, path, data }; + } + + private getTokenAddress(token: PriceToken): string { + if (token.isNative) { + if (token.blockchain === BLOCKCHAIN_NAME.METIS) { + return '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000'; + } + + return oneinchApiParams.nativeAddress; + } + return token.address; + } + + protected async getGasFeeInfo(lifiTradeStruct: LifiTradeStruct): Promise { + try { + const gasPriceInfo = await getGasPriceInfo(lifiTradeStruct.from.blockchain); + const gasLimit = await LifiTrade.getGasLimit(lifiTradeStruct); + return getGasFeeInfo(gasLimit, gasPriceInfo); + } catch { + return null; + } + } + + private getAvailableProtocols(fromBlockchain: OneInchSupportedBlockchains): string | undefined { + if (fromBlockchain === BLOCKCHAIN_NAME.ARBITRUM) { + return oneinchArbitrumProtocols.join(','); + } + if (fromBlockchain === BLOCKCHAIN_NAME.ZK_SYNC) { + return 'ZKSYNC_MUTE,ZKSYNC_PMMX,ZKSYNC_SPACEFI,ZKSYNC_SYNCSWAP,ZKSYNC_GEM,ZKSYNC_MAVERICK_V1'; + } + return undefined; + } + + /** + * Extracts tokens path from oneInch api response. + * @returns Promise Tokens array, used in the route. + */ + private async extractPath( + fromToken: Token, + toToken: Token, + oneInchTrade: OneinchSwapResponse | OneinchQuoteResponse + ): Promise { + const addressesPath = oneInchTrade.protocols[0].map(protocol => { + if (!protocol?.[0]) { + throw new RubicSdkError('Protocol array must not be empty'); + } + return protocol[0].toTokenAddress; + }); + addressesPath.pop(); + + const tokensPathWithoutNative = await Token.createTokens( + addressesPath.filter(tokenAddress => tokenAddress !== oneinchApiParams.nativeAddress), + fromToken.blockchain + ); + let tokensPathWithoutNativeIndex = 0; + const tokensPath = addressesPath.map(tokenAddress => { + if (tokenAddress === oneinchApiParams.nativeAddress) { + return nativeTokensList[fromToken.blockchain]; + } + + const token = tokensPathWithoutNative[tokensPathWithoutNativeIndex]; + if (!token) { + throw new RubicSdkError('Token has to be defined'); + } + + tokensPathWithoutNativeIndex++; + + return token; + }); + + return [fromToken, ...tokensPath, toToken]; + } + + private async loadContractAddress( + fromBlockchain: OneInchSupportedBlockchains + ): Promise { + const response = await OneInchApiService.oneInchHttpGetApproveRequest<{ + address: string; + }>('approve/spender', fromBlockchain); + return response.address; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/one-inch-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/one-inch-trade.ts new file mode 100644 index 0000000..137e812 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/1inch/one-inch-trade.ts @@ -0,0 +1,196 @@ +import BigNumber from 'bignumber.js'; +import { InsufficientFundsOneinchError, LowSlippageError, RubicSdkError } from 'src/common/errors'; +import { PriceTokenAmount, Token } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { Cache } from 'src/common/utils/decorators'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { + createTokenNativeAddressProxy, + createTokenNativeAddressProxyInPathStartAndEnd +} from 'src/features/common/utils/token-native-address-proxy'; +import { oneinchApiParams } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/constants'; +import { OneinchSwapRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-swap-request'; +import { OneinchSwapResponse } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-swap-response'; +import { OneinchTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/models/oneinch-trade-struct'; +import { OneInchApiService } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/one-inch-api-service'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AggregatorEvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; + +export class OneInchTrade extends AggregatorEvmOnChainTrade { + /** @internal */ + public static async getGasLimit(tradeStruct: OneinchTradeStruct): Promise { + const fromBlockchain = tradeStruct.from.blockchain; + const walletAddress = + Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address; + if (!walletAddress) { + return null; + } + + try { + const transactionConfig = await new OneInchTrade( + tradeStruct, + EvmWeb3Pure.EMPTY_ADDRESS + ).encode({ fromAddress: walletAddress }); + + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const gasLimit = ( + await web3Public.batchEstimatedGas(walletAddress, [transactionConfig]) + )[0]; + + if (!gasLimit?.isFinite()) { + return null; + } + return gasLimit; + } catch (_err) { + return null; + } + } + + /** @internal */ + public static async checkIfNeedApproveAndThrowError( + from: PriceTokenAmount, + toToken: Token, + fromWithoutFee: PriceTokenAmount, + fromAddress: string, + useProxy: boolean + ): Promise { + const needApprove = await new OneInchTrade( + { + from, + to: toToken, + fromWithoutFee, + useProxy, + path: [from, toToken] as ReadonlyArray + } as OneinchTradeStruct, + EvmWeb3Pure.EMPTY_ADDRESS + ).needApprove(fromAddress); + if (needApprove) { + throw new RubicSdkError('Approve is needed'); + } + } + + public readonly dexContractAddress: string; + + private readonly nativeSupportedFromWithoutFee: PriceTokenAmount; + + private readonly nativeSupportedTo: PriceTokenAmount; + + private readonly disableMultihops: boolean; + + private readonly availableProtocols: string | undefined; + + /** + * @internal + * Path with wrapped native address. + */ + public readonly wrappedPath: ReadonlyArray; + + /** @internal */ + public readonly transactionData: string | null; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ONE_INCH; + } + + constructor(tradeStruct: OneinchTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this.dexContractAddress = tradeStruct.dexContractAddress; + this.disableMultihops = tradeStruct.disableMultihops; + this.transactionData = tradeStruct.data; + this.availableProtocols = tradeStruct.availableProtocols; + this.wrappedPath = createTokenNativeAddressProxyInPathStartAndEnd( + this.path, + oneinchApiParams.nativeAddress + ); + + this.nativeSupportedFromWithoutFee = createTokenNativeAddressProxy( + tradeStruct.fromWithoutFee, + oneinchApiParams.nativeAddress + ); + this.nativeSupportedTo = createTokenNativeAddressProxy( + tradeStruct.to, + oneinchApiParams.nativeAddress + ); + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + const fromTokenAddress = this.nativeSupportedFromWithoutFee.address; + const toTokenAddress = this.nativeSupportedTo.address; + const swapRequest: OneinchSwapRequest = { + params: { + src: fromTokenAddress, + dst: toTokenAddress, + amount: this.nativeSupportedFromWithoutFee.stringWeiAmount, + slippage: (this.slippageTolerance * 100).toString(), + from: options.fromAddress || this.walletAddress, + disableEstimate: true, + ...(this.disableMultihops && { + connectorTokens: `${fromTokenAddress},${toTokenAddress}` + }), + ...(options.receiverAddress && { receiver: options.receiverAddress }), + ...(this.availableProtocols && { protocols: this.availableProtocols }) + } + }; + + const { tx, dstAmount } = await this.getResponseFromApiToTransactionRequest(swapRequest); + + return { + tx: { + ...tx, + gas: String(tx.gas) + }, + toAmount: dstAmount + }; + } + + @Cache({ + maxAge: 15_000 + }) + private async getResponseFromApiToTransactionRequest( + params: OneinchSwapRequest + ): Promise { + return OneInchApiService.oneInchHttpGetRequest( + 'swap', + this.from.blockchain, + params + ); + } + + private specifyError(err: { + error?: + | { + message?: string; + description?: string; + } + | Error; + }): RubicSdkError | null { + const inchError = err?.error || err; + + if (inchError) { + if ('message' in inchError) { + if (inchError.message?.includes('cannot estimate')) { + const nativeToken = nativeTokensList[this.from.blockchain]?.symbol; + const message = `1inch sets increased costs on gas fee. For transaction enter less ${nativeToken} amount or top up your ${nativeToken} balance.`; + return new RubicSdkError(message); + } + if (inchError.message?.includes('insufficient funds for transfer')) { + return new InsufficientFundsOneinchError(this.from.blockchain); + } + } + if ('description' in inchError && inchError.description?.includes('cannot estimate')) { + return new LowSlippageError(); + } + } + + return null; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/constants/native-router-abstract-supported-blockchains.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/constants/native-router-abstract-supported-blockchains.ts new file mode 100644 index 0000000..c6b5486 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/constants/native-router-abstract-supported-blockchains.ts @@ -0,0 +1,22 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { NativeRouterSupportedBlockchains } from '../../../native-router/constants/native-router-supported-blockchains'; +import { ZetaswapOnChainSupportedBlockchains } from '../../../zetaswap/constants/zetaswap-supported-blockchains'; + +export type AllSupportedNetworks = + | ZetaswapOnChainSupportedBlockchains + | NativeRouterSupportedBlockchains; +export const blockchainNameMapping: Record = { + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: 'bsc', + [BLOCKCHAIN_NAME.ETHEREUM]: 'ethereum', + [BLOCKCHAIN_NAME.POLYGON]: 'polygon', + [BLOCKCHAIN_NAME.ARBITRUM]: 'arbitrum', + [BLOCKCHAIN_NAME.AVALANCHE]: 'avalanche', + [BLOCKCHAIN_NAME.MANTLE]: 'mantle', + [BLOCKCHAIN_NAME.BASE]: 'base', + [BLOCKCHAIN_NAME.SCROLL]: 'scroll', + [BLOCKCHAIN_NAME.MANTA_PACIFIC]: 'manta', + [BLOCKCHAIN_NAME.ZETACHAIN]: 'zetachain', + [BLOCKCHAIN_NAME.LINEA]: 'linea', + [BLOCKCHAIN_NAME.ZK_LINK]: 'zkLink' +} as const; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/models/native-router-quote.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/models/native-router-quote.ts new file mode 100644 index 0000000..84fa538 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/models/native-router-quote.ts @@ -0,0 +1,15 @@ +import { NativeRouterTransactionRequest } from './native-router-transaction-request'; + +export interface NativeRouterQuoteRequestParams { + chain: string; + tokenIn: string; + tokenOut: string; + amount: string; + fromAddress: string; + slippage?: number; +} + +export interface NativeRouterQuoteResponse { + amountOut: string; + txRequest: NativeRouterTransactionRequest; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/models/native-router-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/models/native-router-trade-struct.ts new file mode 100644 index 0000000..68feb3c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/models/native-router-trade-struct.ts @@ -0,0 +1,15 @@ +import { EvmOnChainTradeStruct } from '../../../../common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; +import { GasFeeInfo } from '../../../../common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { NativeRouterQuoteRequestParams } from './native-router-quote'; +import { NativeRouterTransactionRequest } from './native-router-transaction-request'; + +export interface NativeRouterTradeStruct extends EvmOnChainTradeStruct { + txRequest: NativeRouterTransactionRequest; +} + +export interface NativeRouterTradeInstance { + tradeStruct: NativeRouterTradeStruct & { gasFeeInfo: GasFeeInfo | null }; + providerAddress: string; + nativeRouterQuoteParams: NativeRouterQuoteRequestParams; + providerGateway: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/models/native-router-transaction-request.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/models/native-router-transaction-request.ts new file mode 100644 index 0000000..8949293 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/models/native-router-transaction-request.ts @@ -0,0 +1,13 @@ +export interface NativeRouterTransactionRequest { + target: string; + calldata: string; + value: string; +} + +export interface NativeRouterChain { + chainId: number; + chain: string; + label: string; + token: string; + isMainnet: boolean; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/native-router-abstract-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/native-router-abstract-provider.ts new file mode 100644 index 0000000..f92c81d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/native-router-abstract-provider.ts @@ -0,0 +1,119 @@ +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { OnChainTradeError } from 'src/features/on-chain/calculation-manager/models/on-chain-trade-error'; + +import { RequiredOnChainCalculationOptions } from '../../../common/models/on-chain-calculation-options'; +import { AggregatorOnChainProvider } from '../../../common/on-chain-aggregator/aggregator-on-chain-provider-abstract'; +import { GasFeeInfo } from '../../../common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { getGasFeeInfo } from '../../../common/utils/get-gas-fee-info'; +import { getGasPriceInfo } from '../../../common/utils/get-gas-price-info'; +import { + AllSupportedNetworks, + blockchainNameMapping +} from './constants/native-router-abstract-supported-blockchains'; +import { NativeRouterQuoteRequestParams } from './models/native-router-quote'; +import { + NativeRouterTradeInstance, + NativeRouterTradeStruct +} from './models/native-router-trade-struct'; +import { NativeRouterAbstractTrade } from './native-router-abstract-trade'; +import { NativeRouterApiService } from './services/native-router-api-service'; + +export abstract class NativeRouterAbstractProvider< + T extends NativeRouterAbstractTrade = NativeRouterAbstractTrade +> extends AggregatorOnChainProvider { + private readonly nativeTokenAddress = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'; + + protected abstract createNativeRouterTradeInstance(tradeInstance: NativeRouterTradeInstance): T; + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredOnChainCalculationOptions + ): Promise { + const fromChainId = blockchainId[from.blockchain]; + const toChainId = blockchainId[toToken.blockchain]; + if (fromChainId !== toChainId) { + throw new RubicSdkError(); + } + try { + const fakeAddress = '0xe388Ed184958062a2ea29B7fD049ca21244AE02e'; + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract(from, options); + const fromAddress = this.getWalletAddress(from.blockchain) || fakeAddress; + const chain = this.getBlockchainById(from.blockchain); + const path = this.getRoutePath(from, toToken); + const fromTokenAddress = from.isNative ? this.nativeTokenAddress : from.address; + const toTokenAddress = toToken.isNative ? this.nativeTokenAddress : toToken.address; + const nativeRouterQuoteParams: NativeRouterQuoteRequestParams = { + chain: chain, + tokenIn: fromTokenAddress, + tokenOut: toTokenAddress, + amount: fromWithoutFee.tokenAmount.toString(), + fromAddress: fromAddress, + slippage: options.slippageTolerance + }; + const { amountOut, txRequest } = await NativeRouterApiService.getFirmQuote( + nativeRouterQuoteParams + ); + const providerGateway = txRequest.target; + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: Web3Pure.fromWei(amountOut, toToken.decimals) + }); + const nativeRouterTradeStruct: NativeRouterTradeStruct = { + from, + to, + slippageTolerance: options.slippageTolerance, + path, + gasFeeInfo: null, + useProxy: options.useProxy, + proxyFeeInfo, + fromWithoutFee, + withDeflation: options.withDeflation, + usedForCrossChain: options.usedForCrossChain, + txRequest + }; + const gasFeeInfo = + options.gasCalculation === 'calculate' + ? await this.getGasFeeInfo(nativeRouterTradeStruct, providerGateway) + : null; + const tradeInstance: NativeRouterTradeInstance = { + tradeStruct: { + ...nativeRouterTradeStruct, + gasFeeInfo + }, + providerAddress: options.providerAddress, + nativeRouterQuoteParams, + providerGateway + }; + return this.createNativeRouterTradeInstance(tradeInstance); + } catch (err) { + return { + type: this.tradeType, + error: err + }; + } + } + + protected async getGasFeeInfo( + tradeStruct: NativeRouterTradeStruct, + providerGateway: string + ): Promise { + try { + const gasPriceInfo = await getGasPriceInfo(tradeStruct.from.blockchain); + const gasLimit = await NativeRouterAbstractTrade.getGasLimit( + tradeStruct, + providerGateway + ); + return getGasFeeInfo(gasLimit, gasPriceInfo); + } catch { + return null; + } + } + + public getBlockchainById(blockchain: string): string { + return blockchainNameMapping[blockchain as AllSupportedNetworks]; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/native-router-abstract-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/native-router-abstract-trade.ts new file mode 100644 index 0000000..a5222df --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/native-router-abstract-trade.ts @@ -0,0 +1,107 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { checkUnsupportedReceiverAddress } from 'src/features/common/utils/check-unsupported-receiver-address'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; + +import { AggregatorEvmOnChainTrade } from '../../../common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract'; +import { GetToAmountAndTxDataResponse } from '../../../common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { NativeRouterQuoteRequestParams } from './models/native-router-quote'; +import { + NativeRouterTradeInstance, + NativeRouterTradeStruct +} from './models/native-router-trade-struct'; +import { NativeRouterApiService } from './services/native-router-api-service'; + +export abstract class NativeRouterAbstractTrade extends AggregatorEvmOnChainTrade { + public static async getGasLimit( + tradeStruct: NativeRouterTradeStruct, + providerGateway: string + ): Promise { + const fromBlockchain = tradeStruct.from.blockchain; + const walletAddress = + Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address; + + if (!walletAddress) return null; + + try { + const web3public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const gasLimit = await web3public.getEstimatedGasByData( + walletAddress, + providerGateway, + { + data: tradeStruct.txRequest.calldata, + value: tradeStruct.txRequest.value + } + ); + if (!gasLimit?.isFinite()) { + return null; + } + return gasLimit; + } catch (err) { + console.debug(err); + return null; + } + } + + public readonly providerGateway: string; + + private readonly nativeRouterQuoteParams: NativeRouterQuoteRequestParams; + + protected get spenderAddress(): string { + return this.useProxy + ? rubicProxyContractAddress[this.from.blockchain].gateway + : this.providerGateway; + } + + public get dexContractAddress(): string { + throw new RubicSdkError('Dex address is unknown before swap is started'); + } + + constructor(tradeInstance: NativeRouterTradeInstance) { + super(tradeInstance.tradeStruct, tradeInstance.providerAddress); + this.providerGateway = tradeInstance.providerGateway; + this.nativeRouterQuoteParams = tradeInstance.nativeRouterQuoteParams; + } + + public async encode(options: EncodeTransactionOptions): Promise { + await checkUnsupportedReceiverAddress(options.receiverAddress, this.walletAddress); + await this.checkFromAddress(options.fromAddress, true); + await this.checkReceiverAddress(options.receiverAddress); + + if (this.useProxy) { + return this.encodeProxy(options); + } + return this.encodeDirect(options); + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + const account = options.receiverAddress || options.fromAddress; + try { + const { amountOut, txRequest } = await NativeRouterApiService.getFirmQuote({ + ...this.nativeRouterQuoteParams, + fromAddress: account + }); + + const tx: EvmEncodeConfig = { + to: txRequest.target, + data: txRequest.calldata, + value: txRequest.value + }; + + return { + tx, + toAmount: amountOut + }; + } catch (err) { + if ('statusCode' in err && 'message' in err) { + throw new RubicSdkError(err.message); + } + throw err; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/services/native-router-api-service.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/services/native-router-api-service.ts new file mode 100644 index 0000000..8835a69 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/common/native-router-abstract/services/native-router-api-service.ts @@ -0,0 +1,34 @@ +import { Injector } from 'src/core/injector/injector'; + +import { + NativeRouterQuoteRequestParams, + NativeRouterQuoteResponse +} from '../models/native-router-quote'; + +export class NativeRouterApiService { + private static NATIVE_ROUTER_API_KEY = 'f78e017fa4039cf40c055d6e864bd90df27903cc'; + + private static NATIVE_ROUTER_ENDPOINT = 'https://newapi.native.org/v1'; + + public static async getFirmQuote( + request: NativeRouterQuoteRequestParams + ): Promise { + const result = await Injector.httpClient.get( + `${this.NATIVE_ROUTER_ENDPOINT}/firm-quote`, + { + params: { + chain: request.chain, + token_in: request.tokenIn, + token_out: request.tokenOut, + from_address: request.fromAddress, + amount: request.amount, + slippage: request.slippage as number + }, + headers: { + apiKey: this.NATIVE_ROUTER_API_KEY + } + } + ); + return result; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/chains/dln-evm-on-chain-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/chains/dln-evm-on-chain-trade.ts new file mode 100644 index 0000000..9ffb892 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/chains/dln-evm-on-chain-trade.ts @@ -0,0 +1,132 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { DlnApiService } from 'src/features/common/providers/dln/dln-api-service'; +import { checkUnsupportedReceiverAddress } from 'src/features/common/utils/check-unsupported-receiver-address'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { + DlnEvmOnChainSupportedBlockchain, + DlnOnChainSupportedBlockchain +} from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/constants/dln-on-chain-supported-blockchains'; +import { DlnOnChainFactory } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/dln-on-chain-factory'; +import { DlnOnChainSwapRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-request'; +import { DlnTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-trade-struct'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AggregatorEvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; + +export class DlnEvmOnChainTrade extends AggregatorEvmOnChainTrade { + private readonly transactionRequest: DlnOnChainSwapRequest; + + public static async getGasLimit( + tradeStruct: DlnTradeStruct + ): Promise { + const fromBlockchain = tradeStruct.from.blockchain; + const walletAddress = + Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address; + if (!walletAddress) { + return null; + } + + const trade = DlnOnChainFactory.createTrade( + fromBlockchain, + tradeStruct, + EvmWeb3Pure.EMPTY_ADDRESS + ) as DlnEvmOnChainTrade; + try { + const transactionConfig = await trade.encode({ fromAddress: walletAddress }); + + const web3Public = Injector.web3PublicService.getWeb3Public( + fromBlockchain + ) as EvmWeb3Public; + const gasLimit = ( + await web3Public.batchEstimatedGas(walletAddress, [transactionConfig]) + )[0]; + + if (gasLimit?.isFinite()) { + return gasLimit; + } + } catch {} + + return null; + } + + public readonly providerGateway: string; + + public readonly type: OnChainTradeType; + + private readonly _toTokenAmountMin: PriceTokenAmount; + + protected get spenderAddress(): string { + return this.useProxy + ? rubicProxyContractAddress[this.from.blockchain].gateway + : this.providerGateway; + } + + public get dexContractAddress(): string { + throw new RubicSdkError('Dex address is unknown before swap is started'); + } + + public get toTokenAmountMin(): PriceTokenAmount { + return this._toTokenAmountMin; + } + + constructor( + tradeStruct: DlnTradeStruct, + providerAddress: string + ) { + super(tradeStruct, providerAddress); + + this._toTokenAmountMin = new PriceTokenAmount({ + ...this.to.asStruct, + tokenAmount: tradeStruct.toTokenWeiAmountMin + }); + this.type = tradeStruct.type; + this.providerGateway = tradeStruct.providerGateway; + this.transactionRequest = tradeStruct.transactionRequest; + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + const params: DlnOnChainSwapRequest = { + ...this.transactionRequest, + tokenOutRecipient: options.receiverAddress || this.web3Private.address + }; + + try { + const { tx, tokenOut } = await DlnApiService.fetchOnChainSwapData( + params + ); + + return { + tx, + toAmount: tokenOut.amount + }; + } catch (err) { + if ('statusCode' in err && 'message' in err) { + throw new RubicSdkError(err.message); + } + throw err; + } + } + + public async encode(options: EncodeTransactionOptions): Promise { + await this.checkFromAddress(options.fromAddress, true); + checkUnsupportedReceiverAddress( + options?.receiverAddress, + options?.fromAddress || this.walletAddress + ); + + if (this.useProxy) { + return this.encodeProxy(options); + } + return this.encodeDirect(options); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/chains/dln-solana-on-chain-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/chains/dln-solana-on-chain-trade.ts new file mode 100644 index 0000000..a596b0c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/chains/dln-solana-on-chain-trade.ts @@ -0,0 +1,122 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError, SwapRequestError } from 'src/common/errors'; +import { UpdatedRatesError } from 'src/common/errors/cross-chain/updated-rates-error'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { SolanaBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { DlnApiService } from 'src/features/common/providers/dln/dln-api-service'; +import { checkUnsupportedReceiverAddress } from 'src/features/common/utils/check-unsupported-receiver-address'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { + DlnEvmOnChainSupportedBlockchain, + DlnOnChainSupportedBlockchain +} from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/constants/dln-on-chain-supported-blockchains'; +import { DlnOnChainSwapRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-request'; +import { DlnTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-trade-struct'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AggregatorSolanaOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-solana-on-chain-trade-abstract'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; + +export class DlnSolanaOnChainTrade extends AggregatorSolanaOnChainTrade { + private readonly transactionRequest: DlnOnChainSwapRequest; + + public static async getGasLimit( + _tradeStruct: DlnTradeStruct + ): Promise { + return null; + } + + public readonly providerGateway: string; + + public readonly type: OnChainTradeType; + + private readonly _toTokenAmountMin: PriceTokenAmount; + + protected get spenderAddress(): string { + return this.useProxy + ? rubicProxyContractAddress[this.from.blockchain].gateway + : this.providerGateway; + } + + public get dexContractAddress(): string { + throw new RubicSdkError('Dex address is unknown before swap is started'); + } + + public get toTokenAmountMin(): PriceTokenAmount { + return this._toTokenAmountMin; + } + + constructor( + tradeStruct: DlnTradeStruct, + providerAddress: string + ) { + super(tradeStruct, providerAddress); + + this._toTokenAmountMin = new PriceTokenAmount({ + ...this.to.asStruct, + tokenAmount: tradeStruct.toTokenWeiAmountMin + }); + this.type = tradeStruct.type; + this.providerGateway = tradeStruct.providerGateway; + this.transactionRequest = tradeStruct.transactionRequest; + } + + public async encodeDirect(options: EncodeTransactionOptions): Promise { + await this.checkFromAddress(options.fromAddress, true); + checkUnsupportedReceiverAddress( + options?.receiverAddress, + options?.fromAddress || this.walletAddress + ); + + try { + const transactionData = await this.getTxConfigAndCheckAmount( + options.receiverAddress, + options.fromAddress + ); + + return { + data: transactionData.data, + to: '', + value: '' + }; + } catch (err) { + if ([400, 500, 503].includes(err.code)) { + throw new SwapRequestError(); + } + + if (err instanceof UpdatedRatesError || err instanceof RubicSdkError) { + throw err; + } + throw new RubicSdkError('Can not encode trade'); + } + } + + protected async getToAmountAndTxData( + receiverAddress?: string, + _fromAddress?: string + ): Promise { + const params: DlnOnChainSwapRequest = { + ...this.transactionRequest, + tokenOutRecipient: receiverAddress || this.web3Private.address + }; + + try { + const { tx, tokenOut } = await DlnApiService.fetchOnChainSwapData(params); + + return { + tx: { + data: tx.data, + value: '', + to: '' + }, + toAmount: tokenOut.amount + }; + } catch (err) { + if ('statusCode' in err && 'message' in err) { + throw new RubicSdkError(err.message); + } + throw err; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/constants/dln-on-chain-supported-blockchains.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/constants/dln-on-chain-supported-blockchains.ts new file mode 100644 index 0000000..ca63d57 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/constants/dln-on-chain-supported-blockchains.ts @@ -0,0 +1,23 @@ +import { + BLOCKCHAIN_NAME, + EvmBlockchainName, + SolanaBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; + +export const dlnOnChainSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.SOLANA +] as const; + +export type DlnOnChainSupportedBlockchain = (typeof dlnOnChainSupportedBlockchains)[number]; +export type DlnEvmOnChainSupportedBlockchain = (typeof dlnOnChainSupportedBlockchains)[number] & + EvmBlockchainName; +export type DlnSolanaOnChainSupportedBlockchain = (typeof dlnOnChainSupportedBlockchains)[number] & + SolanaBlockchainName; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/dln-on-chain-factory.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/dln-on-chain-factory.ts new file mode 100644 index 0000000..17f4735 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/dln-on-chain-factory.ts @@ -0,0 +1,34 @@ +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { DlnEvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/chains/dln-evm-on-chain-trade'; +import { DlnSolanaOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/chains/dln-solana-on-chain-trade'; +import { + DlnEvmOnChainSupportedBlockchain, + DlnOnChainSupportedBlockchain, + DlnSolanaOnChainSupportedBlockchain +} from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/constants/dln-on-chain-supported-blockchains'; +import { DlnTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-trade-struct'; +import { OnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade'; + +export class DlnOnChainFactory { + public static createTrade( + fromBlockchain: BlockchainName, + tradeStruct: DlnTradeStruct, + providerAddress: string + ): OnChainTrade { + if (BlockchainsInfo.isSolanaBlockchainName(fromBlockchain)) { + return new DlnSolanaOnChainTrade( + tradeStruct as DlnTradeStruct, + providerAddress + ); + } + + if (BlockchainsInfo.isEvmBlockchainName(fromBlockchain)) { + return new DlnEvmOnChainTrade( + tradeStruct as DlnTradeStruct, + providerAddress + ); + } + throw new Error('Can not create trade instance'); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/dln-on-chain-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/dln-on-chain-provider.ts new file mode 100644 index 0000000..8c30edb --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/dln-on-chain-provider.ts @@ -0,0 +1,133 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { combineOptions } from 'src/common/utils/options'; +import { BLOCKCHAIN_NAME, BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { DlnApiService } from 'src/features/common/providers/dln/dln-api-service'; +import { DlnUtils } from 'src/features/common/providers/dln/dln-utils'; +import { + DlnOnChainSupportedBlockchain, + dlnOnChainSupportedBlockchains +} from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/constants/dln-on-chain-supported-blockchains'; +import { DlnOnChainFactory } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/dln-on-chain-factory'; +import { DlnOnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-calculation-options'; +import { DlnOnChainSwapRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-request'; +import { DlnTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-trade-struct'; +import { RequiredLifiCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-calculation-options'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { OnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; +import { GasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { OnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade'; +import { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options'; + +import { OnChainTradeError } from '../../../models/on-chain-trade-error'; +import { AggregatorOnChainProvider } from '../../common/on-chain-aggregator/aggregator-on-chain-provider-abstract'; + +export class DlnOnChainProvider extends AggregatorOnChainProvider { + public readonly tradeType = ON_CHAIN_TRADE_TYPE.DLN; + + private readonly defaultOptions: Omit = { + ...evmProviderDefaultOptions, + gasCalculation: 'calculate' + }; + + protected isSupportedBlockchain(blockchain: BlockchainName): boolean { + return dlnOnChainSupportedBlockchains.some( + supportedNetwork => supportedNetwork === blockchain + ); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: DlnOnChainCalculationOptions + ): Promise { + if (!this.isSupportedBlockchain(from.blockchain)) { + throw new RubicSdkError('Blockchain is not supported'); + } + + if (options.withDeflation.from.isDeflation || options.withDeflation.to.isDeflation) { + throw new RubicSdkError('[RUBIC_SDK] DLN does not work if source token is deflation.'); + } + + const fullOptions = combineOptions(options, { + ...this.defaultOptions, + disabledProviders: [...options.disabledProviders, ON_CHAIN_TRADE_TYPE.DODO] + }); + + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract(from, fullOptions); + + const fromChainId = blockchainId[from.blockchain]; + const fakeReceiver = DlnUtils.getFakeReceiver(toToken.blockchain); + + const slippage = new BigNumber(options.slippageTolerance).multipliedBy(100).toNumber(); + const requestParams: DlnOnChainSwapRequest = { + ...this.getAffiliateFee(from.blockchain), + chainId: fromChainId, + tokenIn: DlnUtils.getSupportedAddress(from), + tokenInAmount: fromWithoutFee.stringWeiAmount, + slippage, + tokenOut: DlnUtils.getSupportedAddress(toToken), + tokenOutRecipient: fakeReceiver + }; + + const debridgeResponse = await DlnApiService.fetchOnChainSwapData(requestParams); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: Web3Pure.fromWei( + debridgeResponse.tokenOut.amount, + debridgeResponse.tokenOut.decimals + ) + }); + + const toTokenAmountMin = Web3Pure.fromWei( + debridgeResponse.tokenOut.minAmount, + debridgeResponse.tokenOut.decimals + ); + + const path = this.getRoutePath(from, to); + + const tradeStruct: DlnTradeStruct = { + from, + to, + gasFeeInfo: null, + slippageTolerance: fullOptions.slippageTolerance!, + type: this.tradeType, + path, + toTokenWeiAmountMin: toTokenAmountMin, + useProxy: fullOptions.useProxy!, + proxyFeeInfo, + fromWithoutFee, + withDeflation: fullOptions.withDeflation!, + transactionRequest: requestParams, + providerGateway: debridgeResponse.tx.to || '' + }; + if (fullOptions.gasCalculation === 'calculate') { + tradeStruct.gasFeeInfo = await this.getGasFeeInfo(tradeStruct); + } + + return DlnOnChainFactory.createTrade(from.blockchain, tradeStruct, options.providerAddress); + } + + protected async getGasFeeInfo( + _tradeStruct: OnChainTradeStruct, + _providerGateway?: string + ): Promise { + return null; + } + + private getAffiliateFee( + fromBlockchain: DlnOnChainSupportedBlockchain + ): Partial> { + if (fromBlockchain === BLOCKCHAIN_NAME.SOLANA) { + return { + affiliateFeeRecipient: '6pvJfh73w1HT3b9eKRMX3EfrKH5AihVqRhasyhN5qtfP', + affiliateFeePercent: 0.1 + }; + } + return {}; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-calculation-options.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-calculation-options.ts new file mode 100644 index 0000000..d824242 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-calculation-options.ts @@ -0,0 +1,12 @@ +import { RequiredOnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { MarkRequired } from 'ts-essentials'; + +export interface DlnOnChainCalculationOptions extends RequiredOnChainCalculationOptions { + readonly disabledProviders: OnChainTradeType[]; +} + +export type RequiredDlnOnChainCalculationOptions = MarkRequired< + RequiredOnChainCalculationOptions & DlnOnChainCalculationOptions, + 'gasCalculation' +>; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-estimate-request.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-estimate-request.ts new file mode 100644 index 0000000..7c8a750 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-estimate-request.ts @@ -0,0 +1,9 @@ +export interface DlnOnChainEstimateRequest { + chainId: number; + tokenIn: string; + tokenInAmount: string; + slippage?: number; + tokenOut: string; + affiliateFeePercent?: number; + affiliateFeeRecipient?: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-estimate-response.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-estimate-response.ts new file mode 100644 index 0000000..fdb0243 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-estimate-response.ts @@ -0,0 +1,11 @@ +import { + DlnTokenAmount, + DlnTokenMinAmount +} from 'src/features/common/providers/dln/models/dln-estimation'; + +export interface DlnOnChainEstimateResponse { + estimation: { + tokenIn: DlnTokenAmount; + tokenOut: DlnTokenMinAmount; + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-request.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-request.ts new file mode 100644 index 0000000..b955569 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-request.ts @@ -0,0 +1,5 @@ +import { DlnOnChainEstimateRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-estimate-request'; + +export interface DlnOnChainSwapRequest extends DlnOnChainEstimateRequest { + tokenOutRecipient: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-response.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-response.ts new file mode 100644 index 0000000..fe82b90 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-response.ts @@ -0,0 +1,13 @@ +import { + DlnTokenAmount, + DlnTokenMinAmount +} from 'src/features/common/providers/dln/models/dln-estimation'; + +export interface DlnOnChainSwapResponse { + tokenIn: DlnTokenAmount; + tokenOut: DlnTokenMinAmount; + tx: { + data: string; + to?: string; + } & T; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-trade-struct.ts new file mode 100644 index 0000000..2b787ff --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-trade-struct.ts @@ -0,0 +1,13 @@ +import BigNumber from 'bignumber.js'; +import { DlnOnChainSupportedBlockchain } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/constants/dln-on-chain-supported-blockchains'; +import { DlnOnChainSwapRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-request'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { OnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export interface DlnTradeStruct + extends OnChainTradeStruct { + type: OnChainTradeType; + toTokenWeiAmountMin: BigNumber; + providerGateway: string; + transactionRequest: DlnOnChainSwapRequest; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/constants/lifi-providers.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/constants/lifi-providers.ts new file mode 100644 index 0000000..d1b0140 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/constants/lifi-providers.ts @@ -0,0 +1,46 @@ +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +export const LIFI_API_ON_CHAIN_PROVIDERS = { + '0x': ON_CHAIN_TRADE_TYPE.ZRX, + '1inch': ON_CHAIN_TRADE_TYPE.ONE_INCH, + openocean: ON_CHAIN_TRADE_TYPE.OPEN_OCEAN, + dodo: ON_CHAIN_TRADE_TYPE.DODO, + sushiswap: ON_CHAIN_TRADE_TYPE.SUSHI_SWAP, + 'sushiswap-fus': ON_CHAIN_TRADE_TYPE.SUSHI_SWAP, + honeyswap: ON_CHAIN_TRADE_TYPE.HONEY_SWAP, + stellaswap: ON_CHAIN_TRADE_TYPE.STELLA_SWAP, + beamswap: ON_CHAIN_TRADE_TYPE.BEAM_SWAP, + ubeswap: ON_CHAIN_TRADE_TYPE.UBE_SWAP, + jswap: ON_CHAIN_TRADE_TYPE.J_SWAP, + cronaswap: ON_CHAIN_TRADE_TYPE.CRONA_SWAP, + odos: ON_CHAIN_TRADE_TYPE.ODOS, + uniswap: ON_CHAIN_TRADE_TYPE.UNI_SWAP_V3, + apeswap: ON_CHAIN_TRADE_TYPE.APE_SWAP, + verse: ON_CHAIN_TRADE_TYPE.VERSE, + quickswap: ON_CHAIN_TRADE_TYPE.QUICK_SWAP, + lif3swap: ON_CHAIN_TRADE_TYPE.LIFI, + pancakeswap: ON_CHAIN_TRADE_TYPE.PANCAKE_SWAP, + kyberswap: ON_CHAIN_TRADE_TYPE.KYBER_SWAP, + spookyswap: ON_CHAIN_TRADE_TYPE.SPOOKY_SWAP, + spiritswap: ON_CHAIN_TRADE_TYPE.SPIRIT_SWAP, + pangolin: ON_CHAIN_TRADE_TYPE.PANGOLIN, + solarbeam: ON_CHAIN_TRADE_TYPE.SOLAR_BEAM, + voltage: ON_CHAIN_TRADE_TYPE.VOLTAGE_SWAP, + oolongswap: ON_CHAIN_TRADE_TYPE.OOLONG_SWAP, + trisolaris: ON_CHAIN_TRADE_TYPE.TRISOLARIS, + soulswap: ON_CHAIN_TRADE_TYPE.SOUL_SWAP, + // NONAME LIFI SUB-PROVIDERS + tombswap: ON_CHAIN_TRADE_TYPE.LIFI, + swapr: ON_CHAIN_TRADE_TYPE.LIFI, + arbswap: ON_CHAIN_TRADE_TYPE.LIFI, + diffusion: ON_CHAIN_TRADE_TYPE.LIFI, + cronus: ON_CHAIN_TRADE_TYPE.LIFI, + evmoswap: ON_CHAIN_TRADE_TYPE.LIFI, + stable: ON_CHAIN_TRADE_TYPE.LIFI, + propeller: ON_CHAIN_TRADE_TYPE.LIFI, + enso: ON_CHAIN_TRADE_TYPE.LIFI +} as const; + +export const LIFI_DISABLED_ON_CHAIN_PROVIDERS: LifiApiOnChainTrade[] = ['openocean']; + +export type LifiApiOnChainTrade = keyof typeof LIFI_API_ON_CHAIN_PROVIDERS; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/constants/lifi-supported-blockchains.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/constants/lifi-supported-blockchains.ts new file mode 100644 index 0000000..134c9f5 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/constants/lifi-supported-blockchains.ts @@ -0,0 +1,24 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const lifiOnChainSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.MOONRIVER, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.GNOSIS, + BLOCKCHAIN_NAME.FUSE, + BLOCKCHAIN_NAME.MOONBEAM, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.POLYGON_ZKEVM, + BLOCKCHAIN_NAME.ZK_SYNC, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.MODE, + BLOCKCHAIN_NAME.SCROLL, + BLOCKCHAIN_NAME.MANTLE +] as const; + +export type LifiOnChainSupportedBlockchain = (typeof lifiOnChainSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/lifi-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/lifi-provider.ts new file mode 100644 index 0000000..5203934 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/lifi-provider.ts @@ -0,0 +1,165 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { notNull } from 'src/common/utils/object'; +import { combineOptions } from 'src/common/utils/options'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { + RouteOptions, + RoutesRequest +} from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-route'; +import { + LIFI_API_ON_CHAIN_PROVIDERS, + LIFI_DISABLED_ON_CHAIN_PROVIDERS +} from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/constants/lifi-providers'; +import { lifiOnChainSupportedBlockchains } from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/constants/lifi-supported-blockchains'; +import { LifiTrade } from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/lifi-trade'; +import { + LifiCalculationOptions, + RequiredLifiCalculationOptions +} from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-calculation-options'; +import { LifiTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-trade-struct'; +import { GasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { OnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade'; +import { getGasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info'; +import { getGasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-price-info'; +import { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options'; + +import { OnChainTradeError } from '../../../models/on-chain-trade-error'; +import { ON_CHAIN_TRADE_TYPE, OnChainTradeType } from '../../common/models/on-chain-trade-type'; +import { AggregatorOnChainProvider } from '../../common/on-chain-aggregator/aggregator-on-chain-provider-abstract'; +import { LifiOnChainApiService } from './services/lifi-on-chain-api-service'; +export class LifiProvider extends AggregatorOnChainProvider { + public readonly tradeType = ON_CHAIN_TRADE_TYPE.LIFI; + + private readonly defaultOptions: Omit = { + ...evmProviderDefaultOptions, + gasCalculation: 'calculate' + }; + + protected isSupportedBlockchain(blockchain: BlockchainName): boolean { + return lifiOnChainSupportedBlockchains.some(chain => chain === blockchain); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: LifiCalculationOptions + ): Promise { + if (!this.isSupportedBlockchain(from.blockchain)) { + throw new RubicSdkError('Blockchain is not supported'); + } + + if (options.withDeflation.from.isDeflation) { + throw new RubicSdkError('[RUBIC_SDK] Lifi does not work if source token is deflation.'); + } + + const fullOptions = combineOptions(options, { + ...this.defaultOptions, + disabledProviders: [...options.disabledProviders, ON_CHAIN_TRADE_TYPE.DODO] + }); + + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract(from, fullOptions); + + const fromChainId = blockchainId[from.blockchain]; + const toChainId = blockchainId[toToken.blockchain]; + + const { disabledProviders } = fullOptions; + const lifiDisabledProviders = Object.entries(LIFI_API_ON_CHAIN_PROVIDERS) + .filter(([_, tradeType]: [string, OnChainTradeType]) => + disabledProviders.includes(tradeType) + ) + .map(([lifiProviderKey]) => lifiProviderKey) + .concat(LIFI_DISABLED_ON_CHAIN_PROVIDERS); + + const routeOptions: RouteOptions = { + order: 'RECOMMENDED', + slippage: fullOptions.slippageTolerance, + maxPriceImpact: 0.5, + exchanges: { + deny: lifiDisabledProviders + }, + integrator: 'rubic' + }; + + const routesRequest: RoutesRequest = { + fromChainId, + fromAmount: fromWithoutFee.stringWeiAmount, + fromTokenAddress: fromWithoutFee.address, + toChainId, + toTokenAddress: toToken.address, + options: routeOptions + }; + + const result = await LifiOnChainApiService.getRoutes(routesRequest); + const { routes } = result; + const allTrades = ( + await Promise.all( + routes.map(async route => { + const step = route.steps[0]; + if (!step) { + return null; + } + const type = ON_CHAIN_TRADE_TYPE.LIFI; + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: new BigNumber(route.toAmount) + }); + const path = this.getRoutePath(from, to); + + let lifiTradeStruct: LifiTradeStruct = { + from, + to, + gasFeeInfo: null, + slippageTolerance: fullOptions.slippageTolerance!, + type, + path, + route, + toTokenWeiAmountMin: new BigNumber(route.toAmountMin), + useProxy: fullOptions.useProxy!, + proxyFeeInfo, + fromWithoutFee, + withDeflation: fullOptions.withDeflation! + }; + + const gasFeeInfo = + fullOptions.gasCalculation === 'disabled' + ? null + : await this.getGasFeeInfo(lifiTradeStruct); + lifiTradeStruct = { + ...lifiTradeStruct, + gasFeeInfo + }; + + return new LifiTrade(lifiTradeStruct, fullOptions.providerAddress); + }) + ) + ).filter(notNull); + const bestTrade = this.getBestTrade(allTrades); + return bestTrade; + } + + /** + * @description Lifi-aggregator provides several providers at the same time, this method chooses the most profitable trade + * @param trades all available lifiTrades + * @returns best trade + */ + private getBestTrade(trades: LifiTrade[]): LifiTrade { + const best = trades.sort((prev, next) => + next.to.tokenAmount.comparedTo(prev.to.tokenAmount) + )[0] as LifiTrade; + return best; + } + + protected async getGasFeeInfo(lifiTradeStruct: LifiTradeStruct): Promise { + try { + const gasPriceInfo = await getGasPriceInfo(lifiTradeStruct.from.blockchain); + const gasLimit = await LifiTrade.getGasLimit(lifiTradeStruct); + return getGasFeeInfo(gasLimit, gasPriceInfo); + } catch { + return null; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/lifi-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/lifi-trade.ts new file mode 100644 index 0000000..a7b2349 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/lifi-trade.ts @@ -0,0 +1,137 @@ +import BigNumber from 'bignumber.js'; +import { + LifiPairIsUnavailableError, + LowSlippageDeflationaryTokenError, + RubicSdkError, + SwapRequestError +} from 'src/common/errors'; +import { UpdatedRatesError } from 'src/common/errors/cross-chain/updated-rates-error'; +import { PriceTokenAmount } from 'src/common/tokens/price-token-amount'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { Route } from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-route'; +import { LifiTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-trade-struct'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { getOnChainGasData } from 'src/features/on-chain/calculation-manager/utils/get-on-chain-gas-data'; + +import { AggregatorEvmOnChainTrade } from '../../common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract'; +import { GetToAmountAndTxDataResponse } from '../../common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { LifiOnChainTransactionRequest } from './models/lifi-on-chain-transaction-request'; +import { LifiOnChainApiService } from './services/lifi-on-chain-api-service'; + +export class LifiTrade extends AggregatorEvmOnChainTrade { + /** @internal */ + public static async getGasLimit(lifiTradeStruct: LifiTradeStruct): Promise { + const lifiTrade = new LifiTrade(lifiTradeStruct, EvmWeb3Pure.EMPTY_ADDRESS); + return getOnChainGasData(lifiTrade); + } + + public readonly providerGateway: string; + + public readonly type: OnChainTradeType; + + private readonly route: Route; + + private readonly _toTokenAmountMin: PriceTokenAmount; + + protected get spenderAddress(): string { + return this.useProxy + ? rubicProxyContractAddress[this.from.blockchain].gateway + : this.providerGateway; + } + + public get dexContractAddress(): string { + throw new RubicSdkError('Dex address is unknown before swap is started'); + } + + public get toTokenAmountMin(): PriceTokenAmount { + return this._toTokenAmountMin; + } + + constructor(tradeStruct: LifiTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this._toTokenAmountMin = new PriceTokenAmount({ + ...this.to.asStruct, + weiAmount: tradeStruct.toTokenWeiAmountMin + }); + this.type = tradeStruct.type; + this.route = tradeStruct.route; + this.providerGateway = this.route.steps[0]!.estimate.approvalAddress; + } + + protected getSwapError(err: unknown & { code: number }): Error { + if ('code' in err && [400, 500, 503].includes(err.code)) { + throw new SwapRequestError(); + } + if (this.isDeflationError()) { + throw new LowSlippageDeflationaryTokenError(); + } + if (err instanceof UpdatedRatesError || err instanceof RubicSdkError) { + throw err; + } + throw new LifiPairIsUnavailableError(); + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + const firstStep = this.route.steps[0]!; + const step = { + ...firstStep, + action: { + ...firstStep.action, + fromAddress: options.fromAddress || this.walletAddress, + toAddress: options.receiverAddress || this.walletAddress + }, + execution: { + status: 'NOT_STARTED', + process: [ + { + message: 'Preparing swap.', + startedAt: Date.now(), + status: 'STARTED', + type: 'SWAP' + } + ] + } + }; + + try { + const swapResponse: { + transactionRequest: LifiOnChainTransactionRequest; + estimate: Route; + } = await LifiOnChainApiService.getQuote( + step.action.fromChainId, + step.action.toChainId, + step.action.fromToken.symbol, + step.action.toToken.symbol, + step.action.fromAmount, + step.action.fromAddress, + step.action.slippage + ); + + const { + transactionRequest, + estimate: { toAmount } + } = swapResponse; + + return { + tx: { + data: transactionRequest.data, + to: transactionRequest.to, + value: transactionRequest.value, + gas: transactionRequest.gasLimit, + gasPrice: transactionRequest.gasPrice + }, + toAmount + }; + } catch (err) { + if ('statusCode' in err && 'message' in err) { + throw new RubicSdkError(err.message); + } + throw err; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-calculation-options.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-calculation-options.ts new file mode 100644 index 0000000..885b2b8 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-calculation-options.ts @@ -0,0 +1,13 @@ +import { RequiredOnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { MarkRequired } from 'ts-essentials'; + +import { OnChainTradeType } from '../../../common/models/on-chain-trade-type'; + +export interface LifiCalculationOptions extends RequiredOnChainCalculationOptions { + readonly disabledProviders: OnChainTradeType[]; +} + +export type RequiredLifiCalculationOptions = MarkRequired< + RequiredOnChainCalculationOptions & LifiCalculationOptions, + 'gasCalculation' +>; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-on-chain-transaction-request.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-on-chain-transaction-request.ts new file mode 100644 index 0000000..47db94c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-on-chain-transaction-request.ts @@ -0,0 +1,7 @@ +export interface LifiOnChainTransactionRequest { + to: string; + data: string; + gasLimit?: string; + gasPrice?: string; + value: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-trade-struct.ts new file mode 100644 index 0000000..088b247 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-trade-struct.ts @@ -0,0 +1,10 @@ +import BigNumber from 'bignumber.js'; +import { Route } from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-route'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export interface LifiTradeStruct extends EvmOnChainTradeStruct { + type: OnChainTradeType; + route: Route; + toTokenWeiAmountMin: BigNumber; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/services/lifi-on-chain-api-service.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/services/lifi-on-chain-api-service.ts new file mode 100644 index 0000000..769055d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/lifi/services/lifi-on-chain-api-service.ts @@ -0,0 +1,55 @@ +import { Injector } from 'src/core/injector/injector'; +import { + Route, + RoutesRequest, + RoutesResponse +} from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-route'; + +import { LifiOnChainTransactionRequest } from '../models/lifi-on-chain-transaction-request'; + +export class LifiOnChainApiService { + private static LIFI_API_ENDPOINT = 'https://li.quest/v1'; + + private static LIFI_API_KEY = + '0a1eec2c-b1bd-4dc1-81cf-c988f099c929.f5950d26-5955-4e21-9db2-77ad984ea575'; + + public static async getQuote( + fromChain: number, + toChain: number, + fromToken: string, + toToken: string, + fromAmount: string, + fromAddress: string, + slippage: number + ): Promise<{ transactionRequest: LifiOnChainTransactionRequest; estimate: Route }> { + const result = await Injector.httpClient.get<{ + transactionRequest: LifiOnChainTransactionRequest; + estimate: Route; + }>(`${this.LIFI_API_ENDPOINT}/quote`, { + params: { + fromChain, + toChain, + fromToken, + toToken, + fromAmount, + fromAddress, + slippage, + integrator: 'rubic' + }, + headers: { 'x-lifi-api-key': this.LIFI_API_KEY } + }); + return result; + } + + public static async getRoutes(request: RoutesRequest): Promise { + const result = await Injector.httpClient.post( + `${this.LIFI_API_ENDPOINT}/advanced/routes`, + request, + { + headers: { 'x-lifi-api-key': this.LIFI_API_KEY } + } + ); + + return result; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/native-router/constants/native-router-supported-blockchains.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/native-router/constants/native-router-supported-blockchains.ts new file mode 100644 index 0000000..a418a4c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/native-router/constants/native-router-supported-blockchains.ts @@ -0,0 +1,17 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const nativeRouterSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.MANTLE, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.SCROLL, + BLOCKCHAIN_NAME.MANTA_PACIFIC, + BLOCKCHAIN_NAME.ZK_LINK, + BLOCKCHAIN_NAME.LINEA +] as const; + +export type NativeRouterSupportedBlockchains = (typeof nativeRouterSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/native-router/native-router-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/native-router/native-router-provider.ts new file mode 100644 index 0000000..4c77f4f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/native-router/native-router-provider.ts @@ -0,0 +1,36 @@ +import { NotSupportedBlockchain } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +import { OnChainTradeError } from '../../../models/on-chain-trade-error'; +import { RequiredOnChainCalculationOptions } from '../../common/models/on-chain-calculation-options'; +import { ON_CHAIN_TRADE_TYPE } from '../../common/models/on-chain-trade-type'; +import { NativeRouterTradeInstance } from '../common/native-router-abstract/models/native-router-trade-struct'; +import { NativeRouterAbstractProvider } from '../common/native-router-abstract/native-router-abstract-provider'; +import { nativeRouterSupportedBlockchains } from './constants/native-router-supported-blockchains'; +import { NativeRouterTrade } from './native-router-trade'; + +export class NativeRouterProvider extends NativeRouterAbstractProvider { + public readonly tradeType = ON_CHAIN_TRADE_TYPE.NATIVE_ROUTER; + + protected isSupportedBlockchain(blockchain: BlockchainName): boolean { + return nativeRouterSupportedBlockchains.some(chain => chain === blockchain); + } + + protected createNativeRouterTradeInstance( + tradeInstance: NativeRouterTradeInstance + ): NativeRouterTrade { + return new NativeRouterTrade(tradeInstance); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredOnChainCalculationOptions + ): Promise { + if (!this.isSupportedBlockchain(from.blockchain)) { + throw new NotSupportedBlockchain(); + } + return super.calculate(from, toToken, options); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/native-router/native-router-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/native-router/native-router-trade.ts new file mode 100644 index 0000000..94040ff --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/native-router/native-router-trade.ts @@ -0,0 +1,8 @@ +import { ON_CHAIN_TRADE_TYPE, OnChainTradeType } from '../../common/models/on-chain-trade-type'; +import { NativeRouterAbstractTrade } from '../common/native-router-abstract/native-router-abstract-trade'; + +export class NativeRouterTrade extends NativeRouterAbstractTrade { + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.NATIVE_ROUTER; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/constants/odos-api-consts.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/constants/odos-api-consts.ts new file mode 100644 index 0000000..95c5366 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/constants/odos-api-consts.ts @@ -0,0 +1,2 @@ +export const ODOS_API_BASE_URL = 'https://api.odos.xyz'; +export const ODOS_REFERRAL_CODE = 1701778763; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-api-best-route-types.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-api-best-route-types.ts new file mode 100644 index 0000000..2ccbc59 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-api-best-route-types.ts @@ -0,0 +1,61 @@ +import { Any } from 'src/common/utils/types'; + +export interface OdosBestRouteRequestBody { + chainId: number; + inputTokens: OdosInputTokenRequest[]; + outputTokens: OdosOutputTokenRequest[]; + userAddr: string; + slippageLimitPercent: number; + /* to exclude/include swappers need to find name in odos-api - check /info/liquidity-sources/{chain_id} endpoint */ + sourceBlacklist: string[]; + sourceWhitelist: string[]; + /* simple: true used to make response faster */ + simple?: boolean; + /* Use Odos V2 compact call data for transaction, defaults to */ + compact?: boolean; + /* If input and output tokens are the same, only route through like assets for decreased slippage. */ + likeAsset?: boolean; + /* Disable all exchanges that qualify as RFQs with centralized API */ + disableRFQs?: boolean; + /* Receiving partner specific benefits */ + referralCode?: number; +} + +export interface OdosInputTokenRequest { + tokenAddress: string; + amount: string; +} + +export interface OdosOutputTokenRequest { + tokenAddress: string; + proportion: number; +} + +export interface OdosBestRouteResponse { + pathId: string; + inTokens: string[]; + outTokens: string[]; + /** + * inAmounts/outAmounts - wei-amount (string) + */ + inAmounts: string[]; + outAmounts: string[]; + /** + * inValues/outValues - float number with comma between integer and decimals + */ + inValues: number[]; + outValues: number[]; + + netOutValue: number; + priceImpact: number | null; + percentDiff: number; + partnerFeePercent: number; + gasEstimate: number; + gasEstimateValue: number; + dataGasEstimate: string; + gweiPerGas: number; + deprecated: string | null; + blockNumber: number; + pathViz: Record; + pathVizImage: string | null; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-api-swap-types.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-api-swap-types.ts new file mode 100644 index 0000000..7f622f4 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-api-swap-types.ts @@ -0,0 +1,52 @@ +export interface OdosSwapRequestBody { + userAddr: string; + pathId: string; + simulate?: boolean; + receiver?: string; +} + +export interface OdosSwapResponse { + inputTokens: OdosInputTokenResponse[]; + outputTokens: OdosOutputTokenResponse[]; + outValues: string[]; + transaction: OdosTx | null; + simulation: OdosSimulation | null; + netOutValue: number; + blockNumber: number; + gasEstimate: number; + gasEstimateValue: number; + deprecated: string | null; +} + +export interface OdosTx { + gas: number; + gasPrice: number; + value: string; + to: string; + from: string; + data: string; + nonce: number; + chainId: number; +} + +interface OdosSimulation { + isSuccess: boolean; + amountsOut: number[]; + gasEstimate: number; + simulationError: OdosSimulationError | null; +} + +interface OdosSimulationError { + type: string; + errorMessage: string; +} + +interface OdosInputTokenResponse { + tokenAddress: string; + amount: string; +} + +interface OdosOutputTokenResponse { + tokenAddress: string; + amount: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-on-chain-parser-types.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-on-chain-parser-types.ts new file mode 100644 index 0000000..4f04962 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-on-chain-parser-types.ts @@ -0,0 +1,15 @@ +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +import { RequiredOnChainCalculationOptions } from '../../../common/models/on-chain-calculation-options'; + +export interface GetBestRouteBodyType { + from: PriceTokenAmount; + toToken: PriceToken; + options: RequiredOnChainCalculationOptions; + /** + * to exclude/include swappers need to find name in odos-api - check /info/liquidity-sources/{chain_id} endpoint + */ + swappersBlacklist: string[]; + swappersWhitelist: string[]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-on-chain-trade-types.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-on-chain-trade-types.ts new file mode 100644 index 0000000..005a0d7 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-on-chain-trade-types.ts @@ -0,0 +1,9 @@ +import { EvmOnChainTradeStruct } from '../../../common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; +import { OdosBestRouteRequestBody } from './odos-api-best-route-types'; + +export interface OdosOnChainTradeStruct extends EvmOnChainTradeStruct { + /** + * Used to get fresh active pathId for swap request + */ + bestRouteRequestBody: OdosBestRouteRequestBody; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-supported-blockchains.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-supported-blockchains.ts new file mode 100644 index 0000000..bc94c9f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/models/odos-supported-blockchains.ts @@ -0,0 +1,18 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const odosSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.ZK_SYNC, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.MANTLE, + BLOCKCHAIN_NAME.MODE +] as const; + +export type OdosSupportedBlockchain = (typeof odosSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/odos-on-chain-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/odos-on-chain-provider.ts new file mode 100644 index 0000000..7934e2c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/odos-on-chain-provider.ts @@ -0,0 +1,122 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Injector } from 'src/core/injector/injector'; +import { FAKE_WALLET_ADDRESS } from 'src/features/common/constants/fake-wallet-address'; + +import { OnChainTradeError } from '../../../models/on-chain-trade-error'; +import { RequiredOnChainCalculationOptions } from '../../common/models/on-chain-calculation-options'; +import { ON_CHAIN_TRADE_TYPE } from '../../common/models/on-chain-trade-type'; +import { AggregatorOnChainProvider } from '../../common/on-chain-aggregator/aggregator-on-chain-provider-abstract'; +import { GasFeeInfo } from '../../common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { OnChainTrade } from '../../common/on-chain-trade/on-chain-trade'; +import { getGasFeeInfo } from '../../common/utils/get-gas-fee-info'; +import { getGasPriceInfo } from '../../common/utils/get-gas-price-info'; +import { OdosOnChainTradeStruct } from './models/odos-on-chain-trade-types'; +import { odosSupportedBlockchains } from './models/odos-supported-blockchains'; +import { OdosOnChainTrade } from './odos-on-chain-trade'; +import { OdosOnChainApiService } from './services/odos-on-chain-api-service'; +import { OdosOnChainParser } from './services/odos-on-chain-parser'; + +export class OdosOnChainProvider extends AggregatorOnChainProvider { + public readonly tradeType = ON_CHAIN_TRADE_TYPE.ODOS; + + protected isSupportedBlockchain(blockchainName: BlockchainName): boolean { + return odosSupportedBlockchains.some(chain => chain === blockchainName); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredOnChainCalculationOptions + ): Promise { + if (!this.isSupportedBlockchain(from.blockchain)) { + throw new RubicSdkError(`Odos doesn't support ${from.blockchain} chain!`); + } + + const walletAddress = Injector.web3PrivateService.getWeb3PrivateByBlockchain( + from.blockchain + ).address; + + try { + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract(from, options); + + const path = this.getRoutePath(from, toToken); + + const bestRouteRequestBody = OdosOnChainParser.getBestRouteBody({ + from, + toToken, + options, + swappersBlacklist: [], + swappersWhitelist: [] + }); + + const { pathId, outAmounts, gasEstimate } = await OdosOnChainApiService.getBestRoute( + bestRouteRequestBody + ); + const { transaction: tx } = await OdosOnChainApiService.getSwapTx({ + pathId, + userAddr: options.fromAddress ?? walletAddress ?? FAKE_WALLET_ADDRESS + }); + + const providerGateway = tx!.to; + + const outputAmount = outAmounts[0] as string; + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: new BigNumber(outputAmount) + }); + + const tradeStruct: OdosOnChainTradeStruct = { + from, + to, + fromWithoutFee, + proxyFeeInfo, + gasFeeInfo: { + gasLimit: new BigNumber(gasEstimate) + }, + slippageTolerance: options.slippageTolerance, + useProxy: options.useProxy, + withDeflation: options.withDeflation, + path, + bestRouteRequestBody + }; + + const gasFeeInfo = + options.gasCalculation === 'calculate' + ? await this.getGasFeeInfo(tradeStruct, providerGateway) + : null; + + return new OdosOnChainTrade( + { + ...tradeStruct, + gasFeeInfo + }, + options.providerAddress, + providerGateway! + ); + } catch (err) { + return { + type: ON_CHAIN_TRADE_TYPE.ODOS, + error: err + }; + } + } + + protected async getGasFeeInfo( + tradeStruct: OdosOnChainTradeStruct, + providerGateway: string + ): Promise { + try { + const gasPriceInfo = await getGasPriceInfo(tradeStruct.from.blockchain); + const gasLimit = tradeStruct.gasFeeInfo?.gasLimit + ? tradeStruct.gasFeeInfo?.gasLimit + : await OdosOnChainTrade.getGasLimit(tradeStruct, providerGateway); + return getGasFeeInfo(gasLimit, gasPriceInfo); + } catch { + return null; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/odos-on-chain-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/odos-on-chain-trade.ts new file mode 100644 index 0000000..37fb60d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/odos-on-chain-trade.ts @@ -0,0 +1,78 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { AggregatorEvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract'; +import { getOnChainGasData } from 'src/features/on-chain/calculation-manager/utils/get-on-chain-gas-data'; + +import { ON_CHAIN_TRADE_TYPE } from '../../common/models/on-chain-trade-type'; +import { GetToAmountAndTxDataResponse } from '../../common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { OdosBestRouteRequestBody } from './models/odos-api-best-route-types'; +import { OdosOnChainTradeStruct } from './models/odos-on-chain-trade-types'; +import { OdosOnChainApiService } from './services/odos-on-chain-api-service'; + +export class OdosOnChainTrade extends AggregatorEvmOnChainTrade { + /* @internal */ + public static async getGasLimit( + tradeStruct: OdosOnChainTradeStruct, + providerGateway: string + ): Promise { + const odosTrade = new OdosOnChainTrade( + tradeStruct, + EvmWeb3Pure.EMPTY_ADDRESS, + providerGateway + ); + + return getOnChainGasData(odosTrade); + } + + public readonly type = ON_CHAIN_TRADE_TYPE.ODOS; + + public readonly providerGateway: string; + + private bestRouteRequestBody: OdosBestRouteRequestBody; + + public get dexContractAddress(): string { + throw new RubicSdkError('Dex address is unknown before swap is started'); + } + + protected get spenderAddress(): string { + return this.useProxy + ? rubicProxyContractAddress[this.from.blockchain].gateway + : this.providerGateway; + } + + constructor( + tradeStruct: OdosOnChainTradeStruct, + providerAddress: string, + providerGateway: string + ) { + super(tradeStruct, providerAddress); + this.bestRouteRequestBody = tradeStruct.bestRouteRequestBody; + this.providerGateway = providerGateway; + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + const { pathId } = await OdosOnChainApiService.getBestRoute(this.bestRouteRequestBody); + + const { transaction, outputTokens } = await OdosOnChainApiService.getSwapTx({ + userAddr: this.walletAddress, + receiver: options.receiverAddress, + pathId + }); + + const toAmount = outputTokens[0]!.amount; + + return { + tx: { + data: transaction!.data, + to: transaction!.to, + value: transaction!.value + }, + toAmount + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/services/odos-on-chain-api-service.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/services/odos-on-chain-api-service.ts new file mode 100644 index 0000000..b1b7ba4 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/services/odos-on-chain-api-service.ts @@ -0,0 +1,35 @@ +import { RubicSdkError } from 'src/common/errors'; +import { Injector } from 'src/core/injector/injector'; + +import { ODOS_API_BASE_URL } from '../constants/odos-api-consts'; +import { + OdosBestRouteRequestBody, + OdosBestRouteResponse +} from '../models/odos-api-best-route-types'; +import { OdosSwapRequestBody, OdosSwapResponse } from '../models/odos-api-swap-types'; + +export class OdosOnChainApiService { + public static async getBestRoute( + body: OdosBestRouteRequestBody + ): Promise { + const res = await Injector.httpClient.post( + `${ODOS_API_BASE_URL}/sor/quote/v2`, + body + ); + + return res; + } + + public static async getSwapTx(body: OdosSwapRequestBody): Promise { + const res = await Injector.httpClient.post( + `${ODOS_API_BASE_URL}/sor/assemble`, + body + ); + + if (!res.transaction) { + throw new RubicSdkError(`Transaction status is undefined!`); + } + + return res; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/services/odos-on-chain-parser.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/services/odos-on-chain-parser.ts new file mode 100644 index 0000000..ffc2622 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/odos/services/odos-on-chain-parser.ts @@ -0,0 +1,47 @@ +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { FAKE_WALLET_ADDRESS } from 'src/features/common/constants/fake-wallet-address'; + +import { ODOS_REFERRAL_CODE } from '../constants/odos-api-consts'; +import { + OdosBestRouteRequestBody, + OdosInputTokenRequest, + OdosOutputTokenRequest +} from '../models/odos-api-best-route-types'; +import { GetBestRouteBodyType } from '../models/odos-on-chain-parser-types'; + +export class OdosOnChainParser { + public static getBestRouteBody({ + from, + toToken, + options, + swappersBlacklist = [], + swappersWhitelist = [] + }: GetBestRouteBodyType): OdosBestRouteRequestBody { + const chainId = blockchainId[from.blockchain]; + const userAddr = + options.fromAddress ?? + Injector.web3PrivateService.getWeb3PrivateByBlockchain(from.blockchain).address ?? + FAKE_WALLET_ADDRESS; + + const inputTokens = [ + { tokenAddress: from.address, amount: Web3Pure.toWei(from.tokenAmount, from.decimals) } + ] as OdosInputTokenRequest[]; + const outputTokens = [ + { proportion: 1, tokenAddress: toToken.address } + ] as OdosOutputTokenRequest[]; + + return { + inputTokens, + outputTokens, + chainId, + userAddr, + slippageLimitPercent: options.slippageTolerance * 100, + sourceBlacklist: swappersBlacklist, + sourceWhitelist: swappersWhitelist, + likeAsset: true, + referralCode: ODOS_REFERRAL_CODE + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/constants/okuswap-api.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/constants/okuswap-api.ts new file mode 100644 index 0000000..959b582 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/constants/okuswap-api.ts @@ -0,0 +1,2 @@ +export const RUBIC_X_API_OKU_BASE_URL = 'https://x-api.rubic.exchange/oku/api/market'; +export const RUBIC_X_API_OKU_APIKEY = 'sndfje3u4b3fnNSDNFUSDNVSunw345842hrnfd3b4nt4'; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/models/okuswap-api-types.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/models/okuswap-api-types.ts new file mode 100644 index 0000000..3203026 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/models/okuswap-api-types.ts @@ -0,0 +1,101 @@ +import { OkuSwapBlockchainName } from './okuswap-chain-names'; + +export interface OkuSubProvidersRes { + status: OkuSubProviderInfo[]; +} + +export interface OkuSubProviderInfo { + name: string; + active: boolean; + report?: { + chains: string[]; + }; +} + +export interface OkuQuoteRequestBody { + chain: OkuSwapBlockchainName; + account: string; + isExactIn: boolean; + inTokenAddress: string; + outTokenAddress: string; + /* non-wei raw amount (100, 100.1 etc) */ + inTokenAmount: string; + slippage: number; +} + +export interface OkuQuoteResponse { + /* non-wei raw amount (100, 100.1 etc) */ + outAmount: string; + coupon: OkuCoupon; + signingRequest?: { + typedData: object[]; + permit2Address: string; + permitSignature: OkuPermitSignature[]; + }; + estimatedGas: string; + + chainId: number; + isExactIn: boolean; + market: string; + inToken: object; + outToken: object; + inAmount: string; + humanPrice: number; + candidateTrade: object; + analysis: object; + slippage: number; + path: object[]; + extra: string; + inUsdValue: number; + outUsdValue: number; +} + +export interface OkuPermitSignature { + permit: { + details: { + amount: string; + expiration: string; + nonce: string; + token: string; + }; + sigDeadline: string; + spender: string; + }; + signature: string; +} + +export interface OkuSwapRequestBody { + coupon: OkuCoupon; + signingRequest?: { + typedData: object[]; + permit2Address: string; + permitSignature: OkuPermitSignature[]; + }; +} + +export interface OkuSwapResponse { + trade: { + to: string; + data: string; + value: string; + chainId: number; + accessList: string; + }; + extra: string; + transactions: object[]; + approvals: object[]; +} + +export interface GetBestRouteReturnType { + subProvider: string; + swapReqBody: OkuSwapRequestBody; + toAmount: string; + gas: string; +} + +interface OkuCoupon { + chainId: number; + swapConfig: object; + trade: object; + universalRouter: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/models/okuswap-chain-names.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/models/okuswap-chain-names.ts new file mode 100644 index 0000000..f0b4df7 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/models/okuswap-chain-names.ts @@ -0,0 +1,20 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const OKUSWAP_BLOCKCHAINS = { + [BLOCKCHAIN_NAME.ROOTSTOCK]: 'rootstock', + [BLOCKCHAIN_NAME.ETHEREUM]: 'ethereum', + [BLOCKCHAIN_NAME.ZK_SYNC]: 'zksync', + [BLOCKCHAIN_NAME.BASE]: 'base', + [BLOCKCHAIN_NAME.POLYGON]: 'polygon', + [BLOCKCHAIN_NAME.OPTIMISM]: 'optimism', + [BLOCKCHAIN_NAME.ARBITRUM]: 'arbitrum', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: 'bsc', + [BLOCKCHAIN_NAME.SCROLL]: 'scroll', + [BLOCKCHAIN_NAME.MANTA_PACIFIC]: 'manta', + [BLOCKCHAIN_NAME.BOBA]: 'boba', + [BLOCKCHAIN_NAME.BLAST]: 'blast', + [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: 'polygon-zkevm', + [BLOCKCHAIN_NAME.MOONBEAM]: 'moonbeam' +} as const; + +export type OkuSwapBlockchainName = (typeof OKUSWAP_BLOCKCHAINS)[keyof typeof OKUSWAP_BLOCKCHAINS]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/models/okuswap-on-chain-supported-chains.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/models/okuswap-on-chain-supported-chains.ts new file mode 100644 index 0000000..1678027 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/models/okuswap-on-chain-supported-chains.ts @@ -0,0 +1,5 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const OKUSWAP_ON_CHAIN_SUPPORTED_BLOCKCHAINS = [BLOCKCHAIN_NAME.ROOTSTOCK] as const; + +export type OkuSwapSupportedBlockchain = (typeof OKUSWAP_ON_CHAIN_SUPPORTED_BLOCKCHAINS)[number]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/models/okuswap-trade-types.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/models/okuswap-trade-types.ts new file mode 100644 index 0000000..090b0d6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/models/okuswap-trade-types.ts @@ -0,0 +1,8 @@ +import { EvmOnChainTradeStruct } from '../../../common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; +import { OkuQuoteRequestBody, OkuSwapRequestBody } from './okuswap-api-types'; + +export interface OkuSwapOnChainTradeStruct extends EvmOnChainTradeStruct { + subProvider: string; + swapReqBody: OkuSwapRequestBody; + quoteReqBody: OkuQuoteRequestBody; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/okuswap-on-chain-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/okuswap-on-chain-provider.ts new file mode 100644 index 0000000..2ce9781 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/okuswap-on-chain-provider.ts @@ -0,0 +1,156 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { FAKE_WALLET_ADDRESS } from 'src/features/common/constants/fake-wallet-address'; + +import { OnChainTradeError } from '../../../models/on-chain-trade-error'; +import { RequiredOnChainCalculationOptions } from '../../common/models/on-chain-calculation-options'; +import { ON_CHAIN_TRADE_TYPE } from '../../common/models/on-chain-trade-type'; +import { AggregatorOnChainProvider } from '../../common/on-chain-aggregator/aggregator-on-chain-provider-abstract'; +import { GasFeeInfo } from '../../common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { OnChainTrade } from '../../common/on-chain-trade/on-chain-trade'; +import { getGasFeeInfo } from '../../common/utils/get-gas-fee-info'; +import { getGasPriceInfo } from '../../common/utils/get-gas-price-info'; +import { + GetBestRouteReturnType, + OkuQuoteRequestBody, + OkuQuoteResponse +} from './models/okuswap-api-types'; +import { OKUSWAP_BLOCKCHAINS } from './models/okuswap-chain-names'; +import { + OKUSWAP_ON_CHAIN_SUPPORTED_BLOCKCHAINS, + OkuSwapSupportedBlockchain +} from './models/okuswap-on-chain-supported-chains'; +import { OkuSwapOnChainTradeStruct } from './models/okuswap-trade-types'; +import { OkuSwapOnChainTrade } from './okuswap-on-chain-trade'; +import { OkuSwapApiService } from './services/okuswap-api-service'; + +export class OkuSwapOnChainProvider extends AggregatorOnChainProvider { + public readonly tradeType = ON_CHAIN_TRADE_TYPE.OKU_SWAP; + + protected isSupportedBlockchain(blockchain: BlockchainName): boolean { + return OKUSWAP_ON_CHAIN_SUPPORTED_BLOCKCHAINS.some(chain => chain === blockchain); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredOnChainCalculationOptions + ): Promise { + if (!this.isSupportedBlockchain(from.blockchain)) { + throw new RubicSdkError(`OkuSwap doesn't support ${from.blockchain} chain!`); + } + + const fromBlockchain = from.blockchain as OkuSwapSupportedBlockchain; + const walletAddress = + options.fromAddress || this.getWalletAddress(fromBlockchain) || FAKE_WALLET_ADDRESS; + + try { + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract(from, options); + const path = this.getRoutePath(from, toToken); + + const subProviders = await OkuSwapApiService.getOkuSubProvidersForChain( + from.blockchain + ); + const quoteReqBody = { + account: walletAddress, + chain: OKUSWAP_BLOCKCHAINS[fromBlockchain], + inTokenAddress: from.address, + outTokenAddress: toToken.address, + isExactIn: true, + slippage: options.slippageTolerance * 10_000, + inTokenAmount: from.tokenAmount.toString() + } as OkuQuoteRequestBody; + + const { subProvider, swapReqBody, toAmount, gas } = await this.getBestRoute( + subProviders, + quoteReqBody + ); + + const providerGateway = swapReqBody.coupon.universalRouter; + const permit2Address = swapReqBody.signingRequest?.permit2Address; + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: new BigNumber(Web3Pure.toWei(toAmount, toToken.decimals)) + }); + + const tradeStruct: OkuSwapOnChainTradeStruct = { + from, + to, + fromWithoutFee, + proxyFeeInfo, + gasFeeInfo: { + gasLimit: new BigNumber(gas) + }, + slippageTolerance: options.slippageTolerance, + useProxy: options.useProxy, + withDeflation: options.withDeflation, + path, + quoteReqBody, + swapReqBody, + subProvider, + ...(permit2Address && { permit2ApproveAddress: permit2Address }) + }; + + const gasFeeInfo = + options.gasCalculation === 'calculate' + ? await this.getGasFeeInfo(tradeStruct, providerGateway) + : null; + + return new OkuSwapOnChainTrade( + { + ...tradeStruct, + gasFeeInfo + }, + options.providerAddress, + providerGateway + ); + } catch (err) { + return { + type: ON_CHAIN_TRADE_TYPE.OKU_SWAP, + error: err + }; + } + } + + private async getBestRoute( + subProviders: string[], + body: OkuQuoteRequestBody + ): Promise { + const promises = subProviders.map(p => OkuSwapApiService.makeQuoteRequest(p, body)); + const routes = await Promise.all(promises); + const [bestRoute] = routes.sort((a, b) => + new BigNumber(b.outAmount).minus(a.outAmount).toNumber() + ) as OkuQuoteResponse[]; + + if (!bestRoute) { + throw new RubicSdkError('[OKU_SWAP_PROVIDER] No route available!'); + } + + return { + subProvider: bestRoute.market, + swapReqBody: { + coupon: bestRoute.coupon, + signingRequest: bestRoute?.signingRequest + }, + toAmount: bestRoute.outAmount, + gas: bestRoute.estimatedGas + }; + } + + protected async getGasFeeInfo( + tradeStruct: OkuSwapOnChainTradeStruct, + providerGateway: string + ): Promise { + try { + const gasPriceInfo = await getGasPriceInfo(tradeStruct.from.blockchain); + const gasLimit = await OkuSwapOnChainTrade.getGasLimit(tradeStruct, providerGateway); + return getGasFeeInfo(gasLimit, gasPriceInfo); + } catch { + return null; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/okuswap-on-chain-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/okuswap-on-chain-trade.ts new file mode 100644 index 0000000..e2c6476 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/okuswap-on-chain-trade.ts @@ -0,0 +1,156 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError, UnnecessaryApproveError } from 'src/common/errors'; +import { EvmBasicTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-basic-transaction-options'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { getOnChainGasData } from 'src/features/on-chain/calculation-manager/utils/get-on-chain-gas-data'; +import { TransactionReceipt } from 'web3-eth'; + +import { ON_CHAIN_TRADE_TYPE, OnChainTradeType } from '../../common/models/on-chain-trade-type'; +import { AggregatorEvmOnChainTrade } from '../../common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract'; +import { GetToAmountAndTxDataResponse } from '../../common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { OkuQuoteRequestBody, OkuSwapRequestBody } from './models/okuswap-api-types'; +import { OkuSwapSupportedBlockchain } from './models/okuswap-on-chain-supported-chains'; +import { OkuSwapOnChainTradeStruct } from './models/okuswap-trade-types'; +import { OkuSwapApiService } from './services/okuswap-api-service'; + +export class OkuSwapOnChainTrade extends AggregatorEvmOnChainTrade { + /* @internal */ + public static async getGasLimit( + tradeStruct: OkuSwapOnChainTradeStruct, + providerGateway: string + ): Promise { + const okuswapTrade = new OkuSwapOnChainTrade( + tradeStruct, + EvmWeb3Pure.EMPTY_ADDRESS, + providerGateway + ); + return getOnChainGasData(okuswapTrade); + } + + public readonly type: OnChainTradeType = ON_CHAIN_TRADE_TYPE.OKU_SWAP; + + private readonly okuSubProvider: string; + + private readonly quoteReqBody: OkuQuoteRequestBody; + + private readonly swapReqBody: OkuSwapRequestBody; + + protected readonly providerGateway: string; + + protected get spenderAddress(): string { + return this.useProxy + ? rubicProxyContractAddress[this.from.blockchain].gateway + : this.providerGateway; + } + + protected get fromBlockchain(): OkuSwapSupportedBlockchain { + return this.from.blockchain as OkuSwapSupportedBlockchain; + } + + public get dexContractAddress(): string { + throw new RubicSdkError('Dex address is unknown before swap is started'); + } + + constructor( + tradeStruct: OkuSwapOnChainTradeStruct, + providerAddress: string, + providerGateway: string + ) { + super(tradeStruct, providerAddress); + + this.providerGateway = providerGateway; + this.okuSubProvider = tradeStruct.subProvider; + this.quoteReqBody = tradeStruct.quoteReqBody; + this.swapReqBody = tradeStruct.swapReqBody; + } + + /** + * Sends approve and if needed permit2Approve on UniswapV3Like-contract + */ + public async approve( + options: EvmBasicTransactionOptions, + checkNeedApprove = true, + amount: BigNumber | 'infinity' = 'infinity' + ): Promise { + const permit2Address = + this.permit2ApproveConfig.permit2Address || + '0xFcf5986450E4A014fFE7ad4Ae24921B589D039b5'; + + if (this.permit2ApproveConfig.usePermit2Approve) { + const needPermit2Approve = await this.needPermit2Approve(); + if (needPermit2Approve) { + await this.web3Private.approveOnPermit2( + this.from.address, + permit2Address, + this.spenderAddress, + amount, + new BigNumber(1_000_000), + options + ); + } + } + + if (checkNeedApprove) { + const needApprove = await this.needApprove(this.walletAddress); + if (!needApprove) { + throw new UnnecessaryApproveError(); + } + } + + this.checkWalletConnected(); + await this.checkBlockchainCorrect(); + + return this.web3Private.approveTokens(this.from.address, permit2Address, amount, options); + } + + public async needApprove(fromAddress?: string): Promise { + if (!fromAddress) { + this.checkWalletConnected(); + } + + const permit2Address = + this.permit2ApproveConfig.permit2Address || + '0xFcf5986450E4A014fFE7ad4Ae24921B589D039b5'; + + const needPermit2Approve = await this.needPermit2Approve(); + + const allowance = await this.web3Public.getAllowance( + this.from.address, + this.walletAddress, + permit2Address + ); + return allowance.lt(this.from.weiAmount) || needPermit2Approve; + } + + private async needPermit2Approve(): Promise { + const permit2Address = + this.permit2ApproveConfig.permit2Address || + '0xFcf5986450E4A014fFE7ad4Ae24921B589D039b5'; + + const [allowance, expiration] = await this.web3Public.getAllowanceAndExpirationOnPermit2( + this.from.address, + this.walletAddress, + this.spenderAddress, + permit2Address + ); + + return this.from.weiAmount.gt(allowance) || new BigNumber(Date.now()).gt(expiration); + } + + protected async getTransactionConfigAndAmount( + _options: EncodeTransactionOptions + ): Promise { + const [{ outAmount, estimatedGas }, evmConfig] = await Promise.all([ + OkuSwapApiService.makeQuoteRequest(this.okuSubProvider, this.quoteReqBody), + OkuSwapApiService.makeSwapRequest(this.okuSubProvider, this.swapReqBody) + ]); + + return { + toAmount: Web3Pure.toWei(outAmount, this.to.decimals), + tx: { ...evmConfig, gas: estimatedGas } + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/services/okuswap-api-service.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/services/okuswap-api-service.ts new file mode 100644 index 0000000..a890978 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/okuswap/services/okuswap-api-service.ts @@ -0,0 +1,101 @@ +import { RubicSdkError } from 'src/common/errors'; +import { Cache } from 'src/common/utils/decorators'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Injector } from 'src/core/injector/injector'; + +import { RUBIC_X_API_OKU_APIKEY, RUBIC_X_API_OKU_BASE_URL } from '../constants/okuswap-api'; +import { + OkuQuoteRequestBody, + OkuQuoteResponse, + OkuSubProviderInfo, + OkuSubProvidersRes, + OkuSwapRequestBody, + OkuSwapResponse +} from '../models/okuswap-api-types'; +import { OKUSWAP_BLOCKCHAINS } from '../models/okuswap-chain-names'; +import { OkuSwapSupportedBlockchain } from '../models/okuswap-on-chain-supported-chains'; + +export class OkuSwapApiService { + @Cache({ + maxAge: 15_000 + }) + public static async makeQuoteRequest( + subProvider: string, + body: OkuQuoteRequestBody + ): Promise { + try { + return Injector.httpClient.post( + `${RUBIC_X_API_OKU_BASE_URL}/${subProvider}/swap_quote`, + body, + { + headers: { + apikey: RUBIC_X_API_OKU_APIKEY + } + } + ); + } catch (err) { + throw new RubicSdkError(`[OKUSWAP] Err in api-method makeQuoteRequest - ${err}!`); + } + } + + public static async makeSwapRequest( + subProvider: string, + body: OkuSwapRequestBody + ): Promise> { + try { + const { trade } = await Injector.httpClient.post( + `${RUBIC_X_API_OKU_BASE_URL}/${subProvider}/execution_information`, + body, + { + headers: { + apikey: RUBIC_X_API_OKU_APIKEY + } + } + ); + + return { + to: trade.to, + data: trade.data, + value: trade.value + }; + } catch (err) { + throw new RubicSdkError(`[OKUSWAP] Err in api-method makeSwapRequest - ${err}!`); + } + } + + @Cache({ + maxAge: 15_000 + }) + public static async getOkuSubProvidersForChain(blockchain: BlockchainName): Promise { + try { + const { status: subProviders } = await Injector.httpClient.get( + `${RUBIC_X_API_OKU_BASE_URL}/overview`, + { + headers: { + apikey: RUBIC_X_API_OKU_APIKEY + } + } + ); + return subProviders + .filter(p => this.isSupportedProvider(p, blockchain)) + .map(p => p.name); + } catch (err) { + throw new RubicSdkError( + `[OKUSWAP] Err in api-method getOkuSubProvidersForChain - ${err}!` + ); + } + } + + private static isSupportedProvider( + provider: OkuSubProviderInfo, + blockchain: BlockchainName + ): boolean { + return ( + provider.active && + !!provider.report?.chains.includes( + OKUSWAP_BLOCKCHAINS[blockchain as OkuSwapSupportedBlockchain] + ) + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/arbitrum-gas-price.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/arbitrum-gas-price.ts new file mode 100644 index 0000000..a61924b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/arbitrum-gas-price.ts @@ -0,0 +1 @@ +export const ARBITRUM_GAS_PRICE = '0.1'; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/get-open-ocean-api-url.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/get-open-ocean-api-url.ts new file mode 100644 index 0000000..6edff92 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/get-open-ocean-api-url.ts @@ -0,0 +1,5 @@ +export const openOceanApiUrl = { + tokenList: (chain: string) => `https://x-api.rubic.exchange/oo/api/token_list/${chain}`, + quote: (chain: string) => `https://x-api.rubic.exchange/oo/api/v3/${chain}/quote`, + swapQuote: (chain: string) => `https://x-api.rubic.exchange/oo/api/v3/${chain}/swap_quote` +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/open-ocean-blockchain.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/open-ocean-blockchain.ts new file mode 100644 index 0000000..8416bed --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/open-ocean-blockchain.ts @@ -0,0 +1,28 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { OpenoceanOnChainSupportedBlockchain } from './open-ocean-on-chain-supported-blockchain'; + +export const openOceanBlockchainName: Record = { + [BLOCKCHAIN_NAME.ETHEREUM]: 'eth', + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: 'bsc', + [BLOCKCHAIN_NAME.POLYGON]: 'polygon', + [BLOCKCHAIN_NAME.AVALANCHE]: 'avax', + [BLOCKCHAIN_NAME.FANTOM]: 'fantom', + [BLOCKCHAIN_NAME.ARBITRUM]: 'arbitrum', + [BLOCKCHAIN_NAME.GNOSIS]: 'xdai', + [BLOCKCHAIN_NAME.BOBA]: 'boba', + [BLOCKCHAIN_NAME.OKE_X_CHAIN]: 'okex', + [BLOCKCHAIN_NAME.HARMONY]: 'harmony', + [BLOCKCHAIN_NAME.OPTIMISM]: 'optimism', + [BLOCKCHAIN_NAME.LINEA]: 'linea', + [BLOCKCHAIN_NAME.POLYGON_ZKEVM]: 'polygon_zkevm', + [BLOCKCHAIN_NAME.ZK_SYNC]: 'zksync', + [BLOCKCHAIN_NAME.BASE]: 'base', + [BLOCKCHAIN_NAME.MANTA_PACIFIC]: 'manta', + [BLOCKCHAIN_NAME.METIS]: 'metis', + [BLOCKCHAIN_NAME.SCROLL]: 'scroll', + [BLOCKCHAIN_NAME.BLAST]: 'blast', + [BLOCKCHAIN_NAME.MODE]: 'mode', + [BLOCKCHAIN_NAME.ROOTSTOCK]: 'rootstock', + [BLOCKCHAIN_NAME.MANTLE]: 'mantle' +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/open-ocean-on-chain-supported-blockchain.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/open-ocean-on-chain-supported-blockchain.ts new file mode 100644 index 0000000..883e527 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/open-ocean-on-chain-supported-blockchain.ts @@ -0,0 +1,29 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const openoceanOnChainSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.OKE_X_CHAIN, + BLOCKCHAIN_NAME.HARMONY, + BLOCKCHAIN_NAME.BOBA, + BLOCKCHAIN_NAME.GNOSIS, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.POLYGON_ZKEVM, + BLOCKCHAIN_NAME.ZK_SYNC, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.MANTA_PACIFIC, + BLOCKCHAIN_NAME.METIS, + BLOCKCHAIN_NAME.SCROLL, + BLOCKCHAIN_NAME.BLAST, + BLOCKCHAIN_NAME.MODE, + BLOCKCHAIN_NAME.ROOTSTOCK, + BLOCKCHAIN_NAME.MANTLE +] as const; + +export type OpenoceanOnChainSupportedBlockchain = + (typeof openoceanOnChainSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-cean-swap-quote-response.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-cean-swap-quote-response.ts new file mode 100644 index 0000000..30426b3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-cean-swap-quote-response.ts @@ -0,0 +1,10 @@ +export interface OpenoceanSwapQuoteResponse { + code: number; + data: { + data: string; + outAmount: string; + value: string; + to: string; + }; + error?: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-quote-response.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-quote-response.ts new file mode 100644 index 0000000..37a5fdc --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-quote-response.ts @@ -0,0 +1,8 @@ +export interface OpenOceanQuoteResponse { + error?: string; + code: number; + data: { + outAmount: string; + estimatedGas: number; + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-token-list-response.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-token-list-response.ts new file mode 100644 index 0000000..7a13a5a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-token-list-response.ts @@ -0,0 +1,6 @@ +export interface OpenOceanTokenListResponse { + code: number; + data: { + address: string; + }[]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-trade-struct.ts new file mode 100644 index 0000000..3dd8b4a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-trade-struct.ts @@ -0,0 +1,6 @@ +import BigNumber from 'bignumber.js'; +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export interface OpenOceanTradeStruct extends EvmOnChainTradeStruct { + toTokenWeiAmountMin: BigNumber; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/open-ocean-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/open-ocean-provider.ts new file mode 100644 index 0000000..2065b40 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/open-ocean-provider.ts @@ -0,0 +1,169 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import pTimeout from 'src/common/utils/p-timeout'; +import { + BLOCKCHAIN_NAME, + BlockchainName, + EvmBlockchainName +} from 'src/core/blockchain/models/blockchain-name'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { OnChainTradeError } from 'src/features/on-chain/calculation-manager/models/on-chain-trade-error'; +import { openOceanApiUrl } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/get-open-ocean-api-url'; +import { openOceanBlockchainName } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/open-ocean-blockchain'; +import { + OpenoceanOnChainSupportedBlockchain, + openoceanOnChainSupportedBlockchains +} from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/open-ocean-on-chain-supported-blockchain'; +import { OpenOceanQuoteResponse } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-quote-response'; +import { OpenOceanTokenListResponse } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-token-list-response'; +import { OpenOceanTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-trade-struct'; +import { OpenOceanTrade } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/open-ocean-trade'; +import { RequiredOnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { GasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { OnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade'; +import { getGasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info'; +import { getGasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-price-info'; + +import { AggregatorOnChainProvider } from '../../common/on-chain-aggregator/aggregator-on-chain-provider-abstract'; +import { ARBITRUM_GAS_PRICE } from './constants/arbitrum-gas-price'; + +export class OpenOceanProvider extends AggregatorOnChainProvider { + public readonly tradeType = ON_CHAIN_TRADE_TYPE.OPEN_OCEAN; + + public static readonly nativeAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; + + protected isSupportedBlockchain(blockchain: BlockchainName): boolean { + return openoceanOnChainSupportedBlockchains.some(item => item === blockchain); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredOnChainCalculationOptions + ): Promise { + try { + if (!this.isSupportedBlockchain(from.blockchain)) { + throw new RubicSdkError(`Open Ocean doesn't support ${from.blockchain} chain!`); + } + + await this.checkIsSupportedTokens(from, toToken); + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract(from, options); + const blockchain = from.blockchain as OpenoceanOnChainSupportedBlockchain; + const gasPrice = await Injector.web3PublicService + .getWeb3Public(blockchain) + .getGasPrice(); + const isArbitrum = blockchain === BLOCKCHAIN_NAME.ARBITRUM; + const apiUrl = openOceanApiUrl.quote(openOceanBlockchainName[blockchain]); + const quoteResponse = await pTimeout( + Injector.httpClient.get(apiUrl, { + headers: { apikey: 'sndfje3u4b3fnNSDNFUSDNVSunw345842hrnfd3b4nt4' }, + params: { + chain: openOceanBlockchainName[blockchain], + inTokenAddress: this.getTokenAddress(fromWithoutFee), + outTokenAddress: this.getTokenAddress(toToken), + amount: fromWithoutFee.tokenAmount.toString(), + slippage: options.slippageTolerance! * 100, + gasPrice: isArbitrum + ? ARBITRUM_GAS_PRICE + : Web3Pure.fromWei(gasPrice, nativeTokensList[from.blockchain].decimals) + .multipliedBy(10 ** 9) + .toString() + } + }), + 7_000 + ); + + if ([500, 400].includes(quoteResponse.code)) { + return { + type: ON_CHAIN_TRADE_TYPE.OPEN_OCEAN, + error: new RubicSdkError(quoteResponse.error) + }; + } + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: new BigNumber(quoteResponse.data.outAmount) + }); + const toTokenWeiAmountMin = new BigNumber(quoteResponse.data.outAmount).multipliedBy( + 1 - options.slippageTolerance + ); + const openOceanTradeStruct: OpenOceanTradeStruct = { + from, + to, + gasFeeInfo: { + gasLimit: new BigNumber(quoteResponse.data.estimatedGas) + }, + slippageTolerance: options.slippageTolerance!, + path: [from, to], + toTokenWeiAmountMin, + useProxy: options.useProxy, + proxyFeeInfo, + fromWithoutFee, + withDeflation: options.withDeflation + }; + const gasFeeInfo = + options.gasCalculation === 'calculate' + ? await this.getGasFeeInfo(openOceanTradeStruct) + : null; + + return new OpenOceanTrade( + { + ...openOceanTradeStruct, + gasFeeInfo + }, + options.providerAddress + ); + } catch (error) { + return { + type: ON_CHAIN_TRADE_TYPE.OPEN_OCEAN, + error + }; + } + } + + private getTokenAddress(token: PriceToken): string { + if (token.isNative) { + if (token.blockchain === BLOCKCHAIN_NAME.METIS) { + return '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000'; + } + + return OpenOceanProvider.nativeAddress; + } + return token.address; + } + + protected async getGasFeeInfo(tradeStruct: OpenOceanTradeStruct): Promise { + try { + const gasPriceInfo = await getGasPriceInfo(tradeStruct.from.blockchain); + const gasLimit = + tradeStruct?.gasFeeInfo?.gasLimit || + (await OpenOceanTrade.getGasLimit(tradeStruct)); + return getGasFeeInfo(gasLimit, gasPriceInfo); + } catch { + return null; + } + } + + private async checkIsSupportedTokens(from: PriceTokenAmount, to: PriceToken): Promise { + const apiUrl = openOceanApiUrl.tokenList( + openOceanBlockchainName[from.blockchain as OpenoceanOnChainSupportedBlockchain] + ); + const tokenListResponse = await Injector.httpClient.get( + apiUrl, + { headers: { apikey: 'sndfje3u4b3fnNSDNFUSDNVSunw345842hrnfd3b4nt4' } } + ); + const tokens = tokenListResponse?.data?.map(token => token.address.toLocaleLowerCase()); + const isSupportedTokens = + Boolean(tokens.length) && + (from.isNative || tokens.includes(from.address.toLocaleLowerCase())) && + (to.isNative || tokens.includes(to.address.toLocaleLowerCase())); + + if (!isSupportedTokens) { + throw new RubicSdkError('Unsupported token pair'); + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/open-ocean-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/open-ocean-trade.ts new file mode 100644 index 0000000..0e7dd5a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/open-ocean-trade.ts @@ -0,0 +1,169 @@ +import BigNumber from 'bignumber.js'; +import { NotWhitelistedProviderError, RubicSdkError } from 'src/common/errors'; +import { PriceToken } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { PriceTokenAmount } from 'src/common/tokens/price-token-amount'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { openOceanApiUrl } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/get-open-ocean-api-url'; +import { openOceanBlockchainName } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/open-ocean-blockchain'; +import { OpenoceanOnChainSupportedBlockchain } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/open-ocean-on-chain-supported-blockchain'; +import { OpenoceanSwapQuoteResponse } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-cean-swap-quote-response'; +import { OpenOceanTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-trade-struct'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { getOnChainGasData } from 'src/features/on-chain/calculation-manager/utils/get-on-chain-gas-data'; + +import { AggregatorEvmOnChainTrade } from '../../common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract'; +import { GetToAmountAndTxDataResponse } from '../../common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { ARBITRUM_GAS_PRICE } from './constants/arbitrum-gas-price'; + +export class OpenOceanTrade extends AggregatorEvmOnChainTrade { + /** @internal */ + public static async getGasLimit( + openOceanTradeStruct: OpenOceanTradeStruct + ): Promise { + const openOceanTrade = new OpenOceanTrade(openOceanTradeStruct, EvmWeb3Pure.EMPTY_ADDRESS); + return getOnChainGasData(openOceanTrade); + } + + public readonly type = ON_CHAIN_TRADE_TYPE.OPEN_OCEAN; + + private readonly _toTokenAmountMin: PriceTokenAmount; + + public static readonly nativeAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; + + protected get spenderAddress(): string { + const openOceanContractAddress = + this.from.blockchain === BLOCKCHAIN_NAME.OKE_X_CHAIN + ? '0xc0006Be82337585481044a7d11941c0828FFD2D4' + : '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64'; + + return this.useProxy + ? rubicProxyContractAddress[this.from.blockchain].gateway + : openOceanContractAddress; + } + + public get dexContractAddress(): string { + throw new RubicSdkError('Dex address is unknown before swap is started'); + } + + public get toTokenAmountMin(): PriceTokenAmount { + return this._toTokenAmountMin; + } + + constructor(tradeStruct: OpenOceanTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this._toTokenAmountMin = new PriceTokenAmount({ + ...this.to.asStruct, + weiAmount: tradeStruct.toTokenWeiAmountMin + }); + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + const gasPrice = await Injector.web3PublicService + .getWeb3Public(this.from.blockchain) + .getGasPrice(); + const walletAddress = ( + Injector.web3PrivateService.getWeb3Private(CHAIN_TYPE.EVM) as EvmWeb3Private + ).address; + const apiUrl = openOceanApiUrl.swapQuote( + openOceanBlockchainName[this.from.blockchain as OpenoceanOnChainSupportedBlockchain] + ); + const isArbitrum = this.from.blockchain === BLOCKCHAIN_NAME.ARBITRUM; + const swapQuoteResponse = await Injector.httpClient.get( + apiUrl, + { + headers: { apikey: 'sndfje3u4b3fnNSDNFUSDNVSunw345842hrnfd3b4nt4' }, + params: { + chain: openOceanBlockchainName[ + this.from.blockchain as OpenoceanOnChainSupportedBlockchain + ], + inTokenAddress: this.getTokenAddress(this.from), + outTokenAddress: this.getTokenAddress(this.to), + amount: this.fromWithoutFee.tokenAmount.toString(), + gasPrice: isArbitrum + ? ARBITRUM_GAS_PRICE + : Web3Pure.fromWei( + gasPrice, + nativeTokensList[this.from.blockchain].decimals + ) + .multipliedBy(10 ** 9) + .toString(), + slippage: this.slippageTolerance * 100, + account: options.receiverAddress || walletAddress, + referrer: '0x429A3A1a2623DFb520f1D93F64F38c0738418F1f' + } + } + ); + const { data, to, value, outAmount: toAmount } = swapQuoteResponse.data; + + return { + tx: { + data, + to, + value + }, + toAmount + }; + } + + private getTokenAddress(token: PriceToken): string { + if (token.isNative) { + if (token.blockchain === BLOCKCHAIN_NAME.METIS) { + return '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000'; + } + + return OpenOceanTrade.nativeAddress; + } + return token.address; + } + + protected async getSwapData(options: GetContractParamsOptions): Promise { + const directTransactionConfig = await this.encodeDirect({ + ...options, + fromAddress: rubicProxyContractAddress[this.from.blockchain].router, + supportFee: false, + receiverAddress: rubicProxyContractAddress[this.from.blockchain].router + }); + const availableDexs = ( + await ProxyCrossChainEvmTrade.getWhitelistedDexes(this.from.blockchain) + ).map(address => address.toLowerCase()); + + const routerAddress = directTransactionConfig.to; + const method = directTransactionConfig.data.slice(0, 10); + + if (!availableDexs.includes(routerAddress.toLowerCase())) { + throw new NotWhitelistedProviderError(routerAddress, undefined, 'dex'); + } + await ProxyCrossChainEvmTrade.checkDexWhiteList( + this.from.blockchain, + routerAddress, + method + ); + + return [ + [ + routerAddress, + routerAddress, + this.from.isNative && this.from.blockchain === BLOCKCHAIN_NAME.METIS + ? '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000' + : this.from.address, + this.to.address, + this.from.stringWeiAmount, + directTransactionConfig.data, + true + ] + ]; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/constants/piteas-on-chain-supported-blockchains.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/constants/piteas-on-chain-supported-blockchains.ts new file mode 100644 index 0000000..87ea27f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/constants/piteas-on-chain-supported-blockchains.ts @@ -0,0 +1,3 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const piteasOnChainSupportedBlockchains = [BLOCKCHAIN_NAME.PULSECHAIN] as const; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/constants/piteas-router-address.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/constants/piteas-router-address.ts new file mode 100644 index 0000000..020b9a6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/constants/piteas-router-address.ts @@ -0,0 +1 @@ +export const piteasRouterAddress = '0x6BF228eb7F8ad948d37deD07E595EfddfaAF88A6'; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-quote.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-quote.ts new file mode 100644 index 0000000..c44e369 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-quote.ts @@ -0,0 +1,20 @@ +export interface PiteasQuoteRequestParams { + tokenInAddress: string; + tokenInChainId: number; + tokenOutAddress: string; + tokenOutChainId: number; + amount: string; // Amount with decimals + allowedSlippage: number; + account?: string; // Receiver address +} + +export interface PiteasSuccessQuoteResponse { + destAmount: string; + gasUseEstimate: number; + methodParameters: PiteasMethodParameters; +} + +export interface PiteasMethodParameters { + calldata: string; + value: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-trade-struct.ts new file mode 100644 index 0000000..5af8ef1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-trade-struct.ts @@ -0,0 +1,8 @@ +import { PiteasMethodParameters } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-quote'; + +import { EvmOnChainTradeStruct } from '../../../common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export interface PiteasTradeStruct extends EvmOnChainTradeStruct { + methodParameters: PiteasMethodParameters; + gasUseEstimate: number; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-api-service.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-api-service.ts new file mode 100644 index 0000000..c4f35f6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-api-service.ts @@ -0,0 +1,20 @@ +import { Injector } from 'src/core/injector/injector'; +import { + PiteasQuoteRequestParams, + PiteasSuccessQuoteResponse +} from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-quote'; + +export class PiteasApiService { + public static apiEndpoint = 'https://api.piteas.io'; + + public static fetchQuote( + quoteRequestParams: PiteasQuoteRequestParams + ): Promise { + return Injector.httpClient.get( + `${PiteasApiService.apiEndpoint}/quote`, + { + params: { ...quoteRequestParams } + } + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-provider.ts new file mode 100644 index 0000000..7cf509b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-provider.ts @@ -0,0 +1,136 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { combineOptions } from 'src/common/utils/options'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { OnChainTradeError } from 'src/features/on-chain/calculation-manager/models/on-chain-trade-error'; +import { piteasOnChainSupportedBlockchains } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/constants/piteas-on-chain-supported-blockchains'; +import { piteasRouterAddress } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/constants/piteas-router-address'; +import { PiteasQuoteRequestParams } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-quote'; +import { PiteasTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-trade-struct'; +import { PiteasApiService } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-api-service'; +import { PiteasTrade } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-trade'; +import { RequiredOnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AggregatorOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-on-chain-provider-abstract'; +import { OnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade'; +import { getGasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info'; +import { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options'; + +import { GasFeeInfo } from '../../common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { getGasPriceInfo } from '../../common/utils/get-gas-price-info'; + +export class PiteasProvider extends AggregatorOnChainProvider { + private readonly defaultOptions: RequiredOnChainCalculationOptions = { + ...evmProviderDefaultOptions, + deadlineMinutes: 20, + disableMultihops: false + }; + + public readonly tradeType = ON_CHAIN_TRADE_TYPE.PITEAS; + + protected isSupportedBlockchain(blockchain: BlockchainName): boolean { + return piteasOnChainSupportedBlockchains.some( + supportedNetwork => supportedNetwork === blockchain + ); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options?: RequiredOnChainCalculationOptions + ): Promise { + if (!this.isSupportedBlockchain(from.blockchain)) { + throw new RubicSdkError(`Piteas doesn't support ${from.blockchain} chain!`); + } + + try { + const fromAddress = + options?.useProxy || this.defaultOptions.useProxy + ? rubicProxyContractAddress[from.blockchain].gateway + : this.getWalletAddress(from.blockchain); + + const fullOptions = combineOptions(options, { + ...this.defaultOptions, + fromAddress + }); + + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract(from, { + ...fullOptions + }); + + const quoteRequestParams: PiteasQuoteRequestParams = { + tokenInAddress: from.isNative ? 'PLS' : from.address, + tokenInChainId: blockchainId[from.blockchain], + tokenOutAddress: toToken.isNative ? 'PLS' : toToken.address, + tokenOutChainId: blockchainId[from.blockchain], + amount: fromWithoutFee.stringWeiAmount, + allowedSlippage: 0.5, + ...(options?.fromAddress && { account: options.fromAddress }) + }; + + const { destAmount, gasUseEstimate, methodParameters } = + await PiteasApiService.fetchQuote(quoteRequestParams); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: Web3Pure.fromWei(destAmount, toToken.decimals) + }); + + const tradeStruct: PiteasTradeStruct = { + from, + to, + slippageTolerance: fullOptions.slippageTolerance, + path: [from, to], + gasFeeInfo: null, + useProxy: fullOptions.useProxy, + proxyFeeInfo, + fromWithoutFee, + withDeflation: fullOptions.withDeflation, + usedForCrossChain: fullOptions.usedForCrossChain, + methodParameters, + gasUseEstimate + }; + + const gasFeeInfo = + options?.gasCalculation === 'calculate' + ? await this.getGasFeeInfo(tradeStruct, piteasRouterAddress) + : null; + + return new PiteasTrade( + { + ...tradeStruct, + gasFeeInfo + }, + fullOptions.providerAddress, + quoteRequestParams + ); + } catch (err) { + return { + type: ON_CHAIN_TRADE_TYPE.PITEAS, + error: err + }; + } + } + + protected async getGasFeeInfo( + tradeStruct: PiteasTradeStruct, + providerGateway: string + ): Promise { + try { + const gasPriceInfo = await getGasPriceInfo(tradeStruct.from.blockchain); + const gasLimit = + (await PiteasTrade.getGasLimit( + tradeStruct, + providerGateway, + tradeStruct.methodParameters + )) || new BigNumber(tradeStruct.gasUseEstimate); + return getGasFeeInfo(gasLimit, gasPriceInfo); + } catch { + return null; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-trade.ts new file mode 100644 index 0000000..8e8340f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-trade.ts @@ -0,0 +1,111 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { piteasRouterAddress } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/constants/piteas-router-address'; +import { + PiteasMethodParameters, + PiteasQuoteRequestParams +} from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-quote'; +import { PiteasTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-trade-struct'; +import { PiteasApiService } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-api-service'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AggregatorEvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export class PiteasTrade extends AggregatorEvmOnChainTrade { + public static async getGasLimit( + tradeStruct: EvmOnChainTradeStruct, + providerGateway: string, + methodParameters: PiteasMethodParameters + ): Promise { + const fromBlockchain = tradeStruct.from.blockchain; + const walletAddress = + Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address; + if (!walletAddress) { + return null; + } + + try { + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const gasLimit = await web3Public.getEstimatedGasByData( + walletAddress, + providerGateway, + { + data: methodParameters.calldata, + value: methodParameters.value + } + ); + + if (!gasLimit?.isFinite()) { + return null; + } + return gasLimit; + } catch (err) { + console.debug(err); + return null; + } + } + + public readonly type: OnChainTradeType = ON_CHAIN_TRADE_TYPE.PITEAS; + + public readonly providerGateway = piteasRouterAddress; + + private readonly quoteRequestParams: PiteasQuoteRequestParams; + + protected get spenderAddress(): string { + return this.useProxy + ? rubicProxyContractAddress[this.from.blockchain].gateway + : this.providerGateway; + } + + public get dexContractAddress(): string { + return piteasRouterAddress; + } + + constructor( + tradeStruct: PiteasTradeStruct, + providerAddress: string, + quoteRequestParams: PiteasQuoteRequestParams + ) { + super(tradeStruct, providerAddress); + + this.quoteRequestParams = quoteRequestParams; + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + const account = options.receiverAddress || options.fromAddress; + try { + const { destAmount, gasUseEstimate, methodParameters } = + await PiteasApiService.fetchQuote({ + ...this.quoteRequestParams, + account + }); + + const tx: EvmEncodeConfig = { + to: piteasRouterAddress, + data: methodParameters.calldata, + value: methodParameters.value, + gas: gasUseEstimate.toString() + }; + + return { + tx, + toAmount: destAmount + }; + } catch (error) { + if ('statusCode' in error && 'message' in error) { + throw new RubicSdkError(error.message); + } + throw error; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/models/rango-on-chain-disabled-providers.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/models/rango-on-chain-disabled-providers.ts new file mode 100644 index 0000000..333444b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/models/rango-on-chain-disabled-providers.ts @@ -0,0 +1,21 @@ +import { + RangoTradeType, + RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS +} from 'src/features/common/providers/rango/models/rango-api-trade-types'; + +import { ON_CHAIN_TRADE_TYPE } from '../../../common/models/on-chain-trade-type'; + +export const RANGO_ON_CHAIN_DISABLED_PROVIDERS: RangoTradeType[] = [ + RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS[ON_CHAIN_TRADE_TYPE.OSMOSIS_SWAP], + RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS[ON_CHAIN_TRADE_TYPE.OOLONG_SWAP], + RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS[ON_CHAIN_TRADE_TYPE['10K_SWAP']], + RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS[ON_CHAIN_TRADE_TYPE.FINKUJIRA], + RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS[ON_CHAIN_TRADE_TYPE.SOLANA], + RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS[ON_CHAIN_TRADE_TYPE.JUPITER], + RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS[ON_CHAIN_TRADE_TYPE.SOLAR_BEAM], + RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS[ON_CHAIN_TRADE_TYPE.WYND], + RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS[ON_CHAIN_TRADE_TYPE.SUN_SWAP], + RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS[ON_CHAIN_TRADE_TYPE.MDEX], + RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS[ON_CHAIN_TRADE_TYPE.BEAM_SWAP], + RUBIC_TO_RANGO_ON_CHAIN_PROVIDERS[ON_CHAIN_TRADE_TYPE.OKC_SWAP] +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/models/rango-on-chain-trade-types.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/models/rango-on-chain-trade-types.ts new file mode 100644 index 0000000..50efd94 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/models/rango-on-chain-trade-types.ts @@ -0,0 +1,7 @@ +import BigNumber from 'bignumber.js'; + +import { EvmOnChainTradeStruct } from '../../../common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export interface RangoOnChainTradeStruct extends EvmOnChainTradeStruct { + toTokenWeiAmountMin: BigNumber; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/rango-on-chain-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/rango-on-chain-provider.ts new file mode 100644 index 0000000..1e6f466 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/rango-on-chain-provider.ts @@ -0,0 +1,108 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { RangoBestRouteSimulationResult } from 'src/features/common/providers/rango/models/rango-api-best-route-types'; +import { rangoSupportedBlockchains } from 'src/features/common/providers/rango/models/rango-supported-blockchains'; +import { RangoCommonParser } from 'src/features/common/providers/rango/services/rango-parser'; + +import { OnChainTradeError } from '../../../models/on-chain-trade-error'; +import { RequiredOnChainCalculationOptions } from '../../common/models/on-chain-calculation-options'; +import { ON_CHAIN_TRADE_TYPE } from '../../common/models/on-chain-trade-type'; +import { AggregatorOnChainProvider } from '../../common/on-chain-aggregator/aggregator-on-chain-provider-abstract'; +import { GasFeeInfo } from '../../common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { OnChainTrade } from '../../common/on-chain-trade/on-chain-trade'; +import { getGasFeeInfo } from '../../common/utils/get-gas-fee-info'; +import { getGasPriceInfo } from '../../common/utils/get-gas-price-info'; +import { RANGO_ON_CHAIN_DISABLED_PROVIDERS } from './models/rango-on-chain-disabled-providers'; +import { RangoOnChainTradeStruct } from './models/rango-on-chain-trade-types'; +import { RangoOnChainTrade } from './rango-on-chain-trade'; +import { RangoOnChainApiService } from './services/rango-on-chain-api-service'; + +export class RangoOnChainProvider extends AggregatorOnChainProvider { + public readonly tradeType = ON_CHAIN_TRADE_TYPE.RANGO; + + protected isSupportedBlockchain(blockchainName: BlockchainName): boolean { + return rangoSupportedBlockchains.some(chain => chain === blockchainName); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredOnChainCalculationOptions + ): Promise { + if (!this.isSupportedBlockchain(from.blockchain)) { + throw new RubicSdkError(`Rango doesn't support ${from.blockchain} chain!`); + } + + try { + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract(from, options); + + const path = this.getRoutePath(from, toToken); + + const swapParams = await RangoCommonParser.getSwapQueryParams(fromWithoutFee, toToken, { + ...options, + swapperGroups: RANGO_ON_CHAIN_DISABLED_PROVIDERS + }); + + const { route, tx } = await RangoOnChainApiService.getSwapTransaction(swapParams); + const { outputAmountMin, outputAmount } = route as RangoBestRouteSimulationResult; + + const providerGateway = tx!.txTo; + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: new BigNumber(outputAmount) + }); + + const toTokenWeiAmountMin = new BigNumber(outputAmountMin); + + const tradeStruct: RangoOnChainTradeStruct = { + from, + to, + fromWithoutFee, + proxyFeeInfo, + toTokenWeiAmountMin, + gasFeeInfo: { + gasLimit: undefined + }, + slippageTolerance: options.slippageTolerance, + useProxy: options.useProxy, + withDeflation: options.withDeflation, + path + }; + + const gasFeeInfo = + options.gasCalculation === 'calculate' + ? await this.getGasFeeInfo(tradeStruct, providerGateway!) + : null; + + return new RangoOnChainTrade( + { + ...tradeStruct, + gasFeeInfo + }, + options.providerAddress, + providerGateway! + ); + } catch (err) { + return { + type: ON_CHAIN_TRADE_TYPE.RANGO, + error: err + }; + } + } + + protected async getGasFeeInfo( + tradeStruct: RangoOnChainTradeStruct, + providerGateway: string + ): Promise { + try { + const gasPriceInfo = await getGasPriceInfo(tradeStruct.from.blockchain); + const gasLimit = await RangoOnChainTrade.getGasLimit(tradeStruct, providerGateway); + return getGasFeeInfo(gasLimit, gasPriceInfo); + } catch { + return null; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/rango-on-chain-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/rango-on-chain-trade.ts new file mode 100644 index 0000000..d4475d1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/rango-on-chain-trade.ts @@ -0,0 +1,96 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { RangoBestRouteSimulationResult } from 'src/features/common/providers/rango/models/rango-api-best-route-types'; +import { RangoCommonParser } from 'src/features/common/providers/rango/services/rango-parser'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { getOnChainGasData } from 'src/features/on-chain/calculation-manager/utils/get-on-chain-gas-data'; + +import { ON_CHAIN_TRADE_TYPE, OnChainTradeType } from '../../common/models/on-chain-trade-type'; +import { AggregatorEvmOnChainTrade } from '../../common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract'; +import { GetToAmountAndTxDataResponse } from '../../common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { RANGO_ON_CHAIN_DISABLED_PROVIDERS } from './models/rango-on-chain-disabled-providers'; +import { RangoOnChainTradeStruct } from './models/rango-on-chain-trade-types'; +import { RangoOnChainApiService } from './services/rango-on-chain-api-service'; + +export class RangoOnChainTrade extends AggregatorEvmOnChainTrade { + /* @internal */ + public static async getGasLimit( + tradeStruct: RangoOnChainTradeStruct, + providerGateway: string + ): Promise { + const rangoTrade = new RangoOnChainTrade( + tradeStruct, + EvmWeb3Pure.EMPTY_ADDRESS, + providerGateway + ); + + return getOnChainGasData(rangoTrade); + } + + /** + * approveTo address - used in this.web3Public.getAllowance() method + */ + public readonly providerGateway: string; + + public readonly type: OnChainTradeType = ON_CHAIN_TRADE_TYPE.RANGO; + + private readonly _toTokenAmountMin: PriceTokenAmount; + + public get toTokenAmountMin(): PriceTokenAmount { + return this._toTokenAmountMin; + } + + protected get spenderAddress(): string { + return this.useProxy + ? rubicProxyContractAddress[this.from.blockchain].gateway + : this.providerGateway; + } + + public get dexContractAddress(): string { + throw new RubicSdkError('Dex address is unknown before swap is started'); + } + + constructor( + tradeStruct: RangoOnChainTradeStruct, + providerAddress: string, + providerGateway: string + ) { + super(tradeStruct, providerAddress); + + this._toTokenAmountMin = new PriceTokenAmount({ + ...this.to.asStruct, + weiAmount: tradeStruct.toTokenWeiAmountMin + }); + + this.providerGateway = providerGateway; + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + const params = await RangoCommonParser.getSwapQueryParams(this.from, this.to, { + slippageTolerance: this.slippageTolerance, + receiverAddress: options.receiverAddress || this.walletAddress, + swapperGroups: RANGO_ON_CHAIN_DISABLED_PROVIDERS, + fromAddress: this.walletAddress + }); + + const { tx: transaction, route } = await RangoOnChainApiService.getSwapTransaction(params); + + const { outputAmount: toAmount } = route as RangoBestRouteSimulationResult; + + return { + tx: { + data: transaction!.txData!, + to: transaction!.txTo!, + value: transaction!.value || '0', + gas: transaction!.gasLimit ?? undefined, + gasPrice: transaction!.gasPrice ?? undefined + }, + toAmount + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/services/rango-on-chain-api-service.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/services/rango-on-chain-api-service.ts new file mode 100644 index 0000000..82f40c8 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/rango/services/rango-on-chain-api-service.ts @@ -0,0 +1,70 @@ +import { RubicSdkError } from 'src/common/errors'; +import { Cache } from 'src/common/utils/decorators'; +import { Injector } from 'src/core/injector/injector'; +import { RANGO_API_ENDPOINT } from 'src/features/common/providers/rango/constants/rango-api-common'; +import { RangoBestRouteResponse } from 'src/features/common/providers/rango/models/rango-api-best-route-types'; +import { HttpClientParams } from 'src/features/common/providers/rango/models/rango-api-common-types'; +import { RangoTxStatusResponse } from 'src/features/common/providers/rango/models/rango-api-status-types'; +import { RangoSwapTransactionResponse } from 'src/features/common/providers/rango/models/rango-api-swap-types'; +import { + RangoBestRouteQueryParams, + RangoSwapQueryParams, + RangoTxStatusQueryParams +} from 'src/features/common/providers/rango/models/rango-parser-types'; + +export class RangoOnChainApiService { + public static async getBestRoute( + params: RangoBestRouteQueryParams + ): Promise { + const res = await Injector.httpClient.get( + `${RANGO_API_ENDPOINT}/quote`, + { + params: params as unknown as HttpClientParams + } + ); + + if (!res.route || res.error) { + throw new RubicSdkError(res.error ?? 'No available routes in rango.'); + } + + return res; + } + + @Cache({ + maxAge: 15_000 + }) + public static async getSwapTransaction( + params: RangoSwapQueryParams + ): Promise { + const res = await Injector.httpClient.get( + `${RANGO_API_ENDPOINT}/swap`, + { params: params as unknown as HttpClientParams } + ); + + if (!res.route || res.error || !res.tx) { + throw new RubicSdkError(res.error ?? 'No available routes in rango.'); + } + + return res; + } + + /** + * @description Get transaction status data + */ + public static async getTxStatus( + params: RangoTxStatusQueryParams + ): Promise { + const res = await Injector.httpClient.get( + `${RANGO_API_ENDPOINT}/status`, + { params: params as unknown as HttpClientParams } + ); + + if (res.error || !res.bridgeData || !res.status) { + throw new RubicSdkError( + "Can't get status, res has error or null data in getTxStatus method" + ); + } + + return res; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/symbiosis/models/symbiosis-on-chain-supported-blockchains.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/symbiosis/models/symbiosis-on-chain-supported-blockchains.ts new file mode 100644 index 0000000..b331a3b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/symbiosis/models/symbiosis-on-chain-supported-blockchains.ts @@ -0,0 +1,33 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const symbiosisOnChainSupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.BOBA, + BLOCKCHAIN_NAME.BOBA_BSC, + BLOCKCHAIN_NAME.TELOS, + BLOCKCHAIN_NAME.ZK_SYNC, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.POLYGON_ZKEVM, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.MANTLE, + BLOCKCHAIN_NAME.TRON, + BLOCKCHAIN_NAME.SCROLL, + BLOCKCHAIN_NAME.METIS, + BLOCKCHAIN_NAME.ROOTSTOCK, + // Testnets + BLOCKCHAIN_NAME.GOERLI, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN_TESTNET, + BLOCKCHAIN_NAME.FUJI, + BLOCKCHAIN_NAME.MUMBAI, + BLOCKCHAIN_NAME.SCROLL_SEPOLIA, + BLOCKCHAIN_NAME.ZETACHAIN, + BLOCKCHAIN_NAME.MANTA_PACIFIC +] as const; + +export type SymbiosisOnChainSupportedBlockchain = + (typeof symbiosisOnChainSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/symbiosis/models/symbiosis-on-chain-trade-types.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/symbiosis/models/symbiosis-on-chain-trade-types.ts new file mode 100644 index 0000000..a851543 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/symbiosis/models/symbiosis-on-chain-trade-types.ts @@ -0,0 +1,3 @@ +import { EvmOnChainTradeStruct } from '../../../common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export interface SymbiosisTradeStruct extends EvmOnChainTradeStruct {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/symbiosis/symbiosis-on-chain-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/symbiosis/symbiosis-on-chain-provider.ts new file mode 100644 index 0000000..e35c4e3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/symbiosis/symbiosis-on-chain-provider.ts @@ -0,0 +1,100 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { SymbiosisApiService } from 'src/features/common/providers/symbiosis/services/symbiosis-api-service'; +import { SymbiosisParser } from 'src/features/common/providers/symbiosis/services/symbiosis-parser'; + +import { OnChainTradeError } from '../../../models/on-chain-trade-error'; +import { RequiredOnChainCalculationOptions } from '../../common/models/on-chain-calculation-options'; +import { ON_CHAIN_TRADE_TYPE } from '../../common/models/on-chain-trade-type'; +import { AggregatorOnChainProvider } from '../../common/on-chain-aggregator/aggregator-on-chain-provider-abstract'; +import { GasFeeInfo } from '../../common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { OnChainTrade } from '../../common/on-chain-trade/on-chain-trade'; +import { getGasFeeInfo } from '../../common/utils/get-gas-fee-info'; +import { getGasPriceInfo } from '../../common/utils/get-gas-price-info'; +import { symbiosisOnChainSupportedBlockchains } from './models/symbiosis-on-chain-supported-blockchains'; +import { SymbiosisTradeStruct } from './models/symbiosis-on-chain-trade-types'; +import { SymbiosisOnChainTrade } from './symbiosis-on-chain-trade'; + +export class SymbiosisOnChainProvider extends AggregatorOnChainProvider { + public readonly tradeType = ON_CHAIN_TRADE_TYPE.SYMBIOSIS_SWAP; + + protected isSupportedBlockchain(blockchain: BlockchainName): boolean { + return symbiosisOnChainSupportedBlockchains.some(chain => chain === blockchain); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredOnChainCalculationOptions + ): Promise { + if (!this.isSupportedBlockchain(from.blockchain)) { + throw new RubicSdkError(`Symbiosis doesn't support ${from.blockchain} chain!`); + } + + try { + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract(from, options); + const path = this.getRoutePath(from, toToken); + + const swapBody = await SymbiosisParser.getSwapRequestBody(fromWithoutFee, toToken, { + slippage: options.slippageTolerance + }); + const { + approveTo: providerGateway, + tokenAmountOut: { amount: toTokenAmount } + } = await SymbiosisApiService.getOnChainSwapTx(swapBody); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: new BigNumber(toTokenAmount) + }); + + const tradeStruct: SymbiosisTradeStruct = { + from: from as PriceTokenAmount, + to: to as PriceTokenAmount, + fromWithoutFee: fromWithoutFee as PriceTokenAmount, + proxyFeeInfo, + gasFeeInfo: { + gasLimit: undefined + }, + slippageTolerance: options.slippageTolerance, + useProxy: options.useProxy, + withDeflation: options.withDeflation, + path + }; + + const gasFeeInfo = + options.gasCalculation === 'calculate' + ? await this.getGasFeeInfo(tradeStruct, providerGateway) + : null; + + return new SymbiosisOnChainTrade( + { + ...tradeStruct, + gasFeeInfo + }, + options.providerAddress, + providerGateway + ); + } catch (err) { + return { + type: ON_CHAIN_TRADE_TYPE.SYMBIOSIS_SWAP, + error: err + }; + } + } + + protected async getGasFeeInfo( + tradeStruct: SymbiosisTradeStruct, + providerGateway: string + ): Promise { + try { + const gasPriceInfo = await getGasPriceInfo(tradeStruct.from.blockchain); + const gasLimit = await SymbiosisOnChainTrade.getGasLimit(tradeStruct, providerGateway); + return getGasFeeInfo(gasLimit, gasPriceInfo); + } catch { + return null; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/symbiosis/symbiosis-on-chain-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/symbiosis/symbiosis-on-chain-trade.ts new file mode 100644 index 0000000..6396d94 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/symbiosis/symbiosis-on-chain-trade.ts @@ -0,0 +1,71 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { SymbiosisApiService } from 'src/features/common/providers/symbiosis/services/symbiosis-api-service'; +import { SymbiosisParser } from 'src/features/common/providers/symbiosis/services/symbiosis-parser'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { getOnChainGasData } from 'src/features/on-chain/calculation-manager/utils/get-on-chain-gas-data'; + +import { ON_CHAIN_TRADE_TYPE, OnChainTradeType } from '../../common/models/on-chain-trade-type'; +import { AggregatorEvmOnChainTrade } from '../../common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract'; +import { GetToAmountAndTxDataResponse } from '../../common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { SymbiosisTradeStruct } from './models/symbiosis-on-chain-trade-types'; + +export class SymbiosisOnChainTrade extends AggregatorEvmOnChainTrade { + /* @internal */ + public static async getGasLimit( + tradeStruct: SymbiosisTradeStruct, + providerGateway: string + ): Promise { + const symbiosisTrade = new SymbiosisOnChainTrade( + tradeStruct, + EvmWeb3Pure.EMPTY_ADDRESS, + providerGateway + ); + + return getOnChainGasData(symbiosisTrade); + } + + public readonly type: OnChainTradeType = ON_CHAIN_TRADE_TYPE.SYMBIOSIS_SWAP; + + public readonly providerGateway: string; + + protected get spenderAddress(): string { + return this.useProxy + ? rubicProxyContractAddress[this.from.blockchain].gateway + : this.providerGateway; + } + + public get dexContractAddress(): string { + throw new RubicSdkError('Dex address is unknown before swap is started'); + } + + constructor( + tradeStruct: SymbiosisTradeStruct, + providerAddress: string, + providerGateway: string + ) { + super(tradeStruct, providerAddress); + + this.providerGateway = providerGateway; + } + + //@TODO - CHECK IF we need to pass fromAddress with proxy or remove it after listing + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + const requestBody = await SymbiosisParser.getSwapRequestBody(this.from, this.to, { + receiverAddress: options.receiverAddress, + fromAddress: this.walletAddress, + slippage: this.slippageTolerance + }); + + const { tx, tokenAmountOut } = await SymbiosisApiService.getOnChainSwapTx(requestBody); + + return { + tx, + toAmount: tokenAmountOut.amount + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/constants/xy-dex-supported-blockchains.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/constants/xy-dex-supported-blockchains.ts new file mode 100644 index 0000000..e3e1b8b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/constants/xy-dex-supported-blockchains.ts @@ -0,0 +1,22 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const xyDexSupportedBlockchains = [ + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.FANTOM, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.MOONRIVER, + BLOCKCHAIN_NAME.ZK_SYNC, + BLOCKCHAIN_NAME.POLYGON_ZKEVM, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.SCROLL, + BLOCKCHAIN_NAME.MANTLE, + BLOCKCHAIN_NAME.BLAST, + BLOCKCHAIN_NAME.XLAYER +] as const; + +export type XyDexSupportedBlockchains = (typeof xyDexSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/models/xy-dex-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/models/xy-dex-trade-struct.ts new file mode 100644 index 0000000..95f1c7d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/models/xy-dex-trade-struct.ts @@ -0,0 +1,6 @@ +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export interface XyDexTradeStruct extends EvmOnChainTradeStruct { + contractAddress: string; + provider: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/xy-dex-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/xy-dex-provider.ts new file mode 100644 index 0000000..4ff1086 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/xy-dex-provider.ts @@ -0,0 +1,143 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, Token } from 'src/common/tokens'; +import { combineOptions } from 'src/common/utils/options'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Injector } from 'src/core/injector/injector'; +import { + XY_AFFILIATE_ADDRESS, + XY_API_ENDPOINT, + XY_NATIVE_ADDRESS +} from 'src/features/common/providers/xy/constants/xy-api-params'; +import { XyQuoteRequest } from 'src/features/common/providers/xy/models/xy-quote-request'; +import { XyQuoteResponse } from 'src/features/common/providers/xy/models/xy-quote-response'; +import { xyAnalyzeStatusCode } from 'src/features/common/providers/xy/utils/xy-utils'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { xySupportedBlockchains } from 'src/features/cross-chain/calculation-manager/providers/xy-provider/constants/xy-supported-blockchains'; +import { LifiTrade } from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/lifi-trade'; +import { LifiTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-trade-struct'; +import { XyDexTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/models/xy-dex-trade-struct'; +import { XyDexTrade } from 'src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/xy-dex-trade'; +import { + OnChainCalculationOptions, + RequiredOnChainCalculationOptions +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AggregatorOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-on-chain-provider-abstract'; +import { GasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { getGasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info'; +import { getGasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-price-info'; +import { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options'; + +import { OnChainTradeError } from '../../../models/on-chain-trade-error'; + +export class XyDexProvider extends AggregatorOnChainProvider { + private readonly defaultOptions = evmProviderDefaultOptions; + + public readonly tradeType = ON_CHAIN_TRADE_TYPE.XY_DEX; + + protected isSupportedBlockchain(blockchain: BlockchainName): boolean { + return xySupportedBlockchains.some(item => item === blockchain); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options?: OnChainCalculationOptions + ): Promise { + if (!this.isSupportedBlockchain(from.blockchain)) { + throw new RubicSdkError('Blockchain is not supported'); + } + const fromAddress = + options?.useProxy || this.defaultOptions.useProxy + ? rubicProxyContractAddress[from.blockchain].gateway + : this.getWalletAddress(from.blockchain); + const fullOptions = combineOptions(options, { + ...this.defaultOptions, + fromAddress + }); + + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract(from, fullOptions); + + const { toTokenAmountInWei, contractAddress, provider } = await this.getTradeInfo( + from, + toToken, + fullOptions + ); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: toTokenAmountInWei + }); + + const tradeStruct: XyDexTradeStruct = { + contractAddress, + from, + to, + slippageTolerance: fullOptions.slippageTolerance, + gasFeeInfo: null, + useProxy: fullOptions.useProxy, + proxyFeeInfo, + fromWithoutFee, + withDeflation: fullOptions.withDeflation, + usedForCrossChain: fullOptions.usedForCrossChain, + path: [from, to], + provider + }; + + return new XyDexTrade(tradeStruct, fullOptions.providerAddress); + } + + private async getTradeInfo( + from: PriceTokenAmount, + toToken: Token, + options: RequiredOnChainCalculationOptions + ): Promise<{ + toTokenAmountInWei: BigNumber; + estimatedGas: string; + contractAddress: string; + provider: string; + }> { + const chainId = blockchainId[from.blockchain]; + const srcQuoteTokenAddress = from.isNative ? XY_NATIVE_ADDRESS : from.address; + const dstQuoteTokenAddress = toToken.isNative ? XY_NATIVE_ADDRESS : toToken.address; + + const quoteTradeParams: XyQuoteRequest = { + srcChainId: chainId, + srcQuoteTokenAddress, + srcQuoteTokenAmount: from.stringWeiAmount, + dstChainId: chainId, + dstQuoteTokenAddress, + slippage: options.slippageTolerance * 100, + affiliate: XY_AFFILIATE_ADDRESS + }; + + const trade = await Injector.httpClient.get(`${XY_API_ENDPOINT}/quote`, { + params: { ...quoteTradeParams } + }); + + if (!trade.success) { + xyAnalyzeStatusCode(trade.errorCode, trade.errorMsg); + } + + const bestRoute = trade.routes[0]!; + + return { + toTokenAmountInWei: new BigNumber(bestRoute.dstQuoteTokenAmount), + estimatedGas: bestRoute.estimatedGas, + contractAddress: bestRoute.contractAddress, + provider: bestRoute.srcSwapDescription.provider + }; + } + + protected async getGasFeeInfo(lifiTradeStruct: LifiTradeStruct): Promise { + try { + const gasPriceInfo = await getGasPriceInfo(lifiTradeStruct.from.blockchain); + const gasLimit = await LifiTrade.getGasLimit(lifiTradeStruct); + return getGasFeeInfo(gasLimit, gasPriceInfo); + } catch { + return null; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/xy-dex-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/xy-dex-trade.ts new file mode 100644 index 0000000..3695af6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/xy-dex-trade.ts @@ -0,0 +1,102 @@ +import BigNumber from 'bignumber.js'; +import { Cache } from 'src/common/utils/decorators'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { + XY_AFFILIATE_ADDRESS, + XY_API_ENDPOINT, + XY_NATIVE_ADDRESS +} from 'src/features/common/providers/xy/constants/xy-api-params'; +import { XyBuildTxRequest } from 'src/features/common/providers/xy/models/xy-build-tx-request'; +import { XyBuildTxResponse } from 'src/features/common/providers/xy/models/xy-build-tx-response'; +import { xyAnalyzeStatusCode } from 'src/features/common/providers/xy/utils/xy-utils'; +import { XyDexTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/models/xy-dex-trade-struct'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AggregatorEvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; + +export class XyDexTrade extends AggregatorEvmOnChainTrade { + /** @internal */ + public static async getGasLimit(tradeStruct: XyDexTradeStruct): Promise { + const fromBlockchain = tradeStruct.from.blockchain; + const walletAddress = + Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address; + if (!walletAddress) { + return null; + } + + try { + const transactionConfig = await new XyDexTrade( + tradeStruct, + EvmWeb3Pure.EMPTY_ADDRESS + ).encode({ fromAddress: walletAddress }); + + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const gasLimit = ( + await web3Public.batchEstimatedGas(walletAddress, [transactionConfig]) + )[0]; + + if (!gasLimit?.isFinite()) { + return null; + } + return gasLimit; + } catch (_err) { + return null; + } + } + + public readonly dexContractAddress: string; + + public type = ON_CHAIN_TRADE_TYPE.XY_DEX; + + private readonly provider: string; + + constructor(tradeStruct: XyDexTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + this.dexContractAddress = tradeStruct.contractAddress; + this.provider = tradeStruct.provider; + } + + public async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + const receiver = options.receiverAddress || this.walletAddress; + + const chainId = blockchainId[this.from.blockchain]; + const srcQuoteTokenAddress = this.from.isNative ? XY_NATIVE_ADDRESS : this.from.address; + const dstQuoteTokenAddress = this.to.isNative ? XY_NATIVE_ADDRESS : this.to.address; + + const quoteTradeParams: XyBuildTxRequest = { + srcChainId: chainId, + srcQuoteTokenAddress, + srcQuoteTokenAmount: this.from.stringWeiAmount, + dstChainId: chainId, + dstQuoteTokenAddress, + slippage: this.slippageTolerance * 100, + receiver, + srcSwapProvider: this.provider, + affiliate: XY_AFFILIATE_ADDRESS + }; + + const tradeData = await this.getResponseFromApiToTransactionRequest(quoteTradeParams); + + if (!tradeData.success) { + xyAnalyzeStatusCode(tradeData.errorCode, tradeData.errorMsg); + } + + return { tx: tradeData.tx, toAmount: tradeData.route.dstQuoteTokenAmount }; + } + + @Cache({ + maxAge: 15_000 + }) + private async getResponseFromApiToTransactionRequest( + params: XyBuildTxRequest + ): Promise { + return Injector.httpClient.get(`${XY_API_ENDPOINT}/buildTx`, { + params: { ...params } + }); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zetaswap/constants/zetaswap-supported-blockchains.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zetaswap/constants/zetaswap-supported-blockchains.ts new file mode 100644 index 0000000..1a25f1d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zetaswap/constants/zetaswap-supported-blockchains.ts @@ -0,0 +1,6 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const zetaswapOnChainSupportedBlockchains = [BLOCKCHAIN_NAME.ZETACHAIN] as const; + +export type ZetaswapOnChainSupportedBlockchains = + (typeof zetaswapOnChainSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zetaswap/zetaswap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zetaswap/zetaswap-provider.ts new file mode 100644 index 0000000..9aca4c0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zetaswap/zetaswap-provider.ts @@ -0,0 +1,36 @@ +import { NotSupportedBlockchain } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; + +import { OnChainTradeError } from '../../../models/on-chain-trade-error'; +import { RequiredOnChainCalculationOptions } from '../../common/models/on-chain-calculation-options'; +import { ON_CHAIN_TRADE_TYPE } from '../../common/models/on-chain-trade-type'; +import { NativeRouterTradeInstance } from '../common/native-router-abstract/models/native-router-trade-struct'; +import { NativeRouterAbstractProvider } from '../common/native-router-abstract/native-router-abstract-provider'; +import { zetaswapOnChainSupportedBlockchains } from './constants/zetaswap-supported-blockchains'; +import { ZetaSwapTrade } from './zetaswap-trade'; + +export class ZetaSwapProvider extends NativeRouterAbstractProvider { + public readonly tradeType = ON_CHAIN_TRADE_TYPE.ZETA_SWAP; + + protected isSupportedBlockchain(blockchain: BlockchainName): boolean { + return zetaswapOnChainSupportedBlockchains.some(chain => chain === blockchain); + } + + protected createNativeRouterTradeInstance( + tradeInstance: NativeRouterTradeInstance + ): ZetaSwapTrade { + return new ZetaSwapTrade(tradeInstance); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredOnChainCalculationOptions + ): Promise { + if (!this.isSupportedBlockchain(from.blockchain)) { + throw new NotSupportedBlockchain(); + } + return super.calculate(from, toToken, options); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zetaswap/zetaswap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zetaswap/zetaswap-trade.ts new file mode 100644 index 0000000..72a5bec --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zetaswap/zetaswap-trade.ts @@ -0,0 +1,8 @@ +import { ON_CHAIN_TRADE_TYPE, OnChainTradeType } from '../../common/models/on-chain-trade-type'; +import { NativeRouterAbstractTrade } from '../common/native-router-abstract/native-router-abstract-trade'; + +export class ZetaSwapTrade extends NativeRouterAbstractTrade { + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ZETA_SWAP; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/constants.ts new file mode 100644 index 0000000..3f2ec3c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/constants.ts @@ -0,0 +1,8 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const zrxApiParams = { + apiBaseUrl: { + [BLOCKCHAIN_NAME.ETHEREUM]: 'https://api.0x.org/' + }, + nativeTokenAddress: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/constants/zrx-supported-blockchains.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/constants/zrx-supported-blockchains.ts new file mode 100644 index 0000000..92f481c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/constants/zrx-supported-blockchains.ts @@ -0,0 +1,5 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const zeroXSupportedBlockchains = [BLOCKCHAIN_NAME.ETHEREUM] as const; + +export type ZeroXSupportedBlockchains = (typeof zeroXSupportedBlockchains)[number]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-quote-request.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-quote-request.ts new file mode 100644 index 0000000..d107381 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-quote-request.ts @@ -0,0 +1,9 @@ +export interface ZrxQuoteRequest { + params: { + sellToken: string; + buyToken: string; + sellAmount: string; + slippagePercentage: string; + affiliateAddress?: string; + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-trade-struct.ts new file mode 100644 index 0000000..6a3741c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-trade-struct.ts @@ -0,0 +1,6 @@ +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export interface ZrxTradeStruct extends EvmOnChainTradeStruct { + affiliateAddress?: string; + routerAddress: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-types.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-types.ts new file mode 100644 index 0000000..c13937a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-types.ts @@ -0,0 +1,14 @@ +export interface ZrxQuoteResponse { + allowanceTarget: string; + buyTokenAddress: string; + buyAmount: string; + data: string; + sellTokenAddress: string; + sellAmount: string; + chainId: number; + to: string; + gas: string; + gasPrice: string; + estimatedGas: string; + value: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/utils.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/utils.ts new file mode 100644 index 0000000..95c5122 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/utils.ts @@ -0,0 +1,11 @@ +import { RubicSdkError } from 'src/common/errors'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { zrxApiParams } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/constants'; + +export function getZrxApiBaseUrl(blockchain: BlockchainName): string { + const { apiBaseUrl } = zrxApiParams; + if (!Object.keys(apiBaseUrl).includes(blockchain)) { + throw new RubicSdkError(`Zrx doesn't support ${blockchain} blockchain`); + } + return apiBaseUrl[blockchain as keyof typeof apiBaseUrl]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-api-service.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-api-service.ts new file mode 100644 index 0000000..8a0696d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-api-service.ts @@ -0,0 +1,21 @@ +import { Cache } from 'src/common/utils/decorators'; +import { Injector } from 'src/core/injector/injector'; +import { ZeroXSupportedBlockchains } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/constants/zrx-supported-blockchains'; +import { ZrxQuoteRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-quote-request'; +import { ZrxQuoteResponse } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-types'; +import { getZrxApiBaseUrl } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/utils'; + +export class ZrxApiService { + public static getTradeData( + params: ZrxQuoteRequest, + blockchain: ZeroXSupportedBlockchains + ): Promise { + const endpoint = this.getApiBaseUrl(blockchain); + return Injector.httpClient.get(`${endpoint}swap/v1/quote`, params); + } + + @Cache + private static getApiBaseUrl(blockchain: ZeroXSupportedBlockchains): string { + return getZrxApiBaseUrl(blockchain); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-provider.ts new file mode 100644 index 0000000..17c8892 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-provider.ts @@ -0,0 +1,106 @@ +import BigNumber from 'bignumber.js'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { combineOptions } from 'src/common/utils/options'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { createTokenNativeAddressProxy } from 'src/features/common/utils/token-native-address-proxy'; +import { LifiTrade } from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/lifi-trade'; +import { LifiTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/lifi/models/lifi-trade-struct'; +import { zrxApiParams } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/constants'; +import { + ZeroXSupportedBlockchains, + zeroXSupportedBlockchains +} from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/constants/zrx-supported-blockchains'; +import { ZrxQuoteRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-quote-request'; +import { ZrxTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-trade-struct'; +import { ZrxApiService } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-api-service'; +import { ZrxTrade } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-trade'; +import { + OnChainCalculationOptions, + RequiredOnChainCalculationOptions +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AggregatorOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-on-chain-provider-abstract'; +import { GasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { getGasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info'; +import { getGasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-price-info'; +import { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options'; + +export class ZrxProvider extends AggregatorOnChainProvider { + private readonly defaultOptions: RequiredOnChainCalculationOptions = evmProviderDefaultOptions; + + protected isSupportedBlockchain(blockchain: BlockchainName): boolean { + return zeroXSupportedBlockchains.some(item => item === blockchain); + } + + public tradeType = ON_CHAIN_TRADE_TYPE.ZRX; + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options?: OnChainCalculationOptions + ): Promise { + const fullOptions = combineOptions(options, this.defaultOptions); + + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract(from, fullOptions); + + const fromClone = createTokenNativeAddressProxy( + fromWithoutFee, + zrxApiParams.nativeTokenAddress + ); + const toClone = createTokenNativeAddressProxy(toToken, zrxApiParams.nativeTokenAddress); + + const affiliateAddress = fullOptions.zrxAffiliateAddress; + const quoteParams: ZrxQuoteRequest = { + params: { + sellToken: fromClone.address, + buyToken: toClone.address, + sellAmount: fromClone.stringWeiAmount, + slippagePercentage: fullOptions.slippageTolerance.toString(), + ...(affiliateAddress && { affiliateAddress }) + } + }; + const apiTradeData = await ZrxApiService.getTradeData( + quoteParams, + from.blockchain as ZeroXSupportedBlockchains + ); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: new BigNumber(apiTradeData.buyAmount) + }); + + const tradeStruct: ZrxTradeStruct = { + from, + to, + slippageTolerance: fullOptions.slippageTolerance, + gasFeeInfo: null, + path: [from, to], + useProxy: fullOptions.useProxy, + proxyFeeInfo, + fromWithoutFee, + withDeflation: fullOptions.withDeflation, + usedForCrossChain: fullOptions.usedForCrossChain, + ...(affiliateAddress && { affiliateAddress }), + routerAddress: apiTradeData.to + }; + if (fullOptions.gasCalculation === 'disabled') { + return new ZrxTrade(tradeStruct, fullOptions.providerAddress); + } + + return new ZrxTrade(tradeStruct, fullOptions.providerAddress); + } + + /** + * Fetches zrx data from api. + */ + + protected async getGasFeeInfo(lifiTradeStruct: LifiTradeStruct): Promise { + try { + const gasPriceInfo = await getGasPriceInfo(lifiTradeStruct.from.blockchain); + const gasLimit = await LifiTrade.getGasLimit(lifiTradeStruct); + return getGasFeeInfo(gasLimit, gasPriceInfo); + } catch { + return null; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-trade.ts new file mode 100644 index 0000000..fcd3050 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-trade.ts @@ -0,0 +1,94 @@ +import BigNumber from 'bignumber.js'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { checkUnsupportedReceiverAddress } from 'src/features/common/utils/check-unsupported-receiver-address'; +import { ZeroXSupportedBlockchains } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/constants/zrx-supported-blockchains'; +import { ZrxQuoteRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-quote-request'; +import { ZrxTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/models/zrx-trade-struct'; +import { ZrxApiService } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-api-service'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AggregatorEvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; + +export class ZrxTrade extends AggregatorEvmOnChainTrade { + private readonly affiliateAddress: string | undefined; + + /** @internal */ + public static async getGasLimit(zrxTradeStruct: ZrxTradeStruct): Promise { + const fromBlockchain = zrxTradeStruct.from.blockchain; + const walletAddress = + Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address; + if (!walletAddress) { + return null; + } + + try { + const transactionConfig = await new ZrxTrade( + zrxTradeStruct, + EvmWeb3Pure.EMPTY_ADDRESS + ).encode({ fromAddress: walletAddress }); + + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const gasLimit = ( + await web3Public.batchEstimatedGas(walletAddress, [transactionConfig]) + )[0]; + + if (!gasLimit?.isFinite()) { + return null; + } + return gasLimit; + } catch (_err) { + return null; + } + } + + public readonly dexContractAddress: string; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ZRX; + } + + constructor(tradeStruct: ZrxTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + this.affiliateAddress = tradeStruct.affiliateAddress; + this.dexContractAddress = tradeStruct.routerAddress; + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + checkUnsupportedReceiverAddress( + options?.receiverAddress, + options?.fromAddress || this.walletAddress + ); + + const quoteParams: ZrxQuoteRequest = { + params: { + sellToken: this.from.address, + buyToken: this.to.address, + sellAmount: this.from.stringWeiAmount, + slippagePercentage: this.slippageTolerance.toString(), + ...(this.affiliateAddress && { affiliateAddress: this.affiliateAddress }) + } + }; + const tradeData = await ZrxApiService.getTradeData( + quoteParams, + this.from.blockchain as ZeroXSupportedBlockchains + ); + + const { gas, gasPrice } = this.getGasParams(options); + const config = { + to: tradeData.to, + data: tradeData.data, + value: tradeData.value, + gas, + gasPrice + }; + + return { tx: config, toAmount: tradeData.buyAmount }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/evm-wrap-trade/evm-wrap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/evm-wrap-trade/evm-wrap-trade.ts new file mode 100644 index 0000000..98b5ac2 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/evm-wrap-trade/evm-wrap-trade.ts @@ -0,0 +1,69 @@ +import { RubicSdkError } from 'src/common/errors'; +import { wrappedAddress } from 'src/common/tokens/constants/wrapped-addresses'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { wrapAbi } from 'src/features/on-chain/calculation-manager/providers/common/evm-wrap-trade/wrap-abi'; +import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export class EvmWrapTrade extends EvmOnChainTrade { + public get dexContractAddress(): string { + return this.from.isNative ? this.to.address : this.from.address; + } + + public readonly type = ON_CHAIN_TRADE_TYPE.WRAPPED; + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + await this.checkFromAddress(options.fromAddress, true); + + const methodName = this.from.isNative ? 'deposit' : 'withdraw'; + const gasParams = this.getGasParams(options); + + const config = EvmWeb3Pure.encodeMethodCall( + this.dexContractAddress, + wrapAbi, + methodName, + this.from.isNative ? [] : [this.from.stringWeiAmount], + this.from.isNative ? this.from.stringWeiAmount : '0', + gasParams + ); + + return { tx: config, toAmount: this.to.stringWeiAmount }; + } + + public constructor(evmOnChainTradeStruct: EvmOnChainTradeStruct, providerAddress: string) { + super(evmOnChainTradeStruct, providerAddress); + } + + public static isSupportedBlockchain(blockchain: EvmBlockchainName): boolean { + return Boolean(wrappedAddress?.[blockchain]); + } + + public static isSupportedTrade( + blockchain: EvmBlockchainName, + fromAddress: string, + toAddress: string + ): boolean { + if (!EvmWrapTrade.isSupportedBlockchain) { + throw new RubicSdkError('Trade is not supported'); + } + const wethAddress = wrappedAddress[blockchain]!; + + return ( + (compareAddresses(fromAddress, EvmWeb3Pure.EMPTY_ADDRESS) && + compareAddresses(toAddress, wethAddress)) || + (compareAddresses(toAddress, EvmWeb3Pure.EMPTY_ADDRESS) && + compareAddresses(fromAddress, wethAddress)) + ); + } + + public async needApprove(_fromAddress?: string): Promise { + return false; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/evm-wrap-trade/wrap-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/evm-wrap-trade/wrap-abi.ts new file mode 100644 index 0000000..55516b0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/evm-wrap-trade/wrap-abi.ts @@ -0,0 +1,22 @@ +import { AbiItem } from 'web3-utils'; + +export const wrapAbi: AbiItem[] = [ + { + constant: false, + inputs: [{ name: 'wad', type: 'uint256' }], + name: 'withdraw', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function' + }, + { + constant: false, + inputs: [], + name: 'deposit', + outputs: [], + payable: true, + stateMutability: 'payable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options.ts new file mode 100644 index 0000000..e66b574 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options.ts @@ -0,0 +1,77 @@ +import { IsDeflationToken } from 'src/features/deflation-token-manager/models/is-deflation-token'; +import { MarkRequired } from 'ts-essentials'; + +/** + * Stores options for calculating trade. + */ +export interface OnChainCalculationOptions { + /** + * Takes value from 0 to 1. + */ + readonly slippageTolerance?: number; + + /** + * Transaction deadline, passed in minutes. + */ + readonly deadlineMinutes?: number; + + /** + * Disabled or enables gas fee calculation. + * `rubicOptimisation` options means, that gas fee is converted into usd + * and subtracted from output token amount, also converted in usd. + */ + readonly gasCalculation?: 'disabled' | 'calculate' | 'rubicOptimisation'; + + /** + * If true, then only direct token pairs can be used in calculation. + */ + readonly disableMultihops?: boolean; + + /** + * User wallet address, from which transaction will be sent. + */ + readonly fromAddress?: string; + + /** + * Affiliate address for zrx provider. + */ + readonly zrxAffiliateAddress?: string; + + readonly providerAddress?: string; + + /** + * @internal + * Wrapped native address. + */ + readonly wrappedAddress?: string; + + /** + * @internal + * True, if trade must be swapped through on-chain proxy contract. + * False, if trade must be swapped through dex directly. + * Default is false. + */ + readonly useProxy?: boolean; + + /** + * @internal + * True, if trade must be swapped through rubic proxy contract. + * False, if trade must be swapped through dex directly. + * Default is false. + */ + readonly usedForCrossChain?: boolean; + + /** + * @internal + * Contains information whether tokens are deflation or not. + */ + readonly withDeflation?: { + from: IsDeflationToken; + to: IsDeflationToken; + }; +} + +export type RequiredOnChainCalculationOptions = MarkRequired< + OnChainCalculationOptions, + 'slippageTolerance' | 'gasCalculation' | 'providerAddress' | 'useProxy' | 'withDeflation' +>; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info.ts new file mode 100644 index 0000000..1b0f5db --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info.ts @@ -0,0 +1,18 @@ +import { PriceTokenAmount } from 'src/common/tokens'; + +export interface OnChainPlatformFee { + percent: number; + token: PriceTokenAmount; +} + +export interface OnChainProxyFeeInfo { + /** + * Fee in native token, attached as additional value. + */ + fixedFeeToken: PriceTokenAmount; + + /** + * Fee in percents of source token. + */ + platformFee: OnChainPlatformFee; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type.ts new file mode 100644 index 0000000..3a1df28 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type.ts @@ -0,0 +1,168 @@ +/** + * List of on-chain trade types. + */ +export const ON_CHAIN_TRADE_TYPE = { + '10K_SWAP': '10K_SWAP', + + AERODROME: 'AERODROME', + ACRYPTOS: 'ACRYPTOS', + ALDRIN_EXCHANGE: 'ALDRIN_EXCHANGE', + ALGEBRA: 'ALGEBRA', + ALGEBRA_INTEGRAL: 'ALGEBRA_INTEGRAL', + ANNEX: 'ANNEX', + APE_SWAP: 'APE_SWAP', + ARTH_SWAP: 'ARTH_SWAP', + ASTRO_SWAP: 'ASTRO_SWAP', + ASCENT: 'ASCENT', + AURORA_SWAP: 'AURORA_SWAP', + AVNU: 'AVNU', + + BABY_SWAP: 'BABY_SWAP', + BALANCER: 'BALANCER', + BASE_SWAP: 'BASE_SWAP', + BEAM_SWAP: 'BEAM_SWAP', + BI_SWAP: 'BI_SWAP', + BRIDGERS: 'BRIDGERS', + + CAMELOT: 'CAMELOT', + CHERRY_SWAP: 'CHERRY_SWAP', + CLAIM_SWAP: 'CLAIM_SWAP', + CREMA_FINANCE: 'CREMA_FINANCE', + CRO_SWAP: 'CRO_SWAP', + CRONA_SWAP: 'CRONA_SWAP', + CROPPER_FINANCE: 'CROPPER_FINANCE', + CROW_FI: 'CROW_FI', + CRO_DEX: 'CRO_DEX', + CURVE: 'CURVE', + + DEFI_PLAZA: 'DEFI_PLAZA', + DEFI_SWAP: 'DEFI_SWAP', + DFYN: 'DFYN', + DLN: 'DLN', + DODO: 'DODO', + DYSTOPIA: 'DYSTOPIA', + + ECHO_DEX: 'ECHO_DEX', + EDDY_FINANCE: 'EDDY_FINANCE', + ELK: 'ELK', + + FENIX_V2: 'FENIX_V2', + FENIX_V3: 'FENIX_V3', + FINKUJIRA: 'FINKUJIRA', + FUSIONX: 'FUSIONX', + + HONEY_SWAP: 'HONEY_SWAP', + HORIZONDEX: 'HORIZONDEX', + + JET_SWAP: 'JET_SWAP', + JOE: 'JOE', + JUPITER: 'JUPITER', + JUPITER_SWAP: 'JUPITER_SWAP', + J_SWAP: 'J_SWAP', + + KIM: 'KIM', + KYBER_SWAP: 'KYBER_SWAP', + + LUA_SWAP: 'LUA_SWAP', + LIFI: 'LIFI', + + MAVERICK: 'MAVERICK', + MDEX: 'MDEX', + MERLIN_SWAP: 'MERLIN_SWAP', + MESH_SWAP: 'MESH_SWAP', + MM_FINANCE: 'MM_FINANCE', + MOJITO_SWAP: 'MOJITO_SWAP', + MUTE_SWAP: 'MUTE_SWAP', + + NET_SWAP: 'NET_SWAP', + NATIVE_ROUTER: 'NATIVE_ROUTER', + + ODOS: 'ODOS', + OKC_SWAP: 'OKC_SWAP', + OKU_SWAP: 'OKU_SWAP', + ONE_INCH: 'ONE_INCH', + ONE_MOON: 'ONE_MOON', + ONE_SOL: 'ONE_SOL', + OMNIDEX: 'OMNIDEX', + OOLONG_SWAP: 'OOLONG_SWAP', + OPEN_OCEAN: 'OPEN_OCEAN', + ORCA_SWAP: 'ORCA_SWAP', + OSMOSIS_SWAP: 'OSMOSIS_SWAP', + + PANCAKE_SWAP: 'PANCAKE_SWAP', + PANCAKE_SWAP_V3: 'PANCAKE_SWAP_V3', + PANGOLIN: 'PANGOLIN', + PARA_SWAP: 'PARA_SWAP', + PEGASYS: 'PEGASYS', + PHOTON_SWAP: 'PHOTON_SWAP', + POLYDEX: 'POLYDEX', + PITEAS: 'PITEAS', + + QUICK_SWAP: 'QUICK_SWAP', + QUICK_SWAP_V3: 'QUICK_SWAP_V3', + + PULSEX_V1: 'PULSEX_V1', + PULSEX_V2: 'PULSEX_V2', + + RANGO: 'RANGO', + RAYDIUM: 'RAYDIUM', + REF_FINANCE: 'REF_FINANCE', + REN_BTC: 'REN_BTC', + + SABER_STABLE_SWAP: 'SABER_STABLE_SWAP', + SAROS_SWAP: 'SAROS_SWAP', + SERUM: 'SERUM', + SHIBA_SWAP: 'SHIBA_SWAP', + SMOOTHY: 'SMOOTHY', + SOLANA: 'SOLANA', + SOLAR_BEAM: 'SOLAR_BEAM', + SPACEFI_SWAP: 'SPACEFI_SWAP', + SPIRIT_SWAP: 'SPIRIT_SWAP', + SPL_TOKEN_SWAP: 'SPL_TOKEN_SWAP', + SPOOKY_SWAP: 'SPOOKY_SWAP', + SOUL_SWAP: 'SOUL_SWAP', + STELLA_SWAP: 'STELLA_SWAP', + SUN_SWAP: 'SUN_SWAP', + SURFDEX: 'SURFDEX', + SUSHI_SWAP: 'SUSHI_SWAP', + SYNAPSE: 'SYNAPSE', + SYNC_SWAP: 'SYNC_SWAP', + + SYMBIOSIS_SWAP: 'SYMBIOSIS_SWAP', // Fake to show swap via unknown symbiosis dex + + TRADER: 'TRADER', + TRISOLARIS: 'TRISOLARIS', + + IZUMI: 'IZUMI', + + UBE_SWAP: 'UBE_SWAP', + UNISWAP_V2: 'UNISWAP_V2', + UNI_SWAP_V3: 'UNI_SWAP_V3', + + VERSE: 'VERSE', + VIPER_SWAP: 'VIPER_SWAP', + VOLTAGE_SWAP: 'VOLTAGE_SWAP', + VOOI: 'VOOI', + VVS_FINANCE: 'VVS_FINANCE', + + WAGYU_SWAP: 'WAGYU_SWAP', + WANNA_SWAP: 'WANNA_SWAP', + WAULT_SWAP: 'WAULT_SWAP', + WOO_FI: 'WOO_FI', + WRAPPED: 'WRAPPED', + WYND: 'WYND', + + YUZU_SWAP: 'YUZU_SWAP', + + XY_DEX: 'XY_DEX', + + ZAPPY: 'ZAPPY', + ZIP_SWAP: 'ZIP_SWAP', + ZRX: 'ZRX', + ZETA_SWAP: 'ZETA_SWAP' +} as const; + +/** + * On-chain trade type. + */ +export type OnChainTradeType = keyof typeof ON_CHAIN_TRADE_TYPE; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract.ts new file mode 100644 index 0000000..6300898 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract.ts @@ -0,0 +1,45 @@ +import BigNumber from 'bignumber.js'; +import { TokenUtils } from 'src/common/utils/token-utils'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; + +import { EvmOnChainTrade } from '../on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +export abstract class AggregatorEvmOnChainTrade extends EvmOnChainTrade { + public async getTxConfigAndCheckAmount( + options: EncodeTransactionOptions + ): Promise { + if (this.lastTransactionConfig && options.useCacheData) { + return this.lastTransactionConfig; + } + + const { tx, toAmount } = await this.getTransactionConfigAndAmount(options); + + const gasLimit = tx.gas && parseInt(tx.gas, 16).toString(); + const gasPrice = tx.gasPrice && parseInt(tx.gasPrice, 16).toString(); + + const config = { + data: tx.data, + to: tx.to, + value: tx.value, + gas: gasLimit, + gasPrice + }; + + const newToTokenAmountMin = TokenUtils.getMinWeiAmountString( + new BigNumber(toAmount), + this.slippageTolerance + ); + + this.lastTransactionConfig = config; + setTimeout(() => { + this.lastTransactionConfig = null; + }, 15_000); + + if (!options.skipAmountCheck) { + this.checkAmountChange(newToTokenAmountMin, this.toTokenAmountMin.stringWeiAmount); + } + + return config; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-on-chain-provider-abstract.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-on-chain-provider-abstract.ts new file mode 100644 index 0000000..837bd50 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-on-chain-provider-abstract.ts @@ -0,0 +1,68 @@ +import { PriceToken, PriceTokenAmount, Token } from 'src/common/tokens'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info'; +import { Web3PrivateSupportedBlockchain } from 'src/core/blockchain/web3-private-service/models/web-private-supported-blockchain'; +import { Injector } from 'src/core/injector/injector'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; + +import { OnChainTradeError } from '../../../models/on-chain-trade-error'; +import { RequiredOnChainCalculationOptions } from '../models/on-chain-calculation-options'; +import { OnChainProxyFeeInfo } from '../models/on-chain-proxy-fee-info'; +import { OnChainTradeType } from '../models/on-chain-trade-type'; +import { OnChainProxyService } from '../on-chain-proxy-service/on-chain-proxy-service'; +import { OnChainTradeStruct } from '../on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; +import { GasFeeInfo } from '../on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { OnChainTrade } from '../on-chain-trade/on-chain-trade'; + +export abstract class AggregatorOnChainProvider { + private readonly onChainProxyService = new OnChainProxyService(); + + public abstract readonly tradeType: OnChainTradeType; + + public abstract calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredOnChainCalculationOptions + ): Promise; + + protected abstract isSupportedBlockchain(blockchain: BlockchainName): boolean; + + protected getWalletAddress(blockchain: Web3PrivateSupportedBlockchain): string { + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(blockchain).address; + } + + protected abstract getGasFeeInfo( + tradeStruct: OnChainTradeStruct, + providerGateway?: string + ): Promise; + + protected async handleProxyContract( + from: PriceTokenAmount, + fullOptions: RequiredOnChainCalculationOptions + ): Promise<{ + fromWithoutFee: PriceTokenAmount; + proxyFeeInfo: OnChainProxyFeeInfo | undefined; + }> { + let fromWithoutFee: PriceTokenAmount; + let proxyFeeInfo: OnChainProxyFeeInfo | undefined; + + if (fullOptions.useProxy && BlockchainsInfo.isEvmBlockchainName(from.blockchain)) { + proxyFeeInfo = await this.onChainProxyService.getFeeInfo( + from as PriceTokenAmount, + fullOptions.providerAddress + ); + fromWithoutFee = getFromWithoutFee(from, proxyFeeInfo.platformFee.percent); + } else { + fromWithoutFee = from; + } + + return { + fromWithoutFee, + proxyFeeInfo + }; + } + + protected getRoutePath(from: Token, to: Token): ReadonlyArray { + return [from, to]; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-solana-on-chain-trade-abstract.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-solana-on-chain-trade-abstract.ts new file mode 100644 index 0000000..c64983a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-solana-on-chain-trade-abstract.ts @@ -0,0 +1,43 @@ +import BigNumber from 'bignumber.js'; +import { TokenUtils } from 'src/common/utils/token-utils'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { SolanaOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/solana-on-chain-trade/solana-on-chain-trade'; + +import { GetToAmountAndTxDataResponse } from './models/aggregator-on-chain-types'; + +export abstract class AggregatorSolanaOnChainTrade extends SolanaOnChainTrade { + protected async getTxConfigAndCheckAmount( + receiverAddress?: string, + fromAddress?: string, + directTransaction?: EvmEncodeConfig + ): Promise { + if (directTransaction) { + return directTransaction; + } + + const { tx, toAmount } = await this.getToAmountAndTxData(receiverAddress, fromAddress); + + const evmEncodeConfig = { + data: tx.data, + to: '', + value: '' + }; + + const newToTokenAmountMin = TokenUtils.getMinWeiAmountString( + new BigNumber(toAmount), + this.slippageTolerance + ); + + this.checkAmountChange(newToTokenAmountMin, this.toTokenAmountMin.stringWeiAmount); + + return evmEncodeConfig; + } + + /** + * @description Returns data for method OnChainTrade.checkAmountChange and EvmEncodeConfig value + */ + protected abstract getToAmountAndTxData( + receiverAddress?: string, + fromAddress?: string + ): Promise; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types.ts new file mode 100644 index 0000000..72dffb6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types.ts @@ -0,0 +1,6 @@ +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; + +export interface GetToAmountAndTxDataResponse { + tx: EvmEncodeConfig; + toAmount: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-proxy-service/on-chain-proxy-service.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-proxy-service/on-chain-proxy-service.ts new file mode 100644 index 0000000..f0ff2aa --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-proxy-service/on-chain-proxy-service.ts @@ -0,0 +1,130 @@ +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { Cache } from 'src/common/utils/decorators'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { + ON_CHAIN_PROXY_DISABLED_CHAINS, + ProxySupportedBlockchain, + proxySupportedBlockchains +} from 'src/features/common/constants/proxy-supported-blockchain'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { + OnChainPlatformFee, + OnChainProxyFeeInfo +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info'; + +export class OnChainProxyService { + public static isSupportedBlockchain( + blockchain: BlockchainName + ): blockchain is ProxySupportedBlockchain { + const isProxySupported = proxySupportedBlockchains.some( + supportedBlockchain => + supportedBlockchain === blockchain && + !ON_CHAIN_PROXY_DISABLED_CHAINS.some(chain => chain === blockchain) + ); + + return isProxySupported; + } + + @Cache({ + maxAge: 15_000 + }) + public async getFeeInfo( + from: PriceTokenAmount, + providerAddress: string + ): Promise { + const fromBlockchain = from.blockchain; + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const contractAddress = rubicProxyContractAddress[fromBlockchain].router; + + let fixedCryptoFeeWei: string | undefined; + let platformFeePercent: number; + let isIntegrator = true; + + if (providerAddress !== EvmWeb3Pure.EMPTY_ADDRESS) { + const fee = await OnChainProxyService.handleIntegratorFee( + web3Public, + contractAddress, + providerAddress + ); + isIntegrator = fee.isIntegrator; + fixedCryptoFeeWei = fee.fixedCryptoFeeWei; + platformFeePercent = fee.platformFeePercent; + } + + if (fixedCryptoFeeWei === undefined || !isIntegrator) { + const fee = await OnChainProxyService.handleRubicFee(web3Public, contractAddress); + fixedCryptoFeeWei = fee.fixedCryptoFeeWei; + platformFeePercent = fee.platformFeePercent; + } + + const fixedFeeToken = await PriceTokenAmount.createFromToken({ + ...nativeTokensList[fromBlockchain], + weiAmount: new BigNumber(fixedCryptoFeeWei) + }); + + const platformFee: OnChainPlatformFee = { + percent: platformFeePercent!, + token: await PriceTokenAmount.createFromToken({ + ...from, + tokenAmount: from.tokenAmount.multipliedBy(platformFeePercent! / 100) + }) + }; + + return { + fixedFeeToken, + platformFee + }; + } + + private static async handleIntegratorFee( + web3Public: EvmWeb3Public, + contractAddress: string, + providerAddress: string + ): Promise<{ + fixedCryptoFeeWei: string | undefined; + platformFeePercent: number; + isIntegrator: boolean; + }> { + const integratorToFeeInfo = await web3Public.callContractMethod<{ + isIntegrator: boolean; + fixedFeeAmount: string; + tokenFee: string; + }>(contractAddress, evmCommonCrossChainAbi, 'integratorToFeeInfo', [providerAddress]); + + return { + fixedCryptoFeeWei: integratorToFeeInfo.fixedFeeAmount, + platformFeePercent: parseInt(integratorToFeeInfo.tokenFee) / 10_000, + isIntegrator: integratorToFeeInfo.isIntegrator + }; + } + + private static async handleRubicFee( + web3Public: EvmWeb3Public, + contractAddress: string + ): Promise<{ fixedCryptoFeeWei: string; platformFeePercent: number }> { + const feeInfo = await Promise.all([ + web3Public.callContractMethod( + contractAddress, + evmCommonCrossChainAbi, + 'fixedNativeFee', + [] + ), + web3Public.callContractMethod( + contractAddress, + evmCommonCrossChainAbi, + 'RubicPlatformFee', + [] + ) + ]); + return { + fixedCryptoFeeWei: feeInfo[0], + platformFeePercent: parseInt(feeInfo[1]) / 10_000 + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade.ts new file mode 100644 index 0000000..b360b87 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade.ts @@ -0,0 +1,475 @@ +import BigNumber from 'bignumber.js'; +import { + FailedToCheckForTransactionReceiptError, + LowSlippageDeflationaryTokenError, + NotWhitelistedProviderError, + SwapRequestError, + UnnecessaryApproveError +} from 'src/common/errors'; +import { PriceTokenAmount, Token } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { parseError } from 'src/common/utils/errors'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { EvmBasicTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-basic-transaction-options'; +import { EvmTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-transaction-options'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from 'src/features/cross-chain/calculation-manager/providers/common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { IsDeflationToken } from 'src/features/deflation-token-manager/models/is-deflation-token'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { OnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; +import { GasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { + OptionsGasParams, + TransactionGasParams +} from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-params'; +import { OnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade'; +import { TransactionConfig } from 'web3-core'; +import { TransactionReceipt } from 'web3-eth'; +import { utf8ToHex } from 'web3-utils'; + +import { Permit2ApproveConfig } from './models/permit2-approve-config'; + +export abstract class EvmOnChainTrade extends OnChainTrade { + protected lastTransactionConfig: EvmEncodeConfig | null = null; + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly slippageTolerance: number; + + public readonly path: ReadonlyArray; + + /** + * Gas fee info, including gas limit and gas price. + */ + public readonly gasFeeInfo: GasFeeInfo | null; + + public readonly feeInfo: FeeInfo; + + /** + * True, if trade must be swapped through on-chain proxy contract. + */ + public readonly useProxy: boolean; + + /** + * Contains from amount, from which proxy fees were subtracted. + * If proxy is not used, then amount is equal to from amount. + */ + protected readonly fromWithoutFee: PriceTokenAmount; + + protected readonly withDeflation: { + from: IsDeflationToken; + to: IsDeflationToken; + }; + + public abstract readonly dexContractAddress: string; // not static because https://github.com/microsoft/TypeScript/issues/34516 + + private readonly usedForCrossChain: boolean; + + /** + * Filled if approve goes through permit2 contract + */ + public readonly permit2ApproveConfig: Permit2ApproveConfig = { + usePermit2Approve: false, + permit2Address: null + }; + + protected get spenderAddress(): string { + return this.useProxy || this.usedForCrossChain + ? rubicProxyContractAddress[this.from.blockchain].gateway + : this.dexContractAddress; + } + + protected get web3Public(): EvmWeb3Public { + return Injector.web3PublicService.getWeb3Public(this.from.blockchain); + } + + protected get web3Private(): EvmWeb3Private { + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(this.from.blockchain); + } + + protected constructor( + evmOnChainTradeStruct: OnChainTradeStruct, + providerAddress: string + ) { + super(providerAddress); + + this.from = evmOnChainTradeStruct.from; + this.to = evmOnChainTradeStruct.to; + + this.slippageTolerance = evmOnChainTradeStruct.slippageTolerance; + this.path = evmOnChainTradeStruct.path; + + this.gasFeeInfo = evmOnChainTradeStruct.gasFeeInfo; + + this.useProxy = evmOnChainTradeStruct.useProxy; + this.fromWithoutFee = evmOnChainTradeStruct.fromWithoutFee; + this.usedForCrossChain = evmOnChainTradeStruct.usedForCrossChain || false; + + if (evmOnChainTradeStruct.permit2ApproveAddress) { + this.permit2ApproveConfig = { + usePermit2Approve: true, + permit2Address: evmOnChainTradeStruct.permit2ApproveAddress + }; + } + + this.feeInfo = { + rubicProxy: { + ...(evmOnChainTradeStruct.proxyFeeInfo?.fixedFeeToken && { + fixedFee: { + amount: + evmOnChainTradeStruct.proxyFeeInfo?.fixedFeeToken.tokenAmount || + new BigNumber(0), + token: evmOnChainTradeStruct.proxyFeeInfo?.fixedFeeToken + } + }), + ...(evmOnChainTradeStruct.proxyFeeInfo?.platformFee && { + platformFee: { + percent: evmOnChainTradeStruct.proxyFeeInfo?.platformFee.percent || 0, + token: evmOnChainTradeStruct.proxyFeeInfo?.platformFee.token + } + }) + } + }; + this.withDeflation = evmOnChainTradeStruct.withDeflation; + } + + public async approve( + options: EvmBasicTransactionOptions, + checkNeedApprove = true, + amount: BigNumber | 'infinity' = 'infinity' + ): Promise { + if (checkNeedApprove) { + const needApprove = await this.needApprove(); + if (!needApprove) { + throw new UnnecessaryApproveError(); + } + } + + this.checkWalletConnected(); + await this.checkBlockchainCorrect(); + + const approveAmount = + this.from.blockchain === BLOCKCHAIN_NAME.GNOSIS || + this.from.blockchain === BLOCKCHAIN_NAME.CRONOS + ? this.from.weiAmount + : amount; + + const fromTokenAddress = + this.from.isNative && this.from.blockchain === BLOCKCHAIN_NAME.METIS + ? '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000' + : this.from.address; + + return this.web3Private.approveTokens( + fromTokenAddress, + this.spenderAddress, + approveAmount, + options + ); + } + + public async encodeApprove( + tokenAddress: string, + spenderAddress: string, + value: BigNumber | 'infinity', + options: EvmTransactionOptions = {} + ): Promise { + return this.web3Private.encodeApprove(tokenAddress, spenderAddress, value, options); + } + + protected async checkAllowanceAndApprove( + options?: Omit + ): Promise { + const needApprove = await this.needApprove(); + if (!needApprove) { + return; + } + + const approveOptions: EvmBasicTransactionOptions = { + onTransactionHash: options?.onApprove, + gas: options?.approveGasLimit || undefined, + gasPriceOptions: options?.gasPriceOptions || undefined + }; + + await this.approve(approveOptions, false); + } + + /** + * Calculates value for swap transaction. + * @param providerValue Value, returned from cross-chain provider. + */ + protected getSwapValue(providerValue?: BigNumber | string | number | null): string { + const nativeToken = nativeTokensList[this.from.blockchain]; + const fixedFeeValue = Web3Pure.toWei( + this.feeInfo.rubicProxy?.fixedFee?.amount || 0, + nativeToken.decimals + ); + + let fromValue: BigNumber; + if (this.from.isNative) { + if (providerValue) { + fromValue = new BigNumber(providerValue).dividedBy( + 1 - (this.feeInfo.rubicProxy?.platformFee?.percent || 0) / 100 + ); + } else { + fromValue = this.from.weiAmount; + } + } else { + fromValue = new BigNumber(providerValue || 0); + } + + return new BigNumber(fromValue).plus(fixedFeeValue).toFixed(0, 0); + } + + public async swap(options: SwapTransactionOptions = {}): Promise { + await this.checkWalletState(options?.testMode); + await this.checkAllowanceAndApprove(options); + + const { onConfirm } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + const fromAddress = this.walletAddress; + const { data, value, to } = await this.encode({ ...options, fromAddress }); + const method = options?.testMode ? 'sendTransaction' : 'trySendTransaction'; + + try { + await this.web3Private[method](to, { + data, + value, + onTransactionHash, + gas: options.gasLimit, + gasPriceOptions: options.gasPriceOptions, + ...(options?.useEip155 && { + chainId: `0x${blockchainId[this.from.blockchain].toString(16)}` + }) + }); + + return transactionHash!; + } catch (err) { + if (err instanceof FailedToCheckForTransactionReceiptError) { + return transactionHash!; + } + + throw parseError(err); + } + } + + public async encode(options: EncodeTransactionOptions): Promise { + await this.checkFromAddress(options.fromAddress, true); + await this.checkReceiverAddress(options.receiverAddress); + + if (this.useProxy) { + return this.encodeProxy(options); + } + return this.encodeDirect(options); + } + + /** + * Encodes trade to swap it through on-chain proxy. + */ + protected async encodeProxy(options: EncodeTransactionOptions): Promise { + const { contractAddress, contractAbi, methodName, methodArguments, value } = + await this.getProxyContractParams(options); + const gasParams = this.getGasParams(options); + + return EvmWeb3Pure.encodeMethodCall( + contractAddress, + contractAbi, + methodName, + methodArguments, + value, + gasParams + ); + } + + private async getProxyContractParams( + options: EncodeTransactionOptions + ): Promise { + const swapData = await this.getSwapData(options); + + const receiverAddress = options.receiverAddress || options.fromAddress; + const methodArguments = [ + EvmWeb3Pure.randomHex(32), + this.providerAddress, + EvmOnChainTrade.getReferrerAddress(options.referrer), + receiverAddress, + this.toTokenAmountMin.stringWeiAmount, + swapData + ]; + + const nativeToken = nativeTokensList[this.from.blockchain]; + const proxyFee = new BigNumber(this.feeInfo.rubicProxy?.fixedFee?.amount || '0'); + const value = Web3Pure.toWei( + proxyFee.plus(this.from.isNative ? this.from.tokenAmount : '0'), + nativeToken.decimals + ); + + const txConfig = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + 'swapTokensGeneric', + methodArguments, + value + ); + + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, txConfig.data], + value + }; + } + + private static getReferrerAddress(referrer: string | undefined): string { + if (referrer) { + return '0x' + utf8ToHex(referrer).slice(2, 42).padStart(40, '0'); + } + + return '0x0000000000000000000000000000000000000000'; + } + + /** + * Encodes trade to swap it directly through dex contract. + * @param options Encode options. + */ + public async encodeDirect(options: EncodeTransactionOptions): Promise { + await this.checkFromAddress(options.fromAddress, true); + await this.checkReceiverAddress(options.receiverAddress); + + try { + const transactionData = await this.setTransactionConfig(options); + + const { gas, gasPrice } = this.getGasParams(options, { + gasLimit: transactionData.gas, + gasPrice: transactionData.gasPrice + }); + + return { + to: transactionData.to, + data: transactionData.data, + value: this.fromWithoutFee.isNative ? this.fromWithoutFee.stringWeiAmount : '0', + gas, + gasPrice + }; + } catch (err) { + throw this.getSwapError(err); + } + } + + protected isDeflationError(): boolean { + return ( + (this.withDeflation.from.isDeflation || this.withDeflation.to.isDeflation) && + this.slippageTolerance < 0.12 + ); + } + + protected getGasParams( + options: OptionsGasParams, + calculatedGasFee: OptionsGasParams = { + gasLimit: this.gasFeeInfo?.gasLimit?.toFixed(), + gasPrice: this.gasFeeInfo?.gasPrice?.toFixed() + } + ): TransactionGasParams { + return { + gas: options.gasLimit || calculatedGasFee.gasLimit, + gasPrice: options.gasPrice || calculatedGasFee.gasPrice, + maxPriorityFeePerGas: + options.maxPriorityFeePerGas || calculatedGasFee.maxPriorityFeePerGas, + maxFeePerGas: options.maxFeePerGas || calculatedGasFee.maxFeePerGas + }; + } + + protected async getSwapData(options: GetContractParamsOptions): Promise { + const directTransactionConfig = await this.encodeDirect({ + ...options, + fromAddress: rubicProxyContractAddress[this.from.blockchain].router, + supportFee: false, + receiverAddress: rubicProxyContractAddress[this.from.blockchain].router + }); + const availableDexs = ( + await ProxyCrossChainEvmTrade.getWhitelistedDexes(this.from.blockchain) + ).map(address => address.toLowerCase()); + + const routerAddress = directTransactionConfig.to; + const method = directTransactionConfig.data.slice(0, 10); + + if (!availableDexs.includes(routerAddress.toLowerCase())) { + throw new NotWhitelistedProviderError(routerAddress, undefined, 'dex'); + } + await ProxyCrossChainEvmTrade.checkDexWhiteList( + this.from.blockchain, + routerAddress, + method + ); + + return [ + [ + routerAddress, + routerAddress, + this.from.address, + this.to.address, + this.from.stringWeiAmount, + directTransactionConfig.data, + true + ] + ]; + } + + protected abstract getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise; + + protected async setTransactionConfig( + options: EncodeTransactionOptions + ): Promise { + if (this.lastTransactionConfig && options.useCacheData) { + return this.lastTransactionConfig; + } + + const { tx, toAmount } = await this.getTransactionConfigAndAmount(options); + this.lastTransactionConfig = tx; + setTimeout(() => { + this.lastTransactionConfig = null; + }, 15_000); + + if (!options.skipAmountCheck) { + this.checkAmountChange(toAmount, this.to.stringWeiAmount); + } + return tx; + } + + protected getSwapError(err: Error & { code: number }): Error { + if ([400, 500, 503].includes(err.code)) { + throw new SwapRequestError(); + } + if (this.isDeflationError()) { + throw new LowSlippageDeflationaryTokenError(); + } + throw parseError(err); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct.ts new file mode 100644 index 0000000..2228beb --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct.ts @@ -0,0 +1,30 @@ +import { PriceTokenAmount, Token } from 'src/common/tokens'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { IsDeflationToken } from 'src/features/deflation-token-manager/models/is-deflation-token'; +import { OnChainProxyFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info'; +import { GasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; + +export interface OnChainTradeStruct { + from: PriceTokenAmount; + to: PriceTokenAmount; + + slippageTolerance: number; + path: ReadonlyArray; + + gasFeeInfo: GasFeeInfo | null; + + useProxy: boolean; + proxyFeeInfo: OnChainProxyFeeInfo | undefined; + fromWithoutFee: PriceTokenAmount; + + withDeflation: { + from: IsDeflationToken; + to: IsDeflationToken; + }; + + usedForCrossChain?: boolean; + + permit2ApproveAddress?: string; +} + +export interface EvmOnChainTradeStruct extends OnChainTradeStruct {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact.ts new file mode 100644 index 0000000..043d340 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact.ts @@ -0,0 +1 @@ +export type Exact = 'input' | 'output'; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info.ts new file mode 100644 index 0000000..3f4ee5d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info.ts @@ -0,0 +1,12 @@ +import BigNumber from 'bignumber.js'; + +/** + * Stores gas fee information in calculated trade. + */ +export interface GasFeeInfo { + readonly gasLimit?: BigNumber; + readonly gasPrice?: BigNumber; + readonly gasFeeInEth?: BigNumber; + readonly gasFeeInUsd?: BigNumber; + readonly maxFeePerGas?: BigNumber; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-params.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-params.ts new file mode 100644 index 0000000..9bc6139 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-params.ts @@ -0,0 +1,13 @@ +export interface OptionsGasParams { + gasPrice?: string; + gasLimit?: string; + maxPriorityFeePerGas?: string; + maxFeePerGas?: string; +} + +export interface TransactionGasParams { + gas?: string; + gasPrice?: string; + maxPriorityFeePerGas?: string; + maxFeePerGas?: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/permit2-approve-config.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/permit2-approve-config.ts new file mode 100644 index 0000000..82cddfa --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/permit2-approve-config.ts @@ -0,0 +1,9 @@ +export type Permit2ApproveConfig = + | { + usePermit2Approve: false; + permit2Address: null; + } + | { + usePermit2Approve: true; + permit2Address: string; + }; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade.ts new file mode 100644 index 0000000..fc2f169 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade.ts @@ -0,0 +1,268 @@ +import BigNumber from 'bignumber.js'; +import { + RubicSdkError, + WalletNotConnectedError, + WrongFromAddressError, + WrongReceiverAddressError +} from 'src/common/errors'; +import { UpdatedRatesError } from 'src/common/errors/cross-chain/updated-rates-error'; +import { PriceTokenAmount, Token, TokenAmount } from 'src/common/tokens'; +import { Cache } from 'src/common/utils/decorators'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { BasicTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/models/basic-transaction-options'; +import { Web3Private } from 'src/core/blockchain/web3-private-service/web3-private/web3-private'; +import { Web3Public } from 'src/core/blockchain/web3-public-service/web3-public/web3-public'; +import { HttpClient } from 'src/core/http-client/models/http-client'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { isAddressCorrect } from 'src/features/common/utils/check-address'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { RubicStep } from 'src/features/cross-chain/calculation-manager/providers/common/models/rubicStep'; +import { TradeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/trade-info'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; + +/** + * Abstract class for all instant trade providers' trades. + */ +export abstract class OnChainTrade { + /** + * Token to sell with input amount. + */ + public abstract readonly from: PriceTokenAmount; + + /** + * Token to get with output amount. + */ + public abstract readonly to: PriceTokenAmount; + + public abstract readonly slippageTolerance: number; + + protected abstract readonly spenderAddress: string; // not static because https://github.com/microsoft/TypeScript/issues/34516 + + public abstract readonly path: ReadonlyArray; + + public abstract readonly feeInfo: FeeInfo; + + /** + * Type of instant trade provider. + */ + public abstract get type(): OnChainTradeType; + + /** + * Minimum amount of output token user can get. + */ + public get toTokenAmountMin(): PriceTokenAmount { + const weiAmountOutMin = this.to.weiAmountMinusSlippage(this.slippageTolerance); + return new PriceTokenAmount({ ...this.to.asStruct, weiAmount: weiAmountOutMin }); + } + + protected get web3Public(): Web3Public { + return Injector.web3PublicService.getWeb3Public(this.from.blockchain); + } + + protected get web3Private(): Web3Private { + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(this.from.blockchain); + } + + protected get walletAddress(): string { + return this.web3Private.address; + } + + protected get httpClient(): HttpClient { + return Injector.httpClient; + } + + /** + * Price impact, based on tokens' usd prices. + */ + @Cache + public get priceImpact(): number | null { + return this.from.calculatePriceImpactPercent(this.to); + } + + protected constructor(protected readonly providerAddress: string) {} + + /** + * Returns true, if allowance is not enough. + */ + public async needApprove(fromAddress?: string): Promise { + if (!fromAddress) { + this.checkWalletConnected(); + } + + // Native coin in METIS can be Token required approve + if (this.from.isNative && this.from.blockchain !== BLOCKCHAIN_NAME.METIS) { + return false; + } + + // Special native address for METIS native coin + const fromTokenAddress = + this.from.isNative && this.from.blockchain === BLOCKCHAIN_NAME.METIS + ? '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000' + : this.from.address; + + const allowance = await this.web3Public.getAllowance( + fromTokenAddress, + fromAddress || this.walletAddress, + this.spenderAddress + ); + return allowance.lt(this.from.weiAmount); + } + + /** + * Sends approve transaction with connected wallet. + * @param options Transaction options. + * @param checkNeedApprove If true, first allowance is checked. + * @param amount Amount of tokens in approval window in spending cap field + */ + public abstract approve( + options: BasicTransactionOptions, + checkNeedApprove?: boolean, + amount?: BigNumber | 'infinity' + ): Promise; + + /** + * Builds encoded approve transaction config. + * @param tokenAddress Address of the smart-contract corresponding to the token. + * @param spenderAddress Wallet or contract address to approve. + * @param value Token amount to approve in wei. + * @param [options] Additional options. + * @returns Encoded approve transaction config. + */ + public abstract encodeApprove( + tokenAddress: string, + spenderAddress: string, + value: BigNumber | 'infinity', + options: BasicTransactionOptions + ): Promise; + + /** + * Sends swap transaction with connected wallet. + * If user has not enough allowance, then approve transaction will be called first. + * + * @example + * ```ts + * const onConfirm = (hash: string) => console.log(hash); + * const receipt = await trades[TRADE_TYPE.UNISWAP_V2].swap({ onConfirm }); + * ``` + * + * @param options Transaction options. + */ + public abstract swap(options?: SwapTransactionOptions): Promise; + + /** + * Builds transaction config, with encoded data. + * @param options Encode transaction options. + */ + public abstract encode(options: EncodeTransactionOptions): Promise; + + protected async checkWalletState(testMode: boolean = false): Promise { + this.checkWalletConnected(); + await this.checkBlockchainCorrect(); + if (!testMode) { + await this.checkBalance(); + } + } + + protected checkWalletConnected(): never | void { + if (!this.walletAddress) { + throw new WalletNotConnectedError(); + } + } + + protected async checkBlockchainCorrect(): Promise { + await this.web3Private.checkBlockchainCorrect(this.from.blockchain); + } + + protected async checkBalance(): Promise { + await this.web3Public.checkBalance(this.from, this.from.tokenAmount, this.walletAddress); + } + + protected async checkFromAddress( + fromAddress: string | undefined, + isRequired = false, + chainType?: OnChainTradeType + ): Promise { + if (!fromAddress) { + if (isRequired) { + throw new RubicSdkError(`'fromAddress' is required option`); + } + return; + } + + const isAddressCorrectValue = await isAddressCorrect( + fromAddress, + this.from.blockchain, + chainType + ); + + if (!isAddressCorrectValue) { + throw new WrongFromAddressError(); + } + } + + protected async checkReceiverAddress( + receiverAddress: string | undefined, + isRequired = false, + chainType?: OnChainTradeType + ): Promise { + if (!receiverAddress) { + if (isRequired) { + throw new RubicSdkError(`'receiverAddress' is required option`); + } + return; + } + + const isAddressCorrectValue = await isAddressCorrect( + receiverAddress, + this.from.blockchain, + chainType + ); + + if (!isAddressCorrectValue) { + throw new WrongReceiverAddressError(); + } + } + + protected getRoutePath(): RubicStep[] { + return [ + { + type: 'on-chain', + provider: this.type, + path: this.path.map( + token => new TokenAmount({ ...token, tokenAmount: new BigNumber(0) }) + ) + } + ]; + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: null, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact ?? null, + slippage: this.slippageTolerance * 100, + routePath: this.getRoutePath() + }; + } + + protected checkAmountChange(newWeiAmount: string, oldWeiAmount: string): void { + const oldAmount = new BigNumber(oldWeiAmount); + const newAmount = new BigNumber(newWeiAmount); + const changePercent = 0.5; + const acceptablePercentPriceChange = new BigNumber(changePercent).dividedBy(100); + + const amountPlusPercent = oldAmount.multipliedBy(acceptablePercentPriceChange.plus(1)); + const amountMinusPercent = oldAmount.multipliedBy( + new BigNumber(1).minus(acceptablePercentPriceChange) + ); + + const shouldThrowError = + newAmount.lt(amountMinusPercent) || newAmount.gt(amountPlusPercent); + + if (shouldThrowError) { + throw new UpdatedRatesError(oldWeiAmount, newWeiAmount); + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/solana-on-chain-trade/solana-on-chain-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/solana-on-chain-trade/solana-on-chain-trade.ts new file mode 100644 index 0000000..18017c9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/solana-on-chain-trade/solana-on-chain-trade.ts @@ -0,0 +1,231 @@ +import BigNumber from 'bignumber.js'; +import { FailedToCheckForTransactionReceiptError } from 'src/common/errors'; +import { PriceTokenAmount, Token } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { parseError } from 'src/common/utils/errors'; +import { BLOCKCHAIN_NAME, SolanaBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmBasicTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-basic-transaction-options'; +import { EvmTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/models/evm-transaction-options'; +import { SolanaWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/solana-web3-private/solana-web3-private'; +import { SolanaWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/solana-web3-public/solana-web3-public'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { IsDeflationToken } from 'src/features/deflation-token-manager/models/is-deflation-token'; +import { OnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; +import { GasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { + OptionsGasParams, + TransactionGasParams +} from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-params'; +import { OnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade'; +import { TransactionConfig } from 'web3-core'; +import { TransactionReceipt } from 'web3-eth'; + +export abstract class SolanaOnChainTrade extends OnChainTrade { + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly slippageTolerance: number; + + public readonly path: ReadonlyArray; + + /** + * Gas fee info, including gas limit and gas price. + */ + public readonly gasFeeInfo: GasFeeInfo | null; + + public readonly feeInfo: FeeInfo; + + /** + * True, if trade must be swapped through on-chain proxy contract. + */ + public readonly useProxy: boolean; + + /** + * Contains from amount, from which proxy fees were subtracted. + * If proxy is not used, then amount is equal to from amount. + */ + protected readonly fromWithoutFee: PriceTokenAmount; + + protected readonly withDeflation: { + from: IsDeflationToken; + to: IsDeflationToken; + }; + + public abstract readonly dexContractAddress: string; // not static because https://github.com/microsoft/TypeScript/issues/34516 + + protected get spenderAddress(): string { + return this.useProxy + ? rubicProxyContractAddress[this.from.blockchain].gateway + : this.dexContractAddress; + } + + protected get web3Public(): SolanaWeb3Public { + return Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.SOLANA); + } + + protected get web3Private(): SolanaWeb3Private { + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(BLOCKCHAIN_NAME.SOLANA); + } + + protected constructor( + tradeStruct: OnChainTradeStruct, + providerAddress: string + ) { + super(providerAddress); + + this.from = tradeStruct.from; + this.to = tradeStruct.to; + + this.slippageTolerance = tradeStruct.slippageTolerance; + this.path = tradeStruct.path; + + this.gasFeeInfo = tradeStruct.gasFeeInfo; + + this.useProxy = tradeStruct.useProxy; + this.fromWithoutFee = tradeStruct.fromWithoutFee; + + this.feeInfo = { + rubicProxy: { + ...(tradeStruct.proxyFeeInfo?.fixedFeeToken && { + fixedFee: { + amount: + tradeStruct.proxyFeeInfo?.fixedFeeToken.tokenAmount || new BigNumber(0), + token: tradeStruct.proxyFeeInfo?.fixedFeeToken + } + }), + ...(tradeStruct.proxyFeeInfo?.platformFee && { + platformFee: { + percent: tradeStruct.proxyFeeInfo?.platformFee.percent || 0, + token: tradeStruct.proxyFeeInfo?.platformFee.token + } + }) + } + }; + this.withDeflation = tradeStruct.withDeflation; + } + + public async approve( + _options: EvmBasicTransactionOptions, + _checkNeedApprove = true, + _amount: BigNumber | 'infinity' = 'infinity' + ): Promise { + throw new Error('Method is not supported'); + } + + public async encodeApprove( + _tokenAddress: string, + _spenderAddress: string, + _value: BigNumber | 'infinity', + _options: EvmTransactionOptions = {} + ): Promise { + throw new Error('Method is not supported'); + } + + protected async checkAllowanceAndApprove(): Promise { + throw new Error('Method is not supported'); + } + + /** + * Calculates value for swap transaction. + * @param providerValue Value, returned from cross-chain provider. + */ + protected getSwapValue(providerValue?: BigNumber | string | number | null): string { + const nativeToken = nativeTokensList[this.from.blockchain]; + const fixedFeeValue = Web3Pure.toWei( + this.feeInfo.rubicProxy?.fixedFee?.amount || 0, + nativeToken.decimals + ); + + let fromValue: BigNumber; + if (this.from.isNative) { + if (providerValue) { + fromValue = new BigNumber(providerValue).dividedBy( + 1 - (this.feeInfo.rubicProxy?.platformFee?.percent || 0) / 100 + ); + } else { + fromValue = this.from.weiAmount; + } + } else { + fromValue = new BigNumber(providerValue || 0); + } + + return new BigNumber(fromValue).plus(fixedFeeValue).toFixed(0, 0); + } + + public async swap(options: SwapTransactionOptions = {}): Promise { + await this.checkWalletState(options?.testMode); + + const { onConfirm } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + const fromAddress = this.walletAddress; + const receiverAddress = options.receiverAddress || this.walletAddress; + + const transactionConfig = await this.encode({ + fromAddress, + receiverAddress, + ...(options?.referrer && { referrer: options?.referrer }) + }); + + try { + await this.web3Private.sendTransaction({ + data: transactionConfig.data, + onTransactionHash + }); + + return transactionHash!; + } catch (err) { + if (err instanceof FailedToCheckForTransactionReceiptError) { + return transactionHash!; + } + + throw parseError(err); + } + } + + public async encode(options: EncodeTransactionOptions): Promise { + await this.checkFromAddress(options.fromAddress, true); + await this.checkReceiverAddress(options.receiverAddress); + + return this.encodeDirect(options); + } + + /** + * Encodes trade to swap it directly through dex contract. + */ + public abstract encodeDirect(options: EncodeTransactionOptions): Promise; + + protected getGasParams( + options: OptionsGasParams, + calculatedGasFee: OptionsGasParams = { + gasLimit: this.gasFeeInfo?.gasLimit?.toFixed(), + gasPrice: this.gasFeeInfo?.gasPrice?.toFixed() + } + ): TransactionGasParams { + return { + gas: options.gasLimit || calculatedGasFee.gasLimit, + gasPrice: options.gasPrice || calculatedGasFee.gasPrice, + maxPriorityFeePerGas: + options.maxPriorityFeePerGas || calculatedGasFee.maxPriorityFeePerGas, + maxFeePerGas: options.maxFeePerGas || calculatedGasFee.maxFeePerGas + }; + } + + protected async getSwapData(_options: GetContractParamsOptions): Promise { + throw new Error('Method is not supported'); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/tron-on-chain-trade/tron-on-chain-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/tron-on-chain-trade/tron-on-chain-trade.ts new file mode 100644 index 0000000..c83615b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/on-chain-trade/tron-on-chain-trade/tron-on-chain-trade.ts @@ -0,0 +1,77 @@ +import BigNumber from 'bignumber.js'; +import { UnnecessaryApproveError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { TronBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { TronTransactionOptions } from 'src/core/blockchain/web3-private-service/web3-private/tron-web3-private/models/tron-transaction-options'; +import { TronWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/tron-web3-private/tron-web3-private'; +import { TronWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/tron-web3-public'; +import { TronTransactionConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-transaction-config'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { OnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade'; + +export abstract class TronOnChainTrade extends OnChainTrade { + public abstract readonly from: PriceTokenAmount; + + public abstract readonly to: PriceTokenAmount; + + protected get web3Public(): TronWeb3Public { + return Injector.web3PublicService.getWeb3Public(this.from.blockchain); + } + + protected get web3Private(): TronWeb3Private { + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(this.from.blockchain); + } + + public async approve( + options: TronTransactionOptions, + checkNeedApprove = true, + amount: BigNumber | 'infinity' = 'infinity' + ): Promise { + if (checkNeedApprove) { + const needApprove = await this.needApprove(); + if (!needApprove) { + throw new UnnecessaryApproveError(); + } + } + + this.checkWalletConnected(); + await this.checkBlockchainCorrect(); + + return this.web3Private.approveTokens( + this.from.address, + this.spenderAddress, + amount, + options + ); + } + + protected async checkAllowanceAndApprove( + options?: Omit + ): Promise { + const needApprove = await this.needApprove(); + if (!needApprove) { + return; + } + + const approveOptions: TronTransactionOptions = { + onTransactionHash: options?.onApprove, + feeLimit: options?.approveFeeLimit + }; + await this.approve(approveOptions, false); + } + + public abstract swap(options?: SwapTransactionOptions): Promise; + + public abstract encode(options: EncodeTransactionOptions): Promise; + + public async encodeApprove( + tokenAddress: string, + spenderAddress: string, + value: BigNumber | 'infinity', + options: TronTransactionOptions = {} + ): Promise { + return this.web3Private.encodeApprove(tokenAddress, spenderAddress, value, options); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info.ts new file mode 100644 index 0000000..9e1a129 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info.ts @@ -0,0 +1,25 @@ +import BigNumber from 'bignumber.js'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { GasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info'; +import { GasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/models/gas-price-info'; + +export function getGasFeeInfo( + estimatedGas: BigNumber | string | number | null | undefined, + gasPriceInfo: GasPriceInfo | undefined +): GasFeeInfo { + const gasLimit = estimatedGas ? Web3Pure.calculateGasMargin(estimatedGas, 1.2) : undefined; + + if (!gasLimit) { + return { gasPrice: gasPriceInfo?.gasPrice }; + } + const gasFeeInEth = gasPriceInfo?.gasPriceInEth?.multipliedBy(gasLimit); + const gasFeeInUsd = gasPriceInfo?.gasPriceInUsd?.multipliedBy(gasLimit); + + return { + gasLimit, + gasPrice: gasPriceInfo?.gasPrice, + gasFeeInEth, + gasFeeInUsd, + maxFeePerGas: gasPriceInfo?.maxFeePerGas + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/utils/get-gas-price-info.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/utils/get-gas-price-info.ts new file mode 100644 index 0000000..567a225 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/common/utils/get-gas-price-info.ts @@ -0,0 +1,24 @@ +import BigNumber from 'bignumber.js'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { GasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/models/gas-price-info'; + +export async function getGasPriceInfo(blockchain: EvmBlockchainName): Promise { + const address = nativeTokensList[blockchain].address; + const [{ gasPrice, maxFeePerGas }, nativeCoinPrice] = await Promise.all([ + Injector.gasPriceApi.getGasPrice(blockchain), + Injector.coingeckoApi.getTokenPrice({ address, blockchain }).catch(() => new BigNumber(0)) + ]); + + const gasPriceInEth = Web3Pure.fromWei(maxFeePerGas || gasPrice || 0); + + const gasPriceInUsd = gasPriceInEth.multipliedBy(nativeCoinPrice); + return { + gasPrice: Web3Pure.fromWei(gasPrice || 0), + gasPriceInEth, + gasPriceInUsd, + maxFeePerGas: Web3Pure.fromWei(maxFeePerGas || 0) + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/camelot-arbitrum-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/camelot-arbitrum-provider.ts new file mode 100644 index 0000000..c81553c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/camelot-arbitrum-provider.ts @@ -0,0 +1,62 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { createTokenNativeAddressProxyInPathStartAndEnd } from 'src/features/common/utils/token-native-address-proxy'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { CamelotArbitrumTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/camelot-arbitrum-trade'; +import { CAMELOT_ARBITRUM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/constants/provider-configuration'; +import { CAMELOT_ARBITRUM_ROUTER_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/constants/swap-router-contract-data'; +import { + CAMELOT_ARBITRUM_QUOTER_CONTRACT_ABI, + CAMELOT_ARBITRUM_QUOTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/utils/quoter-controller/constants/quoter-contract-data'; +import { defaultArbitrumProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/default-constants'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraTradeStructOmitPath } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UniswapV3AlgebraAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider'; +import { QUICK_SWAP_V3_ROUTER_CONTRACT_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/constants/swap-router-contract-data'; +import { QuickSwapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-route'; + +export class CamelotArbitrumProvider extends UniswapV3AlgebraAbstractProvider { + protected readonly contractAddress = CAMELOT_ARBITRUM_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = QUICK_SWAP_V3_ROUTER_CONTRACT_ABI; + + public readonly blockchain = BLOCKCHAIN_NAME.ARBITRUM; + + protected readonly OnChainTradeClass = CamelotArbitrumTrade; + + protected readonly quoterController = new AlgebraQuoterController( + this.blockchain, + defaultArbitrumProviderConfiguration.routingProvidersAddresses, + CAMELOT_ARBITRUM_QUOTER_CONTRACT_ADDRESS, + CAMELOT_ARBITRUM_QUOTER_CONTRACT_ABI + ); + + protected readonly providerConfiguration = CAMELOT_ARBITRUM_PROVIDER_CONFIGURATION; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.CAMELOT; + } + + protected createTradeInstance( + tradeStruct: UniswapV3AlgebraTradeStructOmitPath, + route: QuickSwapV3Route, + providerAddress: string + ): CamelotArbitrumTrade { + const path = createTokenNativeAddressProxyInPathStartAndEnd( + route.path, + EvmWeb3Pure.nativeTokenAddress + ); + return new CamelotArbitrumTrade( + { + ...tradeStruct, + path, + route + }, + providerAddress + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/camelot-arbitrum-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/camelot-arbitrum-trade.ts new file mode 100644 index 0000000..8e838f5 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/camelot-arbitrum-trade.ts @@ -0,0 +1,75 @@ +import { Token } from 'src/common/tokens'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { CAMELOT_ARBITRUM_ROUTER_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/constants/swap-router-contract-data'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade'; +import { QUICK_SWAP_V3_ROUTER_CONTRACT_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/constants/swap-router-contract-data'; +import { QuickSwapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-route'; +import { QuickSwapV3TradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-trade-struct'; + +export class CamelotArbitrumTrade extends UniswapV3AlgebraAbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.CAMELOT; + } + + public readonly dexContractAddress = CAMELOT_ARBITRUM_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = QUICK_SWAP_V3_ROUTER_CONTRACT_ABI; + + protected readonly unwrapWethMethodName = 'unwrapWNativeToken'; + + private readonly route: QuickSwapV3Route; + + public readonly wrappedPath: ReadonlyArray; + + constructor(tradeStruct: QuickSwapV3TradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this.route = tradeStruct.route; + + this.wrappedPath = this.route.path; + } + + /** + * Returns swap `exactInput` method's name and arguments to use in Swap contract. + */ + protected getSwapRouterExactInputMethodData(walletAddress: string): MethodData { + const amountParams = this.getAmountParams(); + + if (this.route.path.length === 2 && this.route?.path?.[0] && this.route?.path?.[1]) { + const methodName = this.exact === 'input' ? 'exactInputSingle' : 'exactOutputSingle'; + + return { + methodName, + methodArguments: [ + [ + this.route.path[0].address, + this.route.path[1].address, + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams, + 0 + ] + ] + }; + } + + const methodName = this.exact === 'input' ? 'exactInput' : 'exactOutput'; + + return { + methodName, + methodArguments: [ + [ + AlgebraQuoterController.getEncodedPath(this.route.path), + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams + ] + ] + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/constants/provider-configuration.ts new file mode 100644 index 0000000..cc71d63 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/constants/provider-configuration.ts @@ -0,0 +1,8 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const CAMELOT_ARBITRUM_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.ARBITRUM]!.address, + maxTransitTokens: 1 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/constants/swap-router-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/constants/swap-router-contract-data.ts new file mode 100644 index 0000000..4e9005b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/constants/swap-router-contract-data.ts @@ -0,0 +1,2 @@ +export const CAMELOT_ARBITRUM_ROUTER_CONTRACT_ADDRESS = + '0x1F721E2E82F6676FCE4eA07A5958cF098D339e18'; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/utils/quoter-controller/constants/quoter-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/utils/quoter-controller/constants/quoter-contract-data.ts new file mode 100644 index 0000000..0c26a2a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/utils/quoter-controller/constants/quoter-contract-data.ts @@ -0,0 +1,104 @@ +import { AbiItem } from 'web3-utils'; + +export const CAMELOT_ARBITRUM_QUOTER_CONTRACT_ADDRESS = + '0x0Fc73040b26E9bC8514fA028D998E73A254Fa76E'; + +export const CAMELOT_ARBITRUM_QUOTER_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'address', name: '_factory', type: 'address' }, + { internalType: 'address', name: '_WNativeToken', type: 'address' }, + { internalType: 'address', name: '_poolDeployer', type: 'address' } + ], + stateMutability: 'nonpayable', + type: 'constructor' + }, + { + inputs: [], + name: 'WNativeToken', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'int256', name: 'amount0Delta', type: 'int256' }, + { internalType: 'int256', name: 'amount1Delta', type: 'int256' }, + { internalType: 'bytes', name: 'path', type: 'bytes' } + ], + name: 'algebraSwapCallback', + outputs: [], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'factory', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'poolDeployer', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' } + ], + name: 'quoteExactInput', + outputs: [ + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint16[]', name: 'fees', type: 'uint16[]' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactInputSingle', + outputs: [ + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint16', name: 'fee', type: 'uint16' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' } + ], + name: 'quoteExactOutput', + outputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint16[]', name: 'fees', type: 'uint16[]' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactOutputSingle', + outputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint16', name: 'fee', type: 'uint16' } + ], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/curve-arbitrum/curve-arbitrum-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/curve-arbitrum/curve-arbitrum-provider.ts new file mode 100644 index 0000000..b7d27fd --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/curve-arbitrum/curve-arbitrum-provider.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { CurveArbitrumTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/curve-arbitrum/curve-arbitrum-trade'; +import { CurveAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider'; + +export class CurveArbitrumProvider extends CurveAbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ARBITRUM; + + public readonly Trade = CurveArbitrumTrade; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/curve-arbitrum/curve-arbitrum-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/curve-arbitrum/curve-arbitrum-trade.ts new file mode 100644 index 0000000..e8f1854 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/curve-arbitrum/curve-arbitrum-trade.ts @@ -0,0 +1,3 @@ +import { CurveAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade'; + +export class CurveArbitrumTrade extends CurveAbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/default-constants.ts new file mode 100644 index 0000000..a451d80 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/default-constants.ts @@ -0,0 +1,20 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultArbitrumRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.ARBITRUM]!.address, // WETH + '0xfea7a6a0b346362bf88a9e4a88416b77a57d6c2a', // MIM + '0x6c2c06790b3e3e3c38e12ee22f8183b37a13ee55', // DPX + '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // USDC + '0x32eb7902d4134bf98a28b963d26de779af92a212', // RDPX + '0x539bde0d7dbd336b79148aa742883198bbf60342' // MAGIC +]; + +const defaultArbitrumWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.ARBITRUM]!.address; + +export const defaultArbitrumProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 1, + routingProvidersAddresses: defaultArbitrumRoutingProvidersAddresses, + wethAddress: defaultArbitrumWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/izumi-arbitrum/izumi-arbitrum-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/izumi-arbitrum/izumi-arbitrum-provider.ts new file mode 100644 index 0000000..6b99b65 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/izumi-arbitrum/izumi-arbitrum-provider.ts @@ -0,0 +1,21 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiArbitrumProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ARBITRUM; + + protected readonly dexAddress = '0x01fDea353849cA29F778B2663BcaCA1D191bED0e'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0x64b005eD986ed5D6aeD7125F49e61083c46b8e02', + liquidityManagerAddress: '0xAD1F11FBB288Cd13819cCB9397E59FAAB4Cdc16F', + routingTokenAddresses: [ + '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // USDC + '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', // USDT + '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1' // WETH + ], + multicallAddress: '0x844A47ad42187F255e5523D4d3Be33f6e94786f8', + supportedFees: [400, 2000, 10_000] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/sushi-swap-arbitrum/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/sushi-swap-arbitrum/constants.ts new file mode 100644 index 0000000..6d82ecb --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/sushi-swap-arbitrum/constants.ts @@ -0,0 +1,5 @@ +import { defaultArbitrumProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/default-constants'; + +export const SUSHI_SWAP_ARBITRUM_CONTRACT_ADDRESS = '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'; + +export const SUSHI_SWAP_ARBITRUM_PROVIDER_CONFIGURATION = defaultArbitrumProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/sushi-swap-arbitrum/sushi-swap-arbitrum-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/sushi-swap-arbitrum/sushi-swap-arbitrum-provider.ts new file mode 100644 index 0000000..05c5427 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/sushi-swap-arbitrum/sushi-swap-arbitrum-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { SUSHI_SWAP_ARBITRUM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/sushi-swap-arbitrum/constants'; +import { SushiSwapArbitrumTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/sushi-swap-arbitrum/sushi-swap-arbitrum-trade'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class SushiSwapArbitrumProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ARBITRUM; + + public readonly UniswapV2TradeClass = SushiSwapArbitrumTrade; + + public readonly providerSettings = SUSHI_SWAP_ARBITRUM_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/sushi-swap-arbitrum/sushi-swap-arbitrum-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/sushi-swap-arbitrum/sushi-swap-arbitrum-trade.ts new file mode 100644 index 0000000..99409f6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/sushi-swap-arbitrum/sushi-swap-arbitrum-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { SUSHI_SWAP_ARBITRUM_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/sushi-swap-arbitrum/constants'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class SushiSwapArbitrumTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SUSHI_SWAP; + } + + public readonly dexContractAddress = SUSHI_SWAP_ARBITRUM_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/constants/provider-configuration.ts new file mode 100644 index 0000000..8398c8c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/constants/provider-configuration.ts @@ -0,0 +1,8 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const UNI_SWAP_V3_ARBITRUM_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.ARBITRUM]!.address, + maxTransitTokens: 1 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/constants/router-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/constants/router-configuration.ts new file mode 100644 index 0000000..2a38d3e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/constants/router-configuration.ts @@ -0,0 +1,66 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { + UniswapV3RouterConfiguration, + UniswapV3RouterLiquidityPool +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration'; + +/** + * Most popular tokens in uni v3 to use in a route. + */ +const tokensSymbols = ['WETH', 'GMX', 'USDC', 'WBTC', 'DAI'] as const; + +type TokenSymbol = (typeof tokensSymbols)[number]; + +const routerTokens: Record = { + WETH: wrappedNativeTokensList[BLOCKCHAIN_NAME.ARBITRUM]!.address, + GMX: '0xfc5A1A6EB076a2C7aD06eD22C90d7E710E35ad0a', + USDC: '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', + WBTC: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f', + DAI: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1' +}; + +const routerLiquidityPools: UniswapV3RouterLiquidityPool[] = [ + { + poolAddress: '0x80A9ae39310abf666A87C743d6ebBD0E8C42158E', + tokenSymbolA: 'WETH', + tokenSymbolB: 'GMX', + fee: 10000 + }, + { + poolAddress: '0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443', + tokenSymbolA: 'WETH', + tokenSymbolB: 'USDC', + fee: 500 + }, + { + poolAddress: '0x149e36E72726e0BceA5c59d40df2c43F60f5A22D', + tokenSymbolA: 'WBTC', + tokenSymbolB: 'WETH', + fee: 3000 + }, + { + poolAddress: '0x17c14D2c404D167802b16C450d3c99F88F2c4F4d', + tokenSymbolA: 'WETH', + tokenSymbolB: 'USDC', + fee: 3000 + }, + { + poolAddress: '0x2f5e87C9312fa29aed5c179E456625D79015299c', + tokenSymbolA: 'WBTC', + tokenSymbolB: 'WETH', + fee: 500 + }, + { + poolAddress: '0xd37Af656Abf91c7f548FfFC0133175b5e4d3d5e6', + tokenSymbolA: 'DAI', + tokenSymbolB: 'USDC', + fee: 500 + } +]; + +export const UNI_SWAP_V3_ARBITRUM_ROUTER_CONFIGURATION: UniswapV3RouterConfiguration = + { + tokens: routerTokens, + liquidityPools: routerLiquidityPools + }; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-provider.ts new file mode 100644 index 0000000..d10f241 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-provider.ts @@ -0,0 +1,21 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UNI_SWAP_V3_ARBITRUM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/constants/provider-configuration'; +import { UNI_SWAP_V3_ARBITRUM_ROUTER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/constants/router-configuration'; +import { UniSwapV3ArbitrumTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-trade'; +import { UniswapV3AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-provider'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; + +export class UniSwapV3ArbitrumProvider extends UniswapV3AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ARBITRUM; + + protected readonly OnChainTradeClass = UniSwapV3ArbitrumTrade; + + protected readonly providerConfiguration = UNI_SWAP_V3_ARBITRUM_PROVIDER_CONFIGURATION; + + protected readonly routerConfiguration = UNI_SWAP_V3_ARBITRUM_ROUTER_CONFIGURATION; + + protected readonly quoterController = new UniswapV3QuoterController( + this.blockchain, + this.routerConfiguration + ); +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-trade.ts new file mode 100644 index 0000000..06701e5 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-trade.ts @@ -0,0 +1,3 @@ +import { UniswapV3AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-trade'; + +export class UniSwapV3ArbitrumTrade extends UniswapV3AbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/algebra-integral-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/algebra-integral-provider.ts new file mode 100644 index 0000000..f3c90bf --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/algebra-integral-provider.ts @@ -0,0 +1,65 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { createTokenNativeAddressProxyInPathStartAndEnd } from 'src/features/common/utils/token-native-address-proxy'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraTradeStructOmitPath } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UniswapV3AlgebraAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider'; + +import { defaultArtheraProviderConfiguration } from '../default-constants'; +import { AlgebraIntegralTrade } from './algebra-integral-trade'; +import { ALGEBRA_INTEGRAL_PROVIDER_CONFIGURATION } from './constants/provider-configuration'; +import { + ALGEBRA_INTEGRAL_ROUTER_CONTRACT_ABI, + ALGEBRA_INTEGRAL_ROUTER_CONTRACT_ADDRESS +} from './constants/swap-router-contract-data'; +import { AlgebraIntegralRoute } from './models/algebra-integral-route'; +import { + ALGEBRA_INTEGRAL_QUOTER_CONTRACT_ABI, + ALGEBRA_INTEGRAL_QUOTER_CONTRACT_ADDRESS +} from './utils/quoter-controller/constants/quoter-contract-data'; + +export class AlgebraIntegralProvider extends UniswapV3AlgebraAbstractProvider { + protected readonly contractAddress = ALGEBRA_INTEGRAL_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = ALGEBRA_INTEGRAL_ROUTER_CONTRACT_ABI; + + public readonly blockchain = BLOCKCHAIN_NAME.ARTHERA; + + protected readonly OnChainTradeClass = AlgebraIntegralTrade; + + protected readonly providerConfiguration = ALGEBRA_INTEGRAL_PROVIDER_CONFIGURATION; + + protected readonly quoterController = new AlgebraQuoterController( + this.blockchain, + defaultArtheraProviderConfiguration.routingProvidersAddresses, + ALGEBRA_INTEGRAL_QUOTER_CONTRACT_ADDRESS, + ALGEBRA_INTEGRAL_QUOTER_CONTRACT_ABI + ); + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ALGEBRA_INTEGRAL; + } + + protected createTradeInstance( + tradeStruct: UniswapV3AlgebraTradeStructOmitPath, + route: AlgebraIntegralRoute, + providerAddress: string + ): AlgebraIntegralTrade { + const path = createTokenNativeAddressProxyInPathStartAndEnd( + route.path, + EvmWeb3Pure.nativeTokenAddress + ); + return new AlgebraIntegralTrade( + { + ...tradeStruct, + path, + route + }, + providerAddress + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/algebra-integral-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/algebra-integral-trade.ts new file mode 100644 index 0000000..2176a21 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/algebra-integral-trade.ts @@ -0,0 +1,78 @@ +import { Token } from 'src/common/tokens'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade'; + +import { + ALGEBRA_INTEGRAL_ROUTER_CONTRACT_ABI, + ALGEBRA_INTEGRAL_ROUTER_CONTRACT_ADDRESS +} from './constants/swap-router-contract-data'; +import { AlgebraIntegralRoute } from './models/algebra-integral-route'; +import { AlgebraIntegralTradeStruct } from './models/algebra-integral-trade-struct'; + +export class AlgebraIntegralTrade extends UniswapV3AlgebraAbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ALGEBRA_INTEGRAL; + } + + public readonly dexContractAddress = ALGEBRA_INTEGRAL_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = ALGEBRA_INTEGRAL_ROUTER_CONTRACT_ABI; + + protected readonly unwrapWethMethodName = 'unwrapWNativeToken'; + + private readonly route: AlgebraIntegralRoute; + + public readonly wrappedPath: ReadonlyArray; + + constructor(tradeStruct: AlgebraIntegralTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this.route = tradeStruct.route; + + this.wrappedPath = this.route.path; + } + + /** + * Returns swap `exactInput` method's name and arguments to use in Swap contract. + */ + protected getSwapRouterExactInputMethodData(walletAddress: string): MethodData { + const amountParams = this.getAmountParams(); + + if (this.route.path.length === 2 && this.route?.path?.[0] && this.route?.path?.[1]) { + const methodName = this.exact === 'input' ? 'exactInputSingle' : 'exactOutputSingle'; + + return { + methodName, + methodArguments: [ + [ + this.route.path[0].address, + this.route.path[1].address, + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams, + 0 + ] + ] + }; + } + + const methodName = this.exact === 'input' ? 'exactInput' : 'exactOutput'; + + return { + methodName, + methodArguments: [ + [ + AlgebraQuoterController.getEncodedPath(this.route.path), + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams + ] + ] + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/constants/provider-configuration.ts new file mode 100644 index 0000000..f9042b4 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/constants/provider-configuration.ts @@ -0,0 +1,8 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const ALGEBRA_INTEGRAL_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.ARTHERA]!.address, + maxTransitTokens: 1 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/constants/swap-router-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/constants/swap-router-contract-data.ts new file mode 100644 index 0000000..b181b45 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/constants/swap-router-contract-data.ts @@ -0,0 +1,109 @@ +import { AbiItem } from 'web3-utils'; + +export const ALGEBRA_INTEGRAL_ROUTER_CONTRACT_ADDRESS = + '0xa77aD9f635a3FB3bCCC5E6d1A87cB269746Aba17'; + +export const ALGEBRA_INTEGRAL_ROUTER_CONTRACT_ABI = [ + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactInputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactInputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactOutputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactOutputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' }], + name: 'multicall', + outputs: [{ internalType: 'bytes[]', name: 'results', type: 'bytes[]' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountMinimum', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'unwrapWNativeToken', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { stateMutability: 'payable', type: 'receive' } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/models/algebra-integral-route.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/models/algebra-integral-route.ts new file mode 100644 index 0000000..ffae94c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/models/algebra-integral-route.ts @@ -0,0 +1,9 @@ +import { Token } from 'src/common/tokens'; +import { UniswapV3AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route'; + +export interface AlgebraIntegralRoute extends UniswapV3AlgebraRoute { + /** + * List of pools' contract addresses to use in a trade's route. + */ + path: Token[]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/models/algebra-integral-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/models/algebra-integral-trade-struct.ts new file mode 100644 index 0000000..29c9baa --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/models/algebra-integral-trade-struct.ts @@ -0,0 +1,7 @@ +import { UniswapV3AlgebraTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; + +import { AlgebraIntegralRoute } from './algebra-integral-route'; + +export interface AlgebraIntegralTradeStruct extends UniswapV3AlgebraTradeStruct { + route: AlgebraIntegralRoute; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/utils/quoter-controller/constants/quoter-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/utils/quoter-controller/constants/quoter-contract-data.ts new file mode 100644 index 0000000..d719114 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/algebra-integral/utils/quoter-controller/constants/quoter-contract-data.ts @@ -0,0 +1,51 @@ +import { AbiItem } from 'web3-utils'; + +export const ALGEBRA_INTEGRAL_QUOTER_CONTRACT_ADDRESS = + '0x03f8B4b140249Dc7B2503C928E7258CCe1d91F1A'; + +export const ALGEBRA_INTEGRAL_QUOTER_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' } + ], + name: 'quoteExactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' } + ], + name: 'quoteExactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/default-constants.ts new file mode 100644 index 0000000..a867a43 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/arthera-testnet/default-constants.ts @@ -0,0 +1,17 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultArtheraRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.ARTHERA]!.address, // WMATIC + '0xEC250E6856e14A494cb1f0abC61d72348c79F418', // USDT + '0x83D4a9Ea77a4dbA073cD90b30410Ac9F95F93E7C' // USDC +]; + +const defaultArtheraWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.ARTHERA]!.address; + +export const defaultArtheraProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 3, + routingProvidersAddresses: defaultArtheraRoutingProvidersAddresses, + wethAddress: defaultArtheraWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/astar-evm/arth-swap/arth-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/astar-evm/arth-swap/arth-swap-provider.ts new file mode 100644 index 0000000..30afb29 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/astar-evm/arth-swap/arth-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { ArthSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/astar-evm/arth-swap/arth-swap-trade'; +import { ARTH_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/astar-evm/arth-swap/constants'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class ArthSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ASTAR_EVM; + + public readonly UniswapV2TradeClass = ArthSwapTrade; + + public readonly providerSettings = ARTH_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/astar-evm/arth-swap/arth-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/astar-evm/arth-swap/arth-swap-trade.ts new file mode 100644 index 0000000..044073c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/astar-evm/arth-swap/arth-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { ARTH_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/astar-evm/arth-swap/constants'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class ArthSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ARTH_SWAP; + } + + public readonly dexContractAddress = ARTH_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/astar-evm/arth-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/astar-evm/arth-swap/constants.ts new file mode 100644 index 0000000..7d47b2a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/astar-evm/arth-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultAstarEvmProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/astar-evm/default-constants'; + +export const ARTH_SWAP_CONTRACT_ADDRESS = '0xE915D2393a08a00c5A463053edD31bAe2199b9e7'; + +export const ARTH_SWAP_PROVIDER_CONFIGURATION = defaultAstarEvmProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/astar-evm/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/astar-evm/default-constants.ts new file mode 100644 index 0000000..d076249 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/astar-evm/default-constants.ts @@ -0,0 +1,24 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultAstarEvmRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.ASTAR_EVM]!.address, // WASTR + '0xefaeee334f0fd1712f9a8cc375f427d9cdd40d73', // mUSDT + '0x3795c36e7d12a8c252a20c5a7b455f7c57b60283', // ceUSDT + '0xffffffff000000000000000000000001000007c0', // USDT + + '0xfa9343c3897324496a05fc75abed6bac29f8a40f', // mUSDC + '0x6a2d262d56735dba19dd70682b39f6be9a931d98', // ceUSDC + + '0x6de33698e9e9b787e09d3bd7771ef63557e148bb', // DAI + '0x4bf769b05e832fcdc9053fffbc78ca889acb5e1e' // BUSD +]; + +const defaultAstarEvmWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.ASTAR_EVM]!.address; + +export const defaultAstarEvmProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultAstarEvmRoutingProvidersAddresses, + wethAddress: defaultAstarEvmWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/default-constants.ts new file mode 100644 index 0000000..1e0b8e9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/default-constants.ts @@ -0,0 +1,22 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultAuroraRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.AURORA]!.address, // WETH + '0xC42C30aC6Cc15faC9bD938618BcaA1a1FaE8501d', // wNEAR + '0xFa94348467f64D5A457F75F8bc40495D33c65aBB', // TRI + '0x7faA64Faf54750a2E3eE621166635fEAF406Ab22', // WANNA + '0xB12BFcA5A55806AaF64E99521918A4bf0fC40802', // USDC + '0x4988a896b1227218e4A686fdE5EabdcAbd91571f', // USDT + '0x8BEc47865aDe3B172A928df8f990Bc7f2A3b9f79', // AURORA + '0xe3520349F477A5F6EB06107066048508498A291b' // DAI +]; + +const defaultAuroraWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.AURORA]!.address; + +export const defaultAuroraProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 1, + routingProvidersAddresses: defaultAuroraRoutingProvidersAddresses, + wethAddress: defaultAuroraWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/trisolaris-aurora/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/trisolaris-aurora/constants.ts new file mode 100644 index 0000000..a27dc7a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/trisolaris-aurora/constants.ts @@ -0,0 +1,5 @@ +import { defaultAuroraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/aurora/default-constants'; + +export const TRISOLARIS_AURORA_CONTRACT_ADDRESS = '0x2CB45Edb4517d5947aFdE3BEAbF95A582506858B'; + +export const TRISOLARIS_AURORA_PROVIDER_CONFIGURATION = defaultAuroraProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/trisolaris-aurora/trisolaris-aurora-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/trisolaris-aurora/trisolaris-aurora-provider.ts new file mode 100644 index 0000000..d3957f2 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/trisolaris-aurora/trisolaris-aurora-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { TRISOLARIS_AURORA_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/aurora/trisolaris-aurora/constants'; +import { TrisolarisAuroraTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/aurora/trisolaris-aurora/trisolaris-aurora-trade'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class TrisolarisAuroraProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.AURORA; + + public readonly UniswapV2TradeClass = TrisolarisAuroraTrade; + + public readonly providerSettings = TRISOLARIS_AURORA_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/trisolaris-aurora/trisolaris-aurora-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/trisolaris-aurora/trisolaris-aurora-trade.ts new file mode 100644 index 0000000..1c30a2e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/trisolaris-aurora/trisolaris-aurora-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { TRISOLARIS_AURORA_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/aurora/trisolaris-aurora/constants'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class TrisolarisAuroraTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.TRISOLARIS; + } + + public readonly dexContractAddress = TRISOLARIS_AURORA_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/wanna-swap-aurora/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/wanna-swap-aurora/constants.ts new file mode 100644 index 0000000..87800a6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/wanna-swap-aurora/constants.ts @@ -0,0 +1,5 @@ +import { defaultAuroraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/aurora/default-constants'; + +export const WANNA_SWAP_AURORA_CONTRACT_ADDRESS = '0xa3a1eF5Ae6561572023363862e238aFA84C72ef5'; + +export const WANNA_SWAP_AURORA_PROVIDER_CONFIGURATION = defaultAuroraProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/wanna-swap-aurora/wanna-swap-aurora-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/wanna-swap-aurora/wanna-swap-aurora-provider.ts new file mode 100644 index 0000000..7f9b66f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/wanna-swap-aurora/wanna-swap-aurora-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { WANNA_SWAP_AURORA_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/aurora/wanna-swap-aurora/constants'; +import { WannaSwapAuroraTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/aurora/wanna-swap-aurora/wanna-swap-aurora-trade'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class WannaSwapAuroraProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.AURORA; + + public readonly UniswapV2TradeClass = WannaSwapAuroraTrade; + + public readonly providerSettings = WANNA_SWAP_AURORA_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/wanna-swap-aurora/wanna-swap-aurora-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/wanna-swap-aurora/wanna-swap-aurora-trade.ts new file mode 100644 index 0000000..f2efa9b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/aurora/wanna-swap-aurora/wanna-swap-aurora-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { WANNA_SWAP_AURORA_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/aurora/wanna-swap-aurora/constants'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class WannaSwapAuroraTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.WANNA_SWAP; + } + + public readonly dexContractAddress = WANNA_SWAP_AURORA_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/avax-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/avax-abi.ts new file mode 100644 index 0000000..087473f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/avax-abi.ts @@ -0,0 +1,100 @@ +import { AbiItem } from 'web3-utils'; + +export const AVAX_ABI = [ + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'getAmountsIn', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' } + ] + }, + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'getAmountsOut', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactAVAXForTokens', + inputs: [ + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'swapExactAVAXForTokensSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactTokensForAVAX', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [], + name: 'swapExactTokensForAVAXSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactTokensForTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [], + name: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/curve-avalanche/curve-avalanche-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/curve-avalanche/curve-avalanche-provider.ts new file mode 100644 index 0000000..28435b0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/curve-avalanche/curve-avalanche-provider.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { CurveAvalancheTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/curve-avalanche/curve-avalanche-trade'; +import { CurveAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider'; + +export class CurveAvalancheProvider extends CurveAbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.AVALANCHE; + + public readonly Trade = CurveAvalancheTrade; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/curve-avalanche/curve-avalanche-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/curve-avalanche/curve-avalanche-trade.ts new file mode 100644 index 0000000..2641125 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/curve-avalanche/curve-avalanche-trade.ts @@ -0,0 +1,3 @@ +import { CurveAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade'; + +export class CurveAvalancheTrade extends CurveAbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/default-constants.ts new file mode 100644 index 0000000..85a7ec9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/default-constants.ts @@ -0,0 +1,21 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultAvalancheRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.AVALANCHE]!.address, // WAVAX + '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', // USDT + '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', // USDC + '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', // DAI + '0x60781C2586D68229fde47564546784ab3fACA982', // PNG + '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB', // WETH + '0xd1c3f94DE7e5B45fa4eDBBA472491a9f4B166FC4' // XAVA +]; + +const defaultAvalancheWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.AVALANCHE]!.address; + +export const defaultAvalancheProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 3, + routingProvidersAddresses: defaultAvalancheRoutingProvidersAddresses, + wethAddress: defaultAvalancheWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/constants.ts new file mode 100644 index 0000000..007c99c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/constants.ts @@ -0,0 +1,20 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { defaultAvalancheProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/default-constants'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +export const JOE_CONTRACT_ADDRESS = '0x60aE616a2155Ee3d9A68541Ba4544862310933d4'; + +const routingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.AVALANCHE]!.address, // WAVAX + '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', // USDT + '0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd', // JOE + '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB', // WETH + '0xd1c3f94DE7e5B45fa4eDBBA472491a9f4B166FC4', // XAVA + '0x8729438EB15e2C8B576fCc6AeCdA6A148776C0F5' // QI +]; + +export const JOE_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultAvalancheProviderConfiguration, + routingProvidersAddresses +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/joe-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/joe-provider.ts new file mode 100644 index 0000000..6ff0d1a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/joe-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { JOE_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/constants'; +import { JoeTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/joe-trade'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class JoeProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.AVALANCHE; + + public readonly UniswapV2TradeClass = JoeTrade; + + public readonly providerSettings = JOE_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/joe-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/joe-trade.ts new file mode 100644 index 0000000..810f634 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/joe-trade.ts @@ -0,0 +1,20 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AVAX_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/avax-abi'; +import { JOE_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/joe/constants'; +import { AVALANCHE_SWAP_METHOD } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/swap-methods'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class JoeTrade extends UniswapV2AbstractTrade { + public static readonly contractAbi = AVAX_ABI; + + public static readonly swapMethods = AVALANCHE_SWAP_METHOD; + + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.JOE; + } + + public readonly dexContractAddress = JOE_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/constants.ts new file mode 100644 index 0000000..19c6960 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/constants.ts @@ -0,0 +1,20 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { defaultAvalancheProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/default-constants'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +export const PANGOLIN_CONTRACT_ADDRESS = '0xE54Ca86531e17Ef3616d22Ca28b0D458b6C89106'; + +const routingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.AVALANCHE]!.address, // WAVAX + '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', // USDT + '0x60781C2586D68229fde47564546784ab3fACA982', // PNG + '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB', // WETH + '0xd1c3f94DE7e5B45fa4eDBBA472491a9f4B166FC4', // XAVA + '0x8729438EB15e2C8B576fCc6AeCdA6A148776C0F5' // QI +]; + +export const PANGOLIN_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultAvalancheProviderConfiguration, + routingProvidersAddresses +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/pangolin-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/pangolin-provider.ts new file mode 100644 index 0000000..f922029 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/pangolin-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { PANGOLIN_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/constants'; +import { PangolinTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/pangolin-trade'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class PangolinProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.AVALANCHE; + + public readonly UniswapV2TradeClass = PangolinTrade; + + public readonly providerSettings = PANGOLIN_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/pangolin-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/pangolin-trade.ts new file mode 100644 index 0000000..8477d1c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/pangolin-trade.ts @@ -0,0 +1,20 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AVAX_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/avax-abi'; +import { PANGOLIN_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/pangolin/constants'; +import { AVALANCHE_SWAP_METHOD } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/swap-methods'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class PangolinTrade extends UniswapV2AbstractTrade { + public static readonly contractAbi = AVAX_ABI; + + public static readonly swapMethods = AVALANCHE_SWAP_METHOD; + + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.PANGOLIN; + } + + public readonly dexContractAddress = PANGOLIN_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/constants.ts new file mode 100644 index 0000000..76b755f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/constants.ts @@ -0,0 +1,5 @@ +import { defaultAvalancheProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/default-constants'; + +export const SUSHI_SWAP_AVALANCHE_CONTRACT_ADDRESS = '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'; + +export const SUSHI_SWAP_AVALANCHE_PROVIDER_CONFIGURATION = defaultAvalancheProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/sushi-swap-avalanche-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/sushi-swap-avalanche-provider.ts new file mode 100644 index 0000000..6191ef9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/sushi-swap-avalanche-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { SUSHI_SWAP_AVALANCHE_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/constants'; +import { SushiSwapAvalancheTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/sushi-swap-avalanche-trade'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class SushiSwapAvalancheProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.AVALANCHE; + + public readonly UniswapV2TradeClass = SushiSwapAvalancheTrade; + + public readonly providerSettings = SUSHI_SWAP_AVALANCHE_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/sushi-swap-avalanche-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/sushi-swap-avalanche-trade.ts new file mode 100644 index 0000000..228d448 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/sushi-swap-avalanche-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { SUSHI_SWAP_AVALANCHE_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/sushi-swap-avalanche/constants'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class SushiSwapAvalancheTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SUSHI_SWAP; + } + + public readonly dexContractAddress = SUSHI_SWAP_AVALANCHE_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/swap-methods.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/swap-methods.ts new file mode 100644 index 0000000..c7a1a95 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/avalanche/swap-methods.ts @@ -0,0 +1,45 @@ +import { + ExactInputOutputSwapMethodsList, + RegularSwapMethodsList, + SupportingFeeSwapMethodsList, + SwapMethodsList +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/SWAP_METHOD'; + +export const AVALANCHE_EXACT_INPUT_REGULAR_SWAP_METHOD: RegularSwapMethodsList = { + TOKENS_TO_TOKENS: 'swapExactTokensForTokens', + ETH_TO_TOKENS: 'swapExactAVAXForTokens', + TOKENS_TO_ETH: 'swapExactTokensForAVAX' +}; + +export const AVALANCHE_EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD: SupportingFeeSwapMethodsList = { + TOKENS_TO_TOKENS_SUPPORTING_FEE: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + ETH_TO_TOKENS_SUPPORTING_FEE: 'swapExactAVAXForTokensSupportingFeeOnTransferTokens', + TOKENS_TO_ETH_SUPPORTING_FEE: 'swapExactTokensForAVAXSupportingFeeOnTransferTokens' +}; + +export const AVALANCHE_EXACT_INPUT_SWAP_METHOD: SwapMethodsList = { + ...AVALANCHE_EXACT_INPUT_REGULAR_SWAP_METHOD, + ...AVALANCHE_EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD +}; + +export const AVALANCHE_EXACT_OUTPUT_REGULAR_SWAP_METHOD: RegularSwapMethodsList = { + TOKENS_TO_TOKENS: 'swapTokensForExactTokens', + ETH_TO_TOKENS: 'swapAVAXForExactTokens', + TOKENS_TO_ETH: 'swapTokensForExactAVAX' +}; + +export const AVALANCHE_EXACT_OUTPUT_SUPPORTING_FEE_SWAP_METHOD: SupportingFeeSwapMethodsList = { + TOKENS_TO_TOKENS_SUPPORTING_FEE: 'swapTokensForExactTokensSupportingFeeOnTransferTokens', + ETH_TO_TOKENS_SUPPORTING_FEE: 'swapAVAXForExactTokensSupportingFeeOnTransferTokens', + TOKENS_TO_ETH_SUPPORTING_FEE: 'swapTokensForExactAVAXSupportingFeeOnTransferTokens' +}; + +export const AVALANCHE_EXACT_OUTPUT_SWAP_METHOD: SwapMethodsList = { + ...AVALANCHE_EXACT_OUTPUT_REGULAR_SWAP_METHOD, + ...AVALANCHE_EXACT_OUTPUT_SUPPORTING_FEE_SWAP_METHOD +}; + +export const AVALANCHE_SWAP_METHOD: ExactInputOutputSwapMethodsList = { + input: AVALANCHE_EXACT_INPUT_SWAP_METHOD, + output: AVALANCHE_EXACT_OUTPUT_SWAP_METHOD +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-abi.ts new file mode 100644 index 0000000..5a293d1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-abi.ts @@ -0,0 +1,251 @@ +import { AbiItem } from 'web3-utils'; + +export const AERODROME_ABI = [ + { + inputs: [], + name: 'ETHER', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }, + { + components: [ + { internalType: 'address', name: 'from', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'bool', name: 'stable', type: 'bool' }, + { internalType: 'address', name: 'factory', type: 'address' } + ], + internalType: 'struct IRouter.Route[]', + name: 'routes', + type: 'tuple[]' + }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' } + ], + name: 'UNSAFE_swapExactTokensForTokens', + outputs: [{ internalType: 'uint256[]', name: '', type: 'uint256[]' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [], + name: 'defaultFactory', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'factoryRegistry', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { + components: [ + { internalType: 'address', name: 'from', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'bool', name: 'stable', type: 'bool' }, + { internalType: 'address', name: 'factory', type: 'address' } + ], + internalType: 'struct IRouter.Route[]', + name: 'routes', + type: 'tuple[]' + } + ], + name: 'getAmountsOut', + outputs: [{ internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenA', type: 'address' }, + { internalType: 'address', name: 'tokenB', type: 'address' }, + { internalType: 'bool', name: 'stable', type: 'bool' }, + { internalType: 'address', name: '_factory', type: 'address' } + ], + name: 'getReserves', + outputs: [ + { internalType: 'uint256', name: 'reserveA', type: 'uint256' }, + { internalType: 'uint256', name: 'reserveB', type: 'uint256' } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'address', name: 'forwarder', type: 'address' }], + name: 'isTrustedForwarder', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenA', type: 'address' }, + { internalType: 'address', name: 'tokenB', type: 'address' }, + { internalType: 'bool', name: 'stable', type: 'bool' }, + { internalType: 'address', name: '_factory', type: 'address' } + ], + name: 'poolFor', + outputs: [{ internalType: 'address', name: 'pool', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenA', type: 'address' }, + { internalType: 'address', name: 'tokenB', type: 'address' } + ], + name: 'sortTokens', + outputs: [ + { internalType: 'address', name: 'token0', type: 'address' }, + { internalType: 'address', name: 'token1', type: 'address' } + ], + stateMutability: 'pure', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' }, + { + components: [ + { internalType: 'address', name: 'from', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'bool', name: 'stable', type: 'bool' }, + { internalType: 'address', name: 'factory', type: 'address' } + ], + internalType: 'struct IRouter.Route[]', + name: 'routes', + type: 'tuple[]' + }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' } + ], + name: 'swapExactETHForTokens', + outputs: [{ internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' }, + { + components: [ + { internalType: 'address', name: 'from', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'bool', name: 'stable', type: 'bool' }, + { internalType: 'address', name: 'factory', type: 'address' } + ], + internalType: 'struct IRouter.Route[]', + name: 'routes', + type: 'tuple[]' + }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' } + ], + name: 'swapExactETHForTokensSupportingFeeOnTransferTokens', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' }, + { + components: [ + { internalType: 'address', name: 'from', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'bool', name: 'stable', type: 'bool' }, + { internalType: 'address', name: 'factory', type: 'address' } + ], + internalType: 'struct IRouter.Route[]', + name: 'routes', + type: 'tuple[]' + }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' } + ], + name: 'swapExactTokensForETH', + outputs: [{ internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' }, + { + components: [ + { internalType: 'address', name: 'from', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'bool', name: 'stable', type: 'bool' }, + { internalType: 'address', name: 'factory', type: 'address' } + ], + internalType: 'struct IRouter.Route[]', + name: 'routes', + type: 'tuple[]' + }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' } + ], + name: 'swapExactTokensForETHSupportingFeeOnTransferTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' }, + { + components: [ + { internalType: 'address', name: 'from', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'bool', name: 'stable', type: 'bool' }, + { internalType: 'address', name: 'factory', type: 'address' } + ], + internalType: 'struct IRouter.Route[]', + name: 'routes', + type: 'tuple[]' + }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' } + ], + name: 'swapExactTokensForTokens', + outputs: [{ internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' }, + { + components: [ + { internalType: 'address', name: 'from', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'bool', name: 'stable', type: 'bool' }, + { internalType: 'address', name: 'factory', type: 'address' } + ], + internalType: 'struct IRouter.Route[]', + name: 'routes', + type: 'tuple[]' + }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' } + ], + name: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { stateMutability: 'payable', type: 'receive' } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-path-factory.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-path-factory.ts new file mode 100644 index 0000000..ae5892a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-path-factory.ts @@ -0,0 +1,172 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { Token } from 'src/common/tokens'; +import { notNull } from 'src/common/utils/object'; +import { AerodromeTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-trade'; +import { AerodromeRoutesMethodArgument } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/aerodrome-route-method-arguments'; +import { UniswapRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-route'; +import { PathFactory } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/path-factory'; + +interface Routes { + path: Token[]; + methodArguments: AerodromeRoutesMethodArgument; +} + +export class AerodromePathFactory extends PathFactory { + private routes: Routes[] = []; + + private calculateRoutes(tokens: string[], finalPath: Token[]): void { + const updRoutesMethodArgumentsWithTransitToken = ( + finalPath: Token[], + tokenA: string, + tokenB: string, + tokenC: string, + isPairAStablePool: boolean, + isPairBStablePool: boolean + ) => { + this.routes.push({ + path: finalPath, + methodArguments: [ + this.stringWeiAmount, + [ + [ + tokenA, + tokenB, + isPairAStablePool, + '0x420DD381b31aEf6683db6B902084cB0FFECe40Da' + ], + [ + tokenB, + tokenC, + isPairBStablePool, + '0x420DD381b31aEf6683db6B902084cB0FFECe40Da' + ] + ] + ] + }); + }; + + const updRoutesMethodArgumentsWithoutTransitToken = ( + finalPath: Token[], + tokenA: string, + tokenB: string, + isStable: boolean + ) => { + this.routes.push({ + path: finalPath, + methodArguments: [ + this.stringWeiAmount, + [[tokenA, tokenB, isStable, '0x420DD381b31aEf6683db6B902084cB0FFECe40Da']] + ] + }); + }; + + if (tokens.length > 3) { + throw new RubicSdkError('Maximum number of transit tokens: 1'); + } + + if (tokens.length === 2) { + updRoutesMethodArgumentsWithoutTransitToken(finalPath, tokens[0]!, tokens[1]!, true); + updRoutesMethodArgumentsWithoutTransitToken(finalPath, tokens[0]!, tokens[1]!, false); + } + + if (tokens.length === 3) { + updRoutesMethodArgumentsWithTransitToken( + finalPath, + tokens[0]!, + tokens[1]!, + tokens[2]!, + true, + true + ); + updRoutesMethodArgumentsWithTransitToken( + finalPath, + tokens[0]!, + tokens[1]!, + tokens[2]!, + false, + false + ); + updRoutesMethodArgumentsWithTransitToken( + finalPath, + tokens[0]!, + tokens[1]!, + tokens[2]!, + false, + true + ); + updRoutesMethodArgumentsWithTransitToken( + finalPath, + tokens[0]!, + tokens[1]!, + tokens[2]!, + true, + false + ); + } + } + + protected async getAllRoutes(): Promise { + const transitTokens = await Token.createTokens( + this.routingProvidersAddresses, + this.from.blockchain + ); + + const vertexes: Token[] = transitTokens.filter( + elem => !elem.isEqualTo(this.from) && !elem.isEqualTo(this.to) + ); + + const initialPath = [this.from]; + + const recGraphVisitor = (path: Token[], transitTokensLimit: number): void => { + if (path.length === transitTokensLimit + 1) { + const finalPath = path.concat(this.to); + + this.calculateRoutes(Token.tokensToAddresses(finalPath), finalPath); + + return; + } + + vertexes + .filter(vertex => path.every(token => !token.isEqualTo(vertex))) + .forEach(vertex => { + const extendedPath = path.concat(vertex); + recGraphVisitor(extendedPath, transitTokensLimit); + }); + }; + + for (let i = 0; i <= this.maxTransitTokens; i++) { + recGraphVisitor(initialPath, i); + } + + const responses = await this.UniswapV2TradeClass.callForRoutes( + this.from.blockchain, + this.exact, + this.routes.map(route => route.methodArguments) + ); + + const tokens = responses.map((response, index) => { + if (!response.success || !response.output || !(response.output[1] !== '0')) { + return null; + } + const amounts = response.output; + + const numberAmount = this.exact === 'input' ? amounts[amounts.length - 1] : amounts[0]; + if (!numberAmount) { + throw new RubicSdkError('Amount has to be defined'); + } + const outputAbsoluteAmount = new BigNumber(numberAmount); + + const path = this.routes?.[index]?.path; + const routPoolInfo = this.routes?.[index]?.methodArguments[1]; + + if (!path || !routPoolInfo) { + throw new RubicSdkError('Path has to be defined'); + } + + return { outputAbsoluteAmount, path, routPoolInfo }; + }); + + return tokens.filter(notNull); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-provider.ts new file mode 100644 index 0000000..a8f626f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-provider.ts @@ -0,0 +1,39 @@ +import BigNumber from 'bignumber.js'; +import { PriceToken } from 'src/common/tokens'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { OnChainProxyFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { AerodromePathFactory } from 'src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-path-factory'; +import { AerodromeTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-trade'; +import { AERODROME_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/constants'; +import { UniswapCalculatedInfo } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-calculated-info'; +import { UniswapV2CalculationOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-calculation-options'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class AerodromeProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.BASE; + + public readonly UniswapV2TradeClass = AerodromeTrade; + + public readonly providerSettings = AERODROME_PROVIDER_CONFIGURATION; + + protected async getAmountAndPath( + from: PriceToken, + to: PriceToken, + weiAmount: BigNumber, + exact: Exact, + options: UniswapV2CalculationOptions, + proxyFeeInfo: OnChainProxyFeeInfo | undefined, + gasPriceInUsd: BigNumber | undefined + ): Promise { + const pathFactory = new AerodromePathFactory(this, { + from, + to, + weiAmount, + exact, + options, + proxyFeeInfo + }); + return pathFactory.getAmountAndPath(gasPriceInUsd); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-trade.ts new file mode 100644 index 0000000..4e3c706 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-trade.ts @@ -0,0 +1,31 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AERODROME_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/aerodrome-abi'; +import { AERODROME_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/constants'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class AerodromeTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.AERODROME; + } + + public static readonly contractAbi = AERODROME_ABI; + + public readonly dexContractAddress = AERODROME_CONTRACT_ADDRESS; + + protected getCallParameters(receiverAddress?: string): unknown[] { + const { amountIn, amountOut } = this.getAmountInAndAmountOut(); + const amountParameters = this.from.isNative ? [amountOut] : [amountIn, amountOut]; + + const path = this.routPoolInfo; + + return [ + ...amountParameters, + path, + receiverAddress || this.walletAddress, + this.deadlineMinutesTimestamp + ]; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/constants.ts new file mode 100644 index 0000000..a269867 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/aerodrome/constants.ts @@ -0,0 +1,9 @@ +import { defaultBaseProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/base/default-constants'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +export const AERODROME_CONTRACT_ADDRESS = '0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43'; + +export const AERODROME_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultBaseProviderConfiguration, + maxTransitTokens: 1 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/base-swap/base-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/base-swap/base-swap-provider.ts new file mode 100644 index 0000000..674c614 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/base-swap/base-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { BaseSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/base/base-swap/base-swap-trade'; +import { BASE_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/base/base-swap/constants'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class BaseSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.BASE; + + public readonly UniswapV2TradeClass = BaseSwapTrade; + + public readonly providerSettings = BASE_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/base-swap/base-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/base-swap/base-swap-trade.ts new file mode 100644 index 0000000..b32f5f1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/base-swap/base-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { BASE_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/base/base-swap/constants'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class BaseSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.BASE_SWAP; + } + + public readonly dexContractAddress = BASE_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/base-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/base-swap/constants.ts new file mode 100644 index 0000000..a5fb51a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/base-swap/constants.ts @@ -0,0 +1,9 @@ +import { defaultBaseProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/base/default-constants'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +export const BASE_SWAP_CONTRACT_ADDRESS = '0x327Df1E6de05895d2ab08513aaDD9313Fe505d86'; + +export const BASE_SWAP_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultBaseProviderConfiguration, + maxTransitTokens: 2 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/default-constants.ts new file mode 100644 index 0000000..0e431b3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/default-constants.ts @@ -0,0 +1,21 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultBaseRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.BASE]!.address, // WETH + '0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22', // cbETH + '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', // USDbC + '0xB79DD08EA68A908A97220C76d19A6aA9cBDE4376', // USD+ + '0xEB466342C4d449BC9f53A865D5Cb90586f405215', // axlUSDb + '0x78a087d713Be963Bf307b18F2Ff8122EF9A63ae9', // BSWAP + '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb' // DAI +]; + +const defaultBaseWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.BASE]!.address; + +export const defaultBaseProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultBaseRoutingProvidersAddresses, + wethAddress: defaultBaseWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/izumi-base/izumi-base-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/izumi-base/izumi-base-provider.ts new file mode 100644 index 0000000..8200031 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/base/izumi-base/izumi-base-provider.ts @@ -0,0 +1,22 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiBaseProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.BASE; + + protected readonly dexAddress = '0x02F55D53DcE23B4AA962CC68b0f685f26143Bdb2'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0x3EF68D3f7664b2805D4E88381b64868a56f88bC4', + liquidityManagerAddress: '0x110dE362cc436D7f54210f96b8C7652C2617887D', + routingTokenAddresses: [ + '0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d', // iUSD + wrappedNativeTokensList[BLOCKCHAIN_NAME.BASE]!.address, // WBNB + '0x60d01ec2d5e98ac51c8b4cf84dfcce98d527c747' // IZI + ], + multicallAddress: '0x7a524c7e82874226F0b51aade60A1BE4D430Cf0F', + supportedFees: [10000, 3000, 500] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/berachain-testnet-algebra-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/berachain-testnet-algebra-provider.ts new file mode 100644 index 0000000..a70fa62 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/berachain-testnet-algebra-provider.ts @@ -0,0 +1,65 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { createTokenNativeAddressProxyInPathStartAndEnd } from 'src/features/common/utils/token-native-address-proxy'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { BerachainTestnetAlgebraTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/berachain-testnet-algebra-trade'; +import { BerachainTestnetAlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/models/berachain-testnet-algebra-route'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraTradeStructOmitPath } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UniswapV3AlgebraAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider'; + +import { defaultBerachainTestnetProviderConfiguration } from '../default-constants'; +import { BERACHAIN_TESTNET_ALGEBRA_PROVIDER_CONFIGURATION } from './constants/provider-configuration'; +import { + BERACHAIN_TESTNET_ALGEBRA_ROUTER_CONTRACT_ABI, + BERACHAIN_TESTNET_ALGEBRA_ROUTER_CONTRACT_ADDRESS +} from './constants/swap-router-contract-data'; +import { + BERACHAIN_TESTNET_ALGEBRA_QUOTER_CONTRACT_ABI, + BERACHAIN_TESTNET_ALGEBRA_QUOTER_CONTRACT_ADDRESS +} from './utils/quoter-controller/constants/quoter-contract-data'; + +export class BerachainTestnetAlgebraProvider extends UniswapV3AlgebraAbstractProvider { + protected readonly contractAddress = BERACHAIN_TESTNET_ALGEBRA_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = BERACHAIN_TESTNET_ALGEBRA_ROUTER_CONTRACT_ABI; + + public readonly blockchain = BLOCKCHAIN_NAME.BERACHAIN; + + protected readonly OnChainTradeClass = BerachainTestnetAlgebraTrade; + + protected readonly providerConfiguration = BERACHAIN_TESTNET_ALGEBRA_PROVIDER_CONFIGURATION; + + protected readonly quoterController = new AlgebraQuoterController( + this.blockchain, + defaultBerachainTestnetProviderConfiguration.routingProvidersAddresses, + BERACHAIN_TESTNET_ALGEBRA_QUOTER_CONTRACT_ADDRESS, + BERACHAIN_TESTNET_ALGEBRA_QUOTER_CONTRACT_ABI + ); + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ALGEBRA_INTEGRAL; + } + + protected createTradeInstance( + tradeStruct: UniswapV3AlgebraTradeStructOmitPath, + route: BerachainTestnetAlgebraRoute, + providerAddress: string + ): BerachainTestnetAlgebraTrade { + const path = createTokenNativeAddressProxyInPathStartAndEnd( + route.path, + EvmWeb3Pure.nativeTokenAddress + ); + return new BerachainTestnetAlgebraTrade( + { + ...tradeStruct, + path, + route + }, + providerAddress + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/berachain-testnet-algebra-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/berachain-testnet-algebra-trade.ts new file mode 100644 index 0000000..17e91ec --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/berachain-testnet-algebra-trade.ts @@ -0,0 +1,78 @@ +import { Token } from 'src/common/tokens'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { BerachainTestnetAlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/models/berachain-testnet-algebra-route'; +import { BerachainTestnetAlgebraTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/models/berachain-testnet-algebra-trade-struct'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade'; + +import { + BERACHAIN_TESTNET_ALGEBRA_ROUTER_CONTRACT_ABI, + BERACHAIN_TESTNET_ALGEBRA_ROUTER_CONTRACT_ADDRESS +} from './constants/swap-router-contract-data'; + +export class BerachainTestnetAlgebraTrade extends UniswapV3AlgebraAbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ALGEBRA; + } + + public readonly dexContractAddress = BERACHAIN_TESTNET_ALGEBRA_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = BERACHAIN_TESTNET_ALGEBRA_ROUTER_CONTRACT_ABI; + + protected readonly unwrapWethMethodName = 'unwrapWNativeToken'; + + private readonly route: BerachainTestnetAlgebraRoute; + + public readonly wrappedPath: ReadonlyArray; + + constructor(tradeStruct: BerachainTestnetAlgebraTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this.route = tradeStruct.route; + + this.wrappedPath = this.route.path; + } + + /** + * Returns swap `exactInput` method's name and arguments to use in Swap contract. + */ + protected getSwapRouterExactInputMethodData(walletAddress: string): MethodData { + const amountParams = this.getAmountParams(); + + if (this.route.path.length === 2 && this.route?.path?.[0] && this.route?.path?.[1]) { + const methodName = this.exact === 'input' ? 'exactInputSingle' : 'exactOutputSingle'; + + return { + methodName, + methodArguments: [ + [ + this.route.path[0].address, + this.route.path[1].address, + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams, + 0 + ] + ] + }; + } + + const methodName = this.exact === 'input' ? 'exactInput' : 'exactOutput'; + + return { + methodName, + methodArguments: [ + [ + AlgebraQuoterController.getEncodedPath(this.route.path), + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams + ] + ] + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/constants/provider-configuration.ts new file mode 100644 index 0000000..1d763de --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/constants/provider-configuration.ts @@ -0,0 +1,9 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const BERACHAIN_TESTNET_ALGEBRA_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = + { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.BERACHAIN]!.address, + maxTransitTokens: 1 + }; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/constants/swap-router-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/constants/swap-router-contract-data.ts new file mode 100644 index 0000000..fab06d9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/constants/swap-router-contract-data.ts @@ -0,0 +1,109 @@ +import { AbiItem } from 'web3-utils'; + +export const BERACHAIN_TESTNET_ALGEBRA_ROUTER_CONTRACT_ADDRESS = + '0x1A69E9B9F43602F1f9a3C98874732b18B09B526D'; + +export const BERACHAIN_TESTNET_ALGEBRA_ROUTER_CONTRACT_ABI = [ + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactInputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactInputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactOutputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactOutputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' }], + name: 'multicall', + outputs: [{ internalType: 'bytes[]', name: 'results', type: 'bytes[]' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountMinimum', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'unwrapWNativeToken', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { stateMutability: 'payable', type: 'receive' } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/models/berachain-testnet-algebra-route.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/models/berachain-testnet-algebra-route.ts new file mode 100644 index 0000000..c8da496 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/models/berachain-testnet-algebra-route.ts @@ -0,0 +1,9 @@ +import { Token } from 'src/common/tokens'; +import { UniswapV3AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route'; + +export interface BerachainTestnetAlgebraRoute extends UniswapV3AlgebraRoute { + /** + * List of pools' contract addresses to use in a trade's route. + */ + path: Token[]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/models/berachain-testnet-algebra-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/models/berachain-testnet-algebra-trade-struct.ts new file mode 100644 index 0000000..f046127 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/models/berachain-testnet-algebra-trade-struct.ts @@ -0,0 +1,6 @@ +import { BerachainTestnetAlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/models/berachain-testnet-algebra-route'; +import { UniswapV3AlgebraTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; + +export interface BerachainTestnetAlgebraTradeStruct extends UniswapV3AlgebraTradeStruct { + route: BerachainTestnetAlgebraRoute; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/utils/quoter-controller/constants/quoter-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/utils/quoter-controller/constants/quoter-contract-data.ts new file mode 100644 index 0000000..dcf1981 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/utils/quoter-controller/constants/quoter-contract-data.ts @@ -0,0 +1,51 @@ +import { AbiItem } from 'web3-utils'; + +export const BERACHAIN_TESTNET_ALGEBRA_QUOTER_CONTRACT_ADDRESS = + '0x298603815c244835CBFB4E005B0278e8C87a3C66'; + +export const BERACHAIN_TESTNET_ALGEBRA_QUOTER_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' } + ], + name: 'quoteExactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' } + ], + name: 'quoteExactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/default-constants.ts new file mode 100644 index 0000000..1213e47 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/default-constants.ts @@ -0,0 +1,18 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultBerachainTestnetRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.BERACHAIN]!.address, // WBERA + '0x7eeca4205ff31f947edbd49195a7a88e6a91161b', // HONEY + '0x6581e59a1c8da66ed0d313a0d4029dce2f746cc5' // USDC +]; + +const defaultBerachainTestnetWethAddress = + wrappedNativeTokensList[BLOCKCHAIN_NAME.BERACHAIN]!.address; + +export const defaultBerachainTestnetProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 1, + routingProvidersAddresses: defaultBerachainTestnetRoutingProvidersAddresses, + wethAddress: defaultBerachainTestnetWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/default-constants.ts new file mode 100644 index 0000000..bb3aa72 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/default-constants.ts @@ -0,0 +1,18 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultBlastRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.BLAST]!.address, // WETH + '0xeb466342c4d449bc9f53a865d5cb90586f405215', // axlUSDC + '0x1a35ee4640b0a3b87705b0a4b45d227ba60ca2ad', // axlWBTC + '0xF7bc58b8D8f97ADC129cfC4c9f45Ce3C0E1D2692' // WBTC +]; + +const defaultBlastWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.BLAST]!.address; + +export const defaultBlastProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultBlastRoutingProvidersAddresses, + wethAddress: defaultBlastWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/blast-fenix-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/blast-fenix-provider.ts new file mode 100644 index 0000000..aa4e73d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/blast-fenix-provider.ts @@ -0,0 +1,65 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { createTokenNativeAddressProxyInPathStartAndEnd } from 'src/features/common/utils/token-native-address-proxy'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { BlastFenixTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/blast-fenix-trade'; +import { BlastFenixRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/models/blast-fenix-route'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraTradeStructOmitPath } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UniswapV3AlgebraAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider'; + +import { defaultBlastProviderConfiguration } from '../default-constants'; +import { BLAST_FENIX_PROVIDER_CONFIGURATION } from './constants/provider-configuration'; +import { + BLAST_FENIX_ROUTER_CONTRACT_ABI, + BLAST_FENIX_ROUTER_CONTRACT_ADDRESS +} from './constants/swap-router-contract-data'; +import { + BLAST_FENIX_QUOTER_CONTRACT_ABI, + BLAST_FENIX_QUOTER_CONTRACT_ADDRESS +} from './utils/quoter-controller/constants/quoter-contract-data'; + +export class BlastFenixProvider extends UniswapV3AlgebraAbstractProvider { + protected readonly contractAddress = BLAST_FENIX_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = BLAST_FENIX_ROUTER_CONTRACT_ABI; + + public readonly blockchain = BLOCKCHAIN_NAME.BLAST; + + protected readonly OnChainTradeClass = BlastFenixTrade; + + protected readonly providerConfiguration = BLAST_FENIX_PROVIDER_CONFIGURATION; + + protected readonly quoterController = new AlgebraQuoterController( + this.blockchain, + defaultBlastProviderConfiguration.routingProvidersAddresses, + BLAST_FENIX_QUOTER_CONTRACT_ADDRESS, + BLAST_FENIX_QUOTER_CONTRACT_ABI + ); + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.FENIX_V3; + } + + protected createTradeInstance( + tradeStruct: UniswapV3AlgebraTradeStructOmitPath, + route: BlastFenixRoute, + providerAddress: string + ): BlastFenixTrade { + const path = createTokenNativeAddressProxyInPathStartAndEnd( + route.path, + EvmWeb3Pure.nativeTokenAddress + ); + return new BlastFenixTrade( + { + ...tradeStruct, + path, + route + }, + providerAddress + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/blast-fenix-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/blast-fenix-trade.ts new file mode 100644 index 0000000..b327388 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/blast-fenix-trade.ts @@ -0,0 +1,78 @@ +import { Token } from 'src/common/tokens'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { BlastFenixRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/models/blast-fenix-route'; +import { BlastFenixTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/models/blast-fenix-trade-struct'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade'; + +import { + BLAST_FENIX_ROUTER_CONTRACT_ABI, + BLAST_FENIX_ROUTER_CONTRACT_ADDRESS +} from './constants/swap-router-contract-data'; + +export class BlastFenixTrade extends UniswapV3AlgebraAbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.FENIX_V3; + } + + public readonly dexContractAddress = BLAST_FENIX_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = BLAST_FENIX_ROUTER_CONTRACT_ABI; + + protected readonly unwrapWethMethodName = 'unwrapWNativeToken'; + + private readonly route: BlastFenixRoute; + + public readonly wrappedPath: ReadonlyArray; + + constructor(tradeStruct: BlastFenixTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this.route = tradeStruct.route; + + this.wrappedPath = this.route.path; + } + + /** + * Returns swap `exactInput` method's name and arguments to use in Swap contract. + */ + protected getSwapRouterExactInputMethodData(walletAddress: string): MethodData { + const amountParams = this.getAmountParams(); + + if (this.route.path.length === 2 && this.route?.path?.[0] && this.route?.path?.[1]) { + const methodName = this.exact === 'input' ? 'exactInputSingle' : 'exactOutputSingle'; + + return { + methodName, + methodArguments: [ + [ + this.route.path[0].address, + this.route.path[1].address, + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams, + 0 + ] + ] + }; + } + + const methodName = this.exact === 'input' ? 'exactInput' : 'exactOutput'; + + return { + methodName, + methodArguments: [ + [ + AlgebraQuoterController.getEncodedPath(this.route.path), + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams + ] + ] + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/constants/provider-configuration.ts new file mode 100644 index 0000000..665e688 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/constants/provider-configuration.ts @@ -0,0 +1,8 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const BLAST_FENIX_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.BLAST]!.address, + maxTransitTokens: 1 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/constants/swap-router-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/constants/swap-router-contract-data.ts new file mode 100644 index 0000000..12832f2 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/constants/swap-router-contract-data.ts @@ -0,0 +1,108 @@ +import { AbiItem } from 'web3-utils'; + +export const BLAST_FENIX_ROUTER_CONTRACT_ADDRESS = '0x2df37Cb897fdffc6B4b03d8252d85BE7C6dA9d00'; + +export const BLAST_FENIX_ROUTER_CONTRACT_ABI = [ + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactInputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactInputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactOutputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactOutputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' }], + name: 'multicall', + outputs: [{ internalType: 'bytes[]', name: 'results', type: 'bytes[]' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountMinimum', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'unwrapWNativeToken', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { stateMutability: 'payable', type: 'receive' } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/models/blast-fenix-route.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/models/blast-fenix-route.ts new file mode 100644 index 0000000..a2049fa --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/models/blast-fenix-route.ts @@ -0,0 +1,9 @@ +import { Token } from 'src/common/tokens'; +import { UniswapV3AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route'; + +export interface BlastFenixRoute extends UniswapV3AlgebraRoute { + /** + * List of pools' contract addresses to use in a trade's route. + */ + path: Token[]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/models/blast-fenix-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/models/blast-fenix-trade-struct.ts new file mode 100644 index 0000000..3c10e71 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/models/blast-fenix-trade-struct.ts @@ -0,0 +1,6 @@ +import { BlastFenixRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/models/blast-fenix-route'; +import { UniswapV3AlgebraTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; + +export interface BlastFenixTradeStruct extends UniswapV3AlgebraTradeStruct { + route: BlastFenixRoute; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/utils/quoter-controller/constants/quoter-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/utils/quoter-controller/constants/quoter-contract-data.ts new file mode 100644 index 0000000..28ee80c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/utils/quoter-controller/constants/quoter-contract-data.ts @@ -0,0 +1,62 @@ +import { AbiItem } from 'web3-utils'; + +export const BLAST_FENIX_QUOTER_CONTRACT_ADDRESS = '0x79F92b0b4ca9aDA848E21Cd1460b12286141cc25'; + +export const BLAST_FENIX_QUOTER_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' } + ], + name: 'quoteExactInput', + outputs: [ + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint16[]', name: 'fees', type: 'uint16[]' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactInputSingle', + outputs: [ + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint16', name: 'fee', type: 'uint16' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' } + ], + name: 'quoteExactOutput', + outputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint16[]', name: 'fees', type: 'uint16[]' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactOutputSingle', + outputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint16', name: 'fee', type: 'uint16' } + ], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/izumi-blast/izumi-blast-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/izumi-blast/izumi-blast-provider.ts new file mode 100644 index 0000000..9caf374 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/izumi-blast/izumi-blast-provider.ts @@ -0,0 +1,22 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiBlastProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.BLAST; + + protected readonly dexAddress = '0xA3F50FeBA40dd3E884688C0AF72C4054D07a1c50'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0xd413b415Bf8449D6DB8238826579647bfDb60a9f', + liquidityManagerAddress: '0x5e7902aDf0Ea0ff827683Cc1d431F740CAD0731b', + routingTokenAddresses: [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.BLAST]!.address, // WETH + '0x4300000000000000000000000000000000000003', // USDB + '0x0A3BB08b3a15A19b4De82F8AcFc862606FB69A2D' // iUSD + ], + multicallAddress: '0x1DADF066518E2b7064D85cED45625BFeC52ca07d', + supportedFees: [500, 3000, 10_000] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/uni-swap-v2-blast/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/uni-swap-v2-blast/constants.ts new file mode 100644 index 0000000..3ad99ae --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/uni-swap-v2-blast/constants.ts @@ -0,0 +1,9 @@ +import { defaultBlastProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/blast/default-constants'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +export const UNISWAP_V2_BLAST_CONTRACT_ADDRESS = '0x0998bEc51D95EAa75Ffdf465D5deD16aEd2ba2fe'; + +export const UNISWAP_V2_BLAST_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultBlastProviderConfiguration, + maxTransitTokens: 1 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/uni-swap-v2-blast/uni-swap-v2-blast-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/uni-swap-v2-blast/uni-swap-v2-blast-provider.ts new file mode 100644 index 0000000..4eb5449 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/uni-swap-v2-blast/uni-swap-v2-blast-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UNISWAP_V2_BLAST_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/blast/uni-swap-v2-blast/constants'; +import { UniSwapV2BlastTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/blast/uni-swap-v2-blast/uni-swap-v2-blast-trade'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class UniSwapV2BlastProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.BLAST; + + public readonly UniswapV2TradeClass = UniSwapV2BlastTrade; + + public readonly providerSettings = UNISWAP_V2_BLAST_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/uni-swap-v2-blast/uni-swap-v2-blast-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/uni-swap-v2-blast/uni-swap-v2-blast-trade.ts new file mode 100644 index 0000000..e35742f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/blast/uni-swap-v2-blast/uni-swap-v2-blast-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UNISWAP_V2_BLAST_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/blast/uni-swap-v2-blast/constants'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class UniSwapV2BlastTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.FENIX_V2; + } + + public readonly dexContractAddress = UNISWAP_V2_BLAST_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/boba/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/boba/default-constants.ts new file mode 100644 index 0000000..e17974c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/boba/default-constants.ts @@ -0,0 +1,19 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultBobaRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.BOBA]!.address, // WETH + '0xa18bf3994c0cc6e3b63ac420308e5383f53120d7', // BOBA + '0x66a2a913e447d6b4bf33efbec43aaef87890fbbc', // USDC + '0x5de1677344d3cb0d7d465c10b72a8f60699c062d', // USDT + '0xf74195bb8a5cf652411867c5c2c5b8c2a402be35' // DAI +]; + +const defaultBobaWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.BOBA]!.address; + +export const defaultBobaProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultBobaRoutingProvidersAddresses, + wethAddress: defaultBobaWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/boba/oolong-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/boba/oolong-swap/constants.ts new file mode 100644 index 0000000..328f30a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/boba/oolong-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultBobaProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/boba/default-constants'; + +export const OOLONG_SWAP_CONTRACT_ADDRESS = '0x17C83E2B96ACfb5190d63F5E46d93c107eC0b514'; + +export const OOLONG_SWAP_PROVIDER_CONFIGURATION = defaultBobaProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/boba/oolong-swap/oolong-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/boba/oolong-swap/oolong-swap-provider.ts new file mode 100644 index 0000000..df89860 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/boba/oolong-swap/oolong-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { OOLONG_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/boba/oolong-swap/constants'; +import { OolongSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/boba/oolong-swap/oolong-swap-trade'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class OolongSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.BOBA; + + public readonly UniswapV2TradeClass = OolongSwapTrade; + + public readonly providerSettings = OOLONG_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/boba/oolong-swap/oolong-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/boba/oolong-swap/oolong-swap-trade.ts new file mode 100644 index 0000000..b3c5e77 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/boba/oolong-swap/oolong-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { OOLONG_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/boba/oolong-swap/constants'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class OolongSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.OOLONG_SWAP; + } + + public readonly dexContractAddress = OOLONG_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/default-constants.ts new file mode 100644 index 0000000..efecc83 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/default-constants.ts @@ -0,0 +1,22 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultBscRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]!.address, // WBNB + '0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82', // CAKE + '0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD + '0x55d398326f99059fF775485246999027B3197955', // USDT + '0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c', // BTCB + '0x23396cF899Ca06c4472205fC903bDB4de249D6fC', // UST + '0x2170Ed0880ac9A755fd29B2688956BD959F933F8', // ETH + '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d' // USDC +]; + +const defaultBscWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]!.address; + +export const defaultBscProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 3, + routingProvidersAddresses: defaultBscRoutingProvidersAddresses, + wethAddress: defaultBscWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/izumi-bsc/izumi-bsc-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/izumi-bsc/izumi-bsc-provider.ts new file mode 100644 index 0000000..0e65b3c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/izumi-bsc/izumi-bsc-provider.ts @@ -0,0 +1,22 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiBscProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN; + + protected readonly dexAddress = '0xedf2021f41AbCfE2dEA4427E1B61f4d0AA5aA4b8'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0xDCe9a4ACC59E69ECcC0cdA2E82fe601fdB726542', + liquidityManagerAddress: '0xBF55ef05412f1528DbD96ED9E7181f87d8C9F453', + routingTokenAddresses: [ + '0x55d398326f99059ff775485246999027b3197955', // USDT + wrappedNativeTokensList[BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]!.address, // WBNB + '0xe9e7cea3dedca5984780bafc599bd69add087d56' // BUSD + ], + multicallAddress: '0xA1189a420662105bef5Be444B8b1E0a7D8279672', + supportedFees: [2000, 400] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-router-bsc/pancake-router-bsc-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-router-bsc/pancake-router-bsc-provider.ts new file mode 100644 index 0000000..19069c7 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-router-bsc/pancake-router-bsc-provider.ts @@ -0,0 +1,20 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { PancakeRouterProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/pancake-router-provider'; +import { bsc } from 'viem/chains'; + +export class PancakeRouterBscProvider extends PancakeRouterProvider { + public readonly blockchain = BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN; + + protected readonly chain = bsc; + + protected readonly dexAddress = '0x13f4EA83D0bd40E75C8222255bc855a974568Dd4'; + + protected readonly v3subgraphAddress = + 'https://api.thegraph.com/subgraphs/name/pancakeswap/exchange-v3-bsc'; + + protected readonly v2subgraphAddress = 'https://proxy-worker-api.pancakeswap.com/bsc-exchange'; + + protected readonly maxHops = 2; + + protected readonly maxSplits = 4; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/constants.ts new file mode 100644 index 0000000..ac287cd --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultBscProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/default-constants'; + +export const PANCAKE_SWAP_CONTRACT_ADDRESS = '0x10ED43C718714eb63d5aA57B78B54704E256024E'; + +export const PANCAKE_SWAP_PROVIDER_CONFIGURATION = defaultBscProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/pancake-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/pancake-swap-provider.ts new file mode 100644 index 0000000..1905d5d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/pancake-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { PANCAKE_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/constants'; +import { PancakeSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/pancake-swap-trade'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class PancakeSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN; + + public readonly UniswapV2TradeClass = PancakeSwapTrade; + + public readonly providerSettings = PANCAKE_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/pancake-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/pancake-swap-trade.ts new file mode 100644 index 0000000..da04534 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/pancake-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { PANCAKE_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/pancake-swap/constants'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class PancakeSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.PANCAKE_SWAP; + } + + public readonly dexContractAddress = PANCAKE_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/constants.ts new file mode 100644 index 0000000..29a0f2a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/constants.ts @@ -0,0 +1,5 @@ +import { defaultBscProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/default-constants'; + +export const SUSHI_SWAP_BSC_CONTRACT_ADDRESS = '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'; + +export const SUSHI_SWAP_BSC_PROVIDER_CONFIGURATION = defaultBscProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/sushi-swap-bsc-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/sushi-swap-bsc-provider.ts new file mode 100644 index 0000000..2ea28fc --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/sushi-swap-bsc-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { SUSHI_SWAP_BSC_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/constants'; +import { SushiSwapBscTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/sushi-swap-bsc-trade'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class SushiSwapBscProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN; + + public readonly UniswapV2TradeClass = SushiSwapBscTrade; + + public readonly providerSettings = SUSHI_SWAP_BSC_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/sushi-swap-bsc-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/sushi-swap-bsc-trade.ts new file mode 100644 index 0000000..cd63870 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/sushi-swap-bsc-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { SUSHI_SWAP_BSC_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/bsc/sushi-swap-bsc/constants'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class SushiSwapBscTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SUSHI_SWAP; + } + + public readonly dexContractAddress = SUSHI_SWAP_BSC_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/default-constants.ts new file mode 100644 index 0000000..14d7eb6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/default-constants.ts @@ -0,0 +1,19 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultBscWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]!.address; + +const defaultBscRoutingProvidersAddresses = [ + defaultBscWethAddress, // WBNB + '0x855fC87f7F14Db747ef27603b02bAe579ba947B6', // USDC + '0x7d43AABC515C356145049227CeE54B608342c0ad', // USDT + '0xC826C23327098cd8A37f140114F2173A8F62DD29', // WUSDT + '0x9a01bf917477dd9f5d715d188618fc8b7350cd22' // BUSD +]; + +export const defaultBscTestnetProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 3, + routingProvidersAddresses: defaultBscRoutingProvidersAddresses, + wethAddress: defaultBscWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/pancake-swap-testnet/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/pancake-swap-testnet/constants.ts new file mode 100644 index 0000000..28536b8 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/pancake-swap-testnet/constants.ts @@ -0,0 +1,5 @@ +import { defaultBscTestnetProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/bsct/default-constants'; + +export const PANCAKE_SWAP_TESTNET_CONTRACT_ADDRESS = '0x10ED43C718714eb63d5aA57B78B54704E256024E'; + +export const PANCAKE_SWAP_TESTNET_PROVIDER_CONFIGURATION = defaultBscTestnetProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/pancake-swap-testnet/pancake-swap-testnet-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/pancake-swap-testnet/pancake-swap-testnet-provider.ts new file mode 100644 index 0000000..e1812d9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/pancake-swap-testnet/pancake-swap-testnet-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { PANCAKE_SWAP_TESTNET_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/bsct/pancake-swap-testnet/constants'; +import { PancakeSwapTestnetTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/bsct/pancake-swap-testnet/pancake-swap-testnet-trade'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class PancakeSwapTestnetProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN_TESTNET; + + public readonly UniswapV2TradeClass = PancakeSwapTestnetTrade; + + public readonly providerSettings = PANCAKE_SWAP_TESTNET_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/pancake-swap-testnet/pancake-swap-testnet-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/pancake-swap-testnet/pancake-swap-testnet-trade.ts new file mode 100644 index 0000000..55bdd95 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/pancake-swap-testnet/pancake-swap-testnet-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { PANCAKE_SWAP_TESTNET_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/bsct/pancake-swap-testnet/constants'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class PancakeSwapTestnetTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.PANCAKE_SWAP; + } + + public readonly dexContractAddress = PANCAKE_SWAP_TESTNET_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/trader-joe-bsct/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/trader-joe-bsct/constants.ts new file mode 100644 index 0000000..7bdd890 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/trader-joe-bsct/constants.ts @@ -0,0 +1,5 @@ +import { defaultBscTestnetProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/bsct/default-constants'; + +export const TRADER_JOE_BSCT_CONTRACT_ADDRESS = '0xf7C6d73336f333b63144644944176072D94128F5'; + +export const TRADER_JOE_BSCT_PROVIDER_CONFIGURATION = defaultBscTestnetProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/trader-joe-bsct/trader-joe-bsct-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/trader-joe-bsct/trader-joe-bsct-provider.ts new file mode 100644 index 0000000..5d8c95e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/trader-joe-bsct/trader-joe-bsct-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { TRADER_JOE_BSCT_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/bsct/trader-joe-bsct/constants'; +import { TraderJoeBsctTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/bsct/trader-joe-bsct/trader-joe-bsct-trade'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; + +export class TraderJoeBsctProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN_TESTNET; + + public readonly UniswapV2TradeClass = TraderJoeBsctTrade; + + public readonly providerSettings = TRADER_JOE_BSCT_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/trader-joe-bsct/trader-joe-bsct-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/trader-joe-bsct/trader-joe-bsct-trade.ts new file mode 100644 index 0000000..5d0e4c8 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/bsct/trader-joe-bsct/trader-joe-bsct-trade.ts @@ -0,0 +1,20 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AVAX_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/avax-abi'; +import { AVALANCHE_SWAP_METHOD } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/swap-methods'; +import { TRADER_JOE_BSCT_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/bsct/trader-joe-bsct/constants'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export class TraderJoeBsctTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.PANCAKE_SWAP; + } + + public readonly dexContractAddress = TRADER_JOE_BSCT_CONTRACT_ADDRESS; + + public static readonly contractAbi = AVAX_ABI; + + public static readonly swapMethods = AVALANCHE_SWAP_METHOD; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/celo/curve-celo/curve-celo-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/celo/curve-celo/curve-celo-provider.ts new file mode 100644 index 0000000..d15d17d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/celo/curve-celo/curve-celo-provider.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { CurveCeloTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/celo/curve-celo/curve-celo-trade'; +import { CurveAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider'; + +export class CurveCeloProvider extends CurveAbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.CELO; + + public readonly Trade = CurveCeloTrade; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/celo/curve-celo/curve-celo-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/celo/curve-celo/curve-celo-trade.ts new file mode 100644 index 0000000..5f3dbef --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/celo/curve-celo/curve-celo-trade.ts @@ -0,0 +1,3 @@ +import { CurveAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade'; + +export class CurveCeloTrade extends CurveAbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller.ts new file mode 100644 index 0000000..3717a89 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller.ts @@ -0,0 +1,179 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, Token } from 'src/common/tokens'; +import { notNull } from 'src/common/utils/object'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { ContractMulticallResponse } from 'src/core/blockchain/web3-public-service/web3-public/models/contract-multicall-response'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { Injector } from 'src/core/injector/injector'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { + UNISWAP_V3_QUOTER_CONTRACT_ABI, + UNISWAP_V3_QUOTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/constants/quoter-contract-data'; +import { UniswapV3AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-quoter-controller'; +import { AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/models/algebra-route'; +import { AbiItem } from 'web3-utils'; + +interface GetQuoterMethodsDataOptions { + routesTokens: Token[]; + to: Token; + exact: Exact; + weiAmount: string; + maxTransitTokens: number; +} + +/** + * Works with requests, related to Uniswap v3 liquidity pools. + */ +export class AlgebraQuoterController extends UniswapV3AlgebraQuoterController { + protected routerTokens: Token[] | undefined; + + /** + * Converts algebra route to encoded bytes string to pass it to contract. + * Structure of encoded string: '0x${tokenAddress_0}${tokenAddress_1}...${tokenAddress_n}. + * @param path Symbol tokens, included in route. + * @returns string Encoded string. + */ + public static getEncodedPath(path: Token[]): string { + const encodedPath = path.reduce( + (accEncodedPath, token) => accEncodedPath + token.address.slice(2).toLowerCase(), + '' + ); + return `0x${encodedPath}`; + } + + /** + * Returns swap method's name and arguments to pass it to Quoter contract. + * @param path Pools, included in route. + * @param exact Is exact input or output trade. + * @param weiAmount Amount of tokens to trade. + */ + private static getQuoterMethodData( + path: Token[], + exact: Exact, + weiAmount: string + ): { + path: Token[]; + methodData: MethodData; + } { + if (path.length === 2 && path?.[0] && path?.[1]) { + const methodName = + exact === 'input' ? 'quoteExactInputSingle' : 'quoteExactOutputSingle'; + const limitSqrtPrice = 0; + return { + path, + methodData: { + methodName, + methodArguments: [path[0].address, path[1].address, weiAmount, limitSqrtPrice] + } + }; + } + + const methodName = exact === 'input' ? 'quoteExactInput' : 'quoteExactOutput'; + const tokensPath = exact === 'input' ? path : path.reverse(); + return { + path, + methodData: { + methodName, + methodArguments: [AlgebraQuoterController.getEncodedPath(tokensPath), weiAmount] + } + }; + } + + protected get web3Public(): EvmWeb3Public { + return Injector.web3PublicService.getWeb3Public(this.blockchain); + } + + constructor( + protected readonly blockchain: EvmBlockchainName, + protected readonly routingTokensAddresses: string[], + protected readonly quoterContractAddress: string = UNISWAP_V3_QUOTER_CONTRACT_ADDRESS, + protected readonly quoterContractABI: AbiItem[] = UNISWAP_V3_QUOTER_CONTRACT_ABI + ) { + super(); + } + + protected async getOrCreateRouterTokens(): Promise { + if (!this.routerTokens) { + this.routerTokens = await Token.createTokens( + this.routingTokensAddresses, + this.blockchain + ); + } + + return this.routerTokens; + } + + public async getAllRoutes( + from: PriceToken, + to: PriceToken, + exact: Exact, + weiAmount: string, + routeMaxTransitTokens: number + ): Promise { + const routesTokens = (await this.getOrCreateRouterTokens()).filter( + token => !token.isEqualToTokens([from, to]) + ); + + const options: Omit = { + routesTokens, + to, + exact, + weiAmount + }; + const quoterMethodsData = [...Array(routeMaxTransitTokens + 1)] + .map((_, maxTransitTokens) => + this.getQuoterMethodsData( + { + ...options, + maxTransitTokens + }, + [from] + ) + ) + .flat(); + + const results = await this.web3Public.multicallContractMethods( + this.quoterContractAddress, + this.quoterContractABI, + quoterMethodsData.map(quoterMethodData => quoterMethodData.methodData) + ); + + return results + .map((result: ContractMulticallResponse, index: number) => { + if (result.success) { + const quoter = quoterMethodsData?.[index]; + if (!quoter) { + throw new RubicSdkError('Quoter has to be defined'); + } + return { + outputAbsoluteAmount: new BigNumber(result.output!), + path: quoter.path + }; + } + return null; + }) + .filter(notNull); + } + + /** + * Returns swap methods' names and arguments, built with passed pools' addresses, to use it in Quoter contract. + */ + protected getQuoterMethodsData( + options: GetQuoterMethodsDataOptions, + path: Token[] + ): { path: Token[]; methodData: MethodData }[] { + const { routesTokens, to, exact, weiAmount, maxTransitTokens } = options; + + if (path.length === maxTransitTokens + 1) { + return [AlgebraQuoterController.getQuoterMethodData(path.concat(to), exact, weiAmount)]; + } + + return routesTokens + .filter(token => !path.includes(token)) + .map(token => this.getQuoterMethodsData(options, path.concat(token))) + .flat(); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/constants/address-provider-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/constants/address-provider-abi.ts new file mode 100644 index 0000000..31dac4a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/constants/address-provider-abi.ts @@ -0,0 +1,12 @@ +import { AbiItem } from 'web3-utils'; + +export const addressProviderAbi: AbiItem[] = [ + { + name: 'get_address', + outputs: [{ type: 'address', name: '' }], + inputs: [{ type: 'uint256', name: '_id' }], + stateMutability: 'view', + type: 'function', + gas: 1308 + } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/constants/registry-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/constants/registry-abi.ts new file mode 100644 index 0000000..1b45cb2 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/constants/registry-abi.ts @@ -0,0 +1,14 @@ +import { AbiItem } from 'web3-utils'; + +export const registryAbi: AbiItem[] = [ + { + stateMutability: 'view', + type: 'function', + name: 'find_pool_for_coins', + inputs: [ + { name: '_from', type: 'address' }, + { name: '_to', type: 'address' } + ], + outputs: [{ name: '', type: 'address' }] + } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/constants/registry-exchange-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/constants/registry-exchange-abi.ts new file mode 100644 index 0000000..d5c3498 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/constants/registry-exchange-abi.ts @@ -0,0 +1,57 @@ +import { AbiItem } from 'web3-utils'; + +export const registryExchangeAbi: AbiItem[] = [ + { + stateMutability: 'payable', + type: 'function', + name: 'exchange', + inputs: [ + { name: '_pool', type: 'address' }, + { name: '_from', type: 'address' }, + { name: '_to', type: 'address' }, + { name: '_amount', type: 'uint256' }, + { name: '_expected', type: 'uint256' } + ], + outputs: [{ name: '', type: 'uint256' }] + }, + { + stateMutability: 'payable', + type: 'function', + name: 'exchange', + inputs: [ + { name: '_pool', type: 'address' }, + { name: '_from', type: 'address' }, + { name: '_to', type: 'address' }, + { name: '_amount', type: 'uint256' }, + { name: '_expected', type: 'uint256' }, + { name: '_receiver', type: 'address' } + ], + outputs: [{ name: '', type: 'uint256' }] + }, + { + stateMutability: 'view', + type: 'function', + name: 'get_best_rate', + inputs: [ + { name: '_from', type: 'address' }, + { name: '_to', type: 'address' }, + { name: '_amount', type: 'uint256' } + ], + outputs: [ + { name: '', type: 'address' }, + { name: '', type: 'uint256' } + ] + }, + { + stateMutability: 'view', + type: 'function', + name: 'get_exchange_amount', + inputs: [ + { name: '_pool', type: 'address' }, + { name: '_from', type: 'address' }, + { name: '_to', type: 'address' }, + { name: '_amount', type: 'uint256' } + ], + outputs: [{ name: '', type: 'uint256' }] + } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider.ts new file mode 100644 index 0000000..ddddae0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider.ts @@ -0,0 +1,200 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { combineOptions } from 'src/common/utils/options'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { + OnChainCalculationOptions, + RequiredOnChainCalculationOptions +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { getGasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info'; +import { getGasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-price-info'; +import { addressProviderAbi } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/constants/address-provider-abi'; +import { registryAbi } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/constants/registry-abi'; +import { registryExchangeAbi } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/constants/registry-exchange-abi'; +import { CurveAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade'; +import { CurveOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/models/curve-on-chain-trade-struct'; +import { CurveTradeClass } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/models/curve-trade-class'; +import { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options'; +import { EvmOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/evm-on-chain-provider'; + +export abstract class CurveAbstractProvider< + T extends CurveAbstractTrade = CurveAbstractTrade +> extends EvmOnChainProvider { + /** @internal */ + public abstract readonly Trade: CurveTradeClass; + + protected readonly addressProvider = '0x0000000022D53366457F9d5E68Ec105046FC4383'; + + public static readonly nativeAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; + + private readonly defaultOptions: RequiredOnChainCalculationOptions = { + ...evmProviderDefaultOptions, + deadlineMinutes: 20, + disableMultihops: false + }; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.CURVE; + } + + public async calculate( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options?: OnChainCalculationOptions + ): Promise { + const fromAddress = fromToken.isNative + ? CurveAbstractProvider.nativeAddress + : fromToken.address; + const toAddress = toToken.isNative ? CurveAbstractProvider.nativeAddress : toToken.address; + + const registryExchangeAddress = await this.fetchRegistryExchangeAddress(); + const registryAddress = await this.fetchRegistryAddress(); + let poolAddress = await this.fetchPoolAddress(fromAddress, toAddress, registryAddress); + + const fullOptions = combineOptions(options, this.defaultOptions); + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract( + fromToken, + fullOptions + ); + const fromAmount = fromWithoutFee.stringWeiAmount; + + if (compareAddresses(poolAddress, EvmWeb3Pure.EMPTY_ADDRESS)) { + const bestRate = await this.fetchBestRate( + fromAddress, + toAddress, + fromAmount, + registryExchangeAddress + ); + poolAddress = bestRate[0]!; + } + + if (compareAddresses(poolAddress, EvmWeb3Pure.EMPTY_ADDRESS)) { + throw new RubicSdkError('Token is not supported.'); + } + + const amountOut = await this.fetchExchangeAmount( + fromAddress, + toAddress, + fromAmount, + poolAddress, + registryExchangeAddress + ); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: new BigNumber(amountOut) + }); + + const tradeStruct: CurveOnChainTradeStruct = { + from: fromToken, + to, + slippageTolerance: fullOptions.slippageTolerance, + gasFeeInfo: null, + useProxy: fullOptions.useProxy, + proxyFeeInfo, + fromWithoutFee, + withDeflation: fullOptions.withDeflation, + path: [fromToken, toToken], + registryExchangeAddress, + poolAddress, + usedForCrossChain: fullOptions.usedForCrossChain + }; + + const trade = new this.Trade(tradeStruct, fullOptions.providerAddress); + + let gasFeeInfo = null; + const spenderAddress = options?.fromAddress || this.walletAddress; + if (spenderAddress) { + const params = await trade.encodeDirect({ + fromAddress: spenderAddress, + supportFee: false + }); + const gasPrice = await getGasPriceInfo(fromToken.blockchain); + + let gasLimit: null | BigNumber; + + try { + gasLimit = await this.web3Public.getEstimatedGasByData( + spenderAddress, + registryExchangeAddress, + { + data: params.data + } + ); + } catch { + gasLimit = new BigNumber(400_000); // Default gas limit + } + gasFeeInfo = getGasFeeInfo(gasLimit, gasPrice); + } + + return new this.Trade({ ...tradeStruct, gasFeeInfo }, fullOptions.providerAddress); + } + + private async fetchRegistryExchangeAddress(): Promise { + return this.web3Public.callContractMethod( + this.addressProvider, + addressProviderAbi, + 'get_address', + ['2'] + ); + } + + private async fetchRegistryAddress(): Promise { + return this.web3Public.callContractMethod( + this.addressProvider, + addressProviderAbi, + 'get_address', + ['0'] + ); + } + + private async fetchPoolAddress( + fromAddress: string, + toAddress: string, + registryAddress: string + ): Promise { + return this.web3Public.callContractMethod( + registryAddress, + registryAbi, + 'find_pool_for_coins', + [fromAddress, toAddress] + ); + } + + private async fetchBestRate( + fromAddress: string, + toAddress: string, + fromAmount: string, + registryExchangeAddress: string + ): Promise<[string, string]> { + return this.web3Public.callContractMethod( + registryExchangeAddress, + registryExchangeAbi, + 'get_best_rate', + [fromAddress, toAddress, fromAmount] + ); + } + + private async fetchExchangeAmount( + fromAddress: string, + toAddress: string, + fromAmount: string, + poolAddress: string, + registryExchangeAddress: string + ): Promise { + return this.web3Public.callContractMethod( + registryExchangeAddress, + registryExchangeAbi, + 'get_exchange_amount', + [poolAddress, fromAddress, toAddress, fromAmount] + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade.ts new file mode 100644 index 0000000..1931575 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade.ts @@ -0,0 +1,80 @@ +import { RubicSdkError } from 'src/common/errors'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { registryExchangeAbi } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/constants/registry-exchange-abi'; +import { CurveAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider'; +import { CurveOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/models/curve-on-chain-trade-struct'; + +export abstract class CurveAbstractTrade extends EvmOnChainTrade { + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.CURVE; + } + + private get nativeValueToSend(): string | undefined { + if (this.from.isNative) { + return this.from.stringWeiAmount; + } + return '0'; + } + + public readonly dexContractAddress: string; + + private readonly poolAddress: string; + + constructor(tradeStruct: CurveOnChainTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + this.dexContractAddress = tradeStruct.registryExchangeAddress; + this.poolAddress = tradeStruct.poolAddress; + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + await this.checkFromAddress(options.fromAddress, true); + await this.checkReceiverAddress(options.receiverAddress); + + if (options.supportFee === undefined) { + if (await this.needApprove(options.fromAddress)) { + throw new RubicSdkError( + 'To use `encode` function, token must be approved for wallet' + ); + } + + try { + await this.checkBalance(); + } catch (_err) { + throw new RubicSdkError( + 'To use `encode` function, wallet must have enough balance or you must provider `supportFee` parameter in options.' + ); + } + } + const gasParams = this.getGasParams(options); + const exchangeParams = [ + this.poolAddress, + this.from.isNative ? CurveAbstractProvider.nativeAddress : this.from.address, + this.to.isNative ? CurveAbstractProvider.nativeAddress : this.to.address, + this.fromWithoutFee.stringWeiAmount, + this.toTokenAmountMin.stringWeiAmount + ]; + if (options.receiverAddress) { + exchangeParams.push(options.receiverAddress); + } + + const config = EvmWeb3Pure.encodeMethodCall( + this.dexContractAddress, + registryExchangeAbi, + 'exchange', + exchangeParams, + this.nativeValueToSend, + gasParams + ); + + return { tx: config, toAmount: this.to.stringWeiAmount }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/models/curve-on-chain-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/models/curve-on-chain-trade-struct.ts new file mode 100644 index 0000000..c579cc0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/models/curve-on-chain-trade-struct.ts @@ -0,0 +1,6 @@ +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export interface CurveOnChainTradeStruct extends EvmOnChainTradeStruct { + poolAddress: string; + registryExchangeAddress: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/models/curve-trade-class.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/models/curve-trade-class.ts new file mode 100644 index 0000000..86cf583 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/models/curve-trade-class.ts @@ -0,0 +1,8 @@ +import { AbstractConstructorParameters, Constructor } from 'src/common/utils/types'; +import { CurveAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade'; + +export type CurveTradeClass = Constructor< + AbstractConstructorParameters, + T +> & + Omit; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/contracts/izumi-quoter-contract-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/contracts/izumi-quoter-contract-abi.ts new file mode 100644 index 0000000..2f979a2 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/contracts/izumi-quoter-contract-abi.ts @@ -0,0 +1,17 @@ +import { AbiItem } from 'web3-utils'; + +export const izumiQuoterContractAbi: AbiItem[] = [ + { + inputs: [ + { internalType: 'uint128', name: 'amount', type: 'uint128' }, + { internalType: 'bytes', name: 'path', type: 'bytes' } + ], + name: 'swapAmount', + outputs: [ + { internalType: 'uint256', name: 'acquire', type: 'uint256' }, + { internalType: 'int24[]', name: 'pointAfterList', type: 'int24[]' } + ], + stateMutability: 'nonpayable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/contracts/izumi-swap-contract-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/contracts/izumi-swap-contract-abi.ts new file mode 100644 index 0000000..0f0a80b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/contracts/izumi-swap-contract-abi.ts @@ -0,0 +1,209 @@ +import { AbiItem } from 'web3-utils'; + +export const izumiSwapContractAbi: AbiItem[] = [ + { + inputs: [{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' }], + name: 'multicall', + outputs: [{ internalType: 'bytes[]', name: 'results', type: 'bytes[]' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenX', type: 'address' }, + { internalType: 'address', name: 'tokenY', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' } + ], + name: 'pool', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { inputs: [], name: 'refundETH', outputs: [], stateMutability: 'payable', type: 'function' }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint128', name: 'amount', type: 'uint128' }, + { internalType: 'uint256', name: 'minAcquired', type: 'uint256' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' } + ], + internalType: 'struct Swap.SwapAmountParams', + name: 'params', + type: 'tuple' + } + ], + name: 'swapAmount', + outputs: [ + { internalType: 'uint256', name: 'cost', type: 'uint256' }, + { internalType: 'uint256', name: 'acquire', type: 'uint256' } + ], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint128', name: 'desire', type: 'uint128' }, + { internalType: 'uint256', name: 'maxPayed', type: 'uint256' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' } + ], + internalType: 'struct Swap.SwapDesireParams', + name: 'params', + type: 'tuple' + } + ], + name: 'swapDesire', + outputs: [ + { internalType: 'uint256', name: 'cost', type: 'uint256' }, + { internalType: 'uint256', name: 'acquire', type: 'uint256' } + ], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenX', type: 'address' }, + { internalType: 'address', name: 'tokenY', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'int24', name: 'boundaryPt', type: 'int24' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint128', name: 'amount', type: 'uint128' }, + { internalType: 'uint256', name: 'maxPayed', type: 'uint256' }, + { internalType: 'uint256', name: 'minAcquired', type: 'uint256' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' } + ], + internalType: 'struct Swap.SwapParams', + name: 'swapParams', + type: 'tuple' + } + ], + name: 'swapX2Y', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'x', type: 'uint256' }, + { internalType: 'uint256', name: 'y', type: 'uint256' }, + { internalType: 'bytes', name: 'data', type: 'bytes' } + ], + name: 'swapX2YCallback', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenX', type: 'address' }, + { internalType: 'address', name: 'tokenY', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'int24', name: 'boundaryPt', type: 'int24' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint128', name: 'amount', type: 'uint128' }, + { internalType: 'uint256', name: 'maxPayed', type: 'uint256' }, + { internalType: 'uint256', name: 'minAcquired', type: 'uint256' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' } + ], + internalType: 'struct Swap.SwapParams', + name: 'swapParams', + type: 'tuple' + } + ], + name: 'swapX2YDesireY', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenX', type: 'address' }, + { internalType: 'address', name: 'tokenY', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'int24', name: 'boundaryPt', type: 'int24' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint128', name: 'amount', type: 'uint128' }, + { internalType: 'uint256', name: 'maxPayed', type: 'uint256' }, + { internalType: 'uint256', name: 'minAcquired', type: 'uint256' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' } + ], + internalType: 'struct Swap.SwapParams', + name: 'swapParams', + type: 'tuple' + } + ], + name: 'swapY2X', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'x', type: 'uint256' }, + { internalType: 'uint256', name: 'y', type: 'uint256' }, + { internalType: 'bytes', name: 'data', type: 'bytes' } + ], + name: 'swapY2XCallback', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenX', type: 'address' }, + { internalType: 'address', name: 'tokenY', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'int24', name: 'boundaryPt', type: 'int24' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint128', name: 'amount', type: 'uint128' }, + { internalType: 'uint256', name: 'maxPayed', type: 'uint256' }, + { internalType: 'uint256', name: 'minAcquired', type: 'uint256' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' } + ], + internalType: 'struct Swap.SwapParams', + name: 'swapParams', + type: 'tuple' + } + ], + name: 'swapY2XDesireX', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'sweepToken', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'unwrapWETH9', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { stateMutability: 'payable', type: 'receive' } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider.ts new file mode 100644 index 0000000..0ca3c44 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider.ts @@ -0,0 +1,183 @@ +import { getMulticallContracts } from 'iziswap-sdk/lib/base'; +import { searchPathQuery } from 'iziswap-sdk/lib/search/func'; +import { SearchPathQueryParams, SwapDirection } from 'iziswap-sdk/lib/search/types'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, Token } from 'src/common/tokens'; +import { wrappedAddress } from 'src/common/tokens/constants/wrapped-addresses'; +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { combineOptions } from 'src/common/utils/options'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { OnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { OnChainProxyFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { getGasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info'; +import { IzumiTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-trade'; +import { IzumiTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/models/izumi-trade-struct'; +import { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options'; +import { EvmOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/evm-on-chain-provider'; +import { UniswapV2CalculationOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-calculation-options'; + +export abstract class IzumiProvider extends EvmOnChainProvider { + public abstract readonly blockchain: EvmBlockchainName; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.IZUMI; + } + + protected readonly defaultOptions: UniswapV2CalculationOptions = { + ...evmProviderDefaultOptions, + deadlineMinutes: 20, + disableMultihops: false + }; + + protected abstract readonly dexAddress: string; + + protected abstract readonly config: { + readonly maxTransitTokens: number; + readonly routingTokenAddresses: string[]; + readonly liquidityManagerAddress: string; + readonly quoterAddress: string; + readonly multicallAddress: string; + readonly supportedFees: number[]; + }; + + public async calculate( + from: PriceTokenAmount, + to: PriceToken, + options?: OnChainCalculationOptions + ): Promise { + const fullOptions = combineOptions(options, this.defaultOptions); + + let proxyFeeInfo: OnChainProxyFeeInfo | undefined; + let weiAmountWithoutFee = from.stringWeiAmount; + + if (fullOptions.useProxy) { + const proxyContractInfo = await this.handleProxyContract( + new PriceTokenAmount({ + ...from.asStruct, + weiAmount: from.weiAmount + }), + fullOptions + ); + proxyFeeInfo = proxyContractInfo.proxyFeeInfo; + weiAmountWithoutFee = proxyContractInfo.fromWithoutFee.stringWeiAmount; + } + + const chainId = blockchainId[from.blockchain]; + + const web3 = Injector.web3PublicService.getWeb3Public(from.blockchain).web3Provider; + const multicallContract = getMulticallContracts(this.config.multicallAddress, web3); + + const transitTokens = await Token.createTokens( + this.config.routingTokenAddresses, + this.blockchain + ); + + const tokenIn = { + chainId, + symbol: from.isNative ? wrappedNativeTokensList[this.blockchain]?.symbol : from.symbol, + address: from.isNative ? wrappedAddress[this.blockchain] : from.address, + decimal: from.decimals + }; + + const tokenOut = { + chainId, + symbol: to.isNative ? wrappedNativeTokensList[this.blockchain]?.symbol : to.symbol, + address: to.isNative ? wrappedAddress[this.blockchain] : to.address, + decimal: to.decimals + }; + + const midTokenList = transitTokens.map(token => ({ + chainId, + symbol: token.symbol, + address: token.address, + decimal: token.decimals + })); + + const searchParams = { + chainId, + web3, + multicall: multicallContract, + tokenIn, + tokenOut, + liquidityManagerAddress: this.config.liquidityManagerAddress, + quoterAddress: this.config.quoterAddress, + poolBlackList: [], + midTokenList, + supportFeeContractNumbers: this.config.supportedFees, + support001Pools: [], + direction: SwapDirection.ExactIn, + amount: weiAmountWithoutFee, + shortBatchSize: this.blockchain === BLOCKCHAIN_NAME.MERLIN ? 5 : 20 + } as SearchPathQueryParams; + + let pathQueryResult = null; + try { + const result = await searchPathQuery(searchParams); + pathQueryResult = result.pathQueryResult; + if (!pathQueryResult) { + throw new RubicSdkError('No result'); + } + } catch (err) { + console.debug(err); + throw err; + } + + const wrapAddress = wrappedNativeTokensList[from.blockchain]?.address; + + const toToken = new PriceTokenAmount({ + ...to.asStruct, + tokenAmount: Web3Pure.fromWei(pathQueryResult.amount, to.decimals) + }); + + const transitPath = await Token.createTokens( + pathQueryResult.path.tokenChain.map(token => token.address).slice(1, -1), + from.blockchain + ); + + const tradeStruct: IzumiTradeStruct = { + from, + to: toToken, + path: [from, ...transitPath, to], + slippageTolerance: fullOptions.slippageTolerance, + gasFeeInfo: null, + useProxy: fullOptions.useProxy, + proxyFeeInfo, + fromWithoutFee: from, + withDeflation: fullOptions.withDeflation, + usedForCrossChain: fullOptions.usedForCrossChain, + dexContractAddress: this.dexAddress, + swapConfig: { + tokenChain: pathQueryResult.path.tokenChain.map(el => el.address), + feeChain: pathQueryResult.path.feeContractNumber + }, + strictERC20Token: + compareAddresses(wrapAddress!, from.address) || + compareAddresses(wrapAddress!, to.address) + }; + if (options?.gasCalculation === 'calculate') { + try { + const gasPriceInfo = await this.getGasPriceInfo(); + const gasLimit = await IzumiTrade.getGasLimit( + tradeStruct, + fullOptions.providerAddress + ); + tradeStruct.gasFeeInfo = getGasFeeInfo(gasLimit, gasPriceInfo!); + } catch {} + } + + return this.getProviderTrade(tradeStruct, fullOptions.providerAddress); + } + + public getProviderTrade(tradeStruct: IzumiTradeStruct, providerAddress: string): IzumiTrade { + return new IzumiTrade(tradeStruct, providerAddress); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-trade.ts new file mode 100644 index 0000000..75ea33a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-trade.ts @@ -0,0 +1,116 @@ +import BigNumber from 'bignumber.js'; +import { initialChainTable } from 'iziswap-sdk/lib/base'; +import { TokenInfoFormatted } from 'iziswap-sdk/lib/base/types'; +import { + getSwapChainWithExactInputCall, + getSwapContract, + SwapChainWithExactInputParams +} from 'iziswap-sdk/lib/swap'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { getGasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-price-info'; +import { IzumiTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/models/izumi-trade-struct'; + +export class IzumiTrade extends EvmOnChainTrade { + public static async getGasLimit( + tradeStruct: IzumiTradeStruct, + providerAddress: string + ): Promise { + const fromBlockchain = tradeStruct.from.blockchain; + const walletAddress = + Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address; + if (!walletAddress) { + return null; + } + + try { + const transactionConfig = await new IzumiTrade(tradeStruct, providerAddress).encode({ + fromAddress: walletAddress + }); + + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const gasLimit = await web3Public.getEstimatedGasByData( + walletAddress, + transactionConfig.to, + { + data: transactionConfig.data, + value: transactionConfig.value + } + ); + + if (!gasLimit?.isFinite()) { + return null; + } + return gasLimit; + } catch (err) { + console.debug(err); + return null; + } + } + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.IZUMI; + } + + public readonly dexContractAddress: string; + + private readonly swapConfig: { + readonly tokenChain: string[]; + readonly feeChain: number[]; + }; + + private readonly strictERC20Token: boolean = false; + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + const tokenChain = blockchainId[this.from.blockchain]; + const swapParams = { + feeChain: this.swapConfig.feeChain, + tokenChain: this.swapConfig.tokenChain.map(token => ({ + address: token, + chainId: tokenChain + })) as unknown as TokenInfoFormatted[], + inputAmount: this.from.stringWeiAmount, + minOutputAmount: this.toTokenAmountMin.stringWeiAmount, + recipient: options?.receiverAddress || this.walletAddress, + strictERC20Token: this.strictERC20Token + } as SwapChainWithExactInputParams; + + const chainId = blockchainId[this.from.blockchain]; + const chain = initialChainTable[chainId]!; + const web3 = Injector.web3PublicService.getWeb3Public(this.from.blockchain).web3Provider; + const swapContract = getSwapContract(this.dexContractAddress, web3); + const gasPriceInfo = await getGasPriceInfo(this.from.blockchain); + + const { swapCalling, options: data } = getSwapChainWithExactInputCall( + swapContract, + this.walletAddress, + chain, + swapParams, + gasPriceInfo.gasPrice.toFixed() + ); + + const config = { + to: this.dexContractAddress, + value: data.value, + data: swapCalling.encodeABI() + }; + + return { tx: config, toAmount: this.to.stringWeiAmount }; + } + + constructor(tradeStruct: IzumiTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + this.dexContractAddress = tradeStruct.dexContractAddress; + this.swapConfig = tradeStruct.swapConfig; + this.strictERC20Token = tradeStruct.strictERC20Token; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/models/izumi-pool-response.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/models/izumi-pool-response.ts new file mode 100644 index 0000000..22f8134 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/models/izumi-pool-response.ts @@ -0,0 +1,12 @@ +export interface IzumiPool { + readonly address: string; + readonly tokenX: string; + readonly tokenX_address: string; + readonly tokenY: string; + readonly tokenY_address: string; + readonly fee: number; +} + +export interface IzumiPoolResponse { + readonly data: ReadonlyArray; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/models/izumi-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/models/izumi-trade-struct.ts new file mode 100644 index 0000000..371f79a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/models/izumi-trade-struct.ts @@ -0,0 +1,10 @@ +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export interface IzumiTradeStruct extends EvmOnChainTradeStruct { + readonly dexContractAddress: string; + readonly swapConfig: { + readonly tokenChain: string[]; + readonly feeChain: number[]; + }; + readonly strictERC20Token: boolean; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/constants/provider-default-options.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/constants/provider-default-options.ts new file mode 100644 index 0000000..27cb09e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/constants/provider-default-options.ts @@ -0,0 +1,13 @@ +import { RequiredOnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; + +export const providerDefaultOptions: Omit< + RequiredOnChainCalculationOptions, + 'gasCalculation' | 'providerAddress' +> = { + slippageTolerance: 0.02, + useProxy: true, + withDeflation: { + from: { isDeflation: false }, + to: { isDeflation: false } + } +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options.ts new file mode 100644 index 0000000..86a3b1a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options.ts @@ -0,0 +1,9 @@ +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { RequiredOnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { providerDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/constants/provider-default-options'; + +export const evmProviderDefaultOptions: RequiredOnChainCalculationOptions = { + ...providerDefaultOptions, + gasCalculation: 'calculate', + providerAddress: EvmWeb3Pure.EMPTY_ADDRESS +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/evm-on-chain-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/evm-on-chain-provider.ts new file mode 100644 index 0000000..e8a158d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/evm-on-chain-provider.ts @@ -0,0 +1,64 @@ +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { Injector } from 'src/core/injector/injector'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; +import { + OnChainCalculationOptions, + RequiredOnChainCalculationOptions +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { OnChainProxyFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info'; +import { OnChainProxyService } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-proxy-service/on-chain-proxy-service'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { getGasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-price-info'; +import { GasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/models/gas-price-info'; +import { OnChainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/on-chain-provider'; + +export abstract class EvmOnChainProvider extends OnChainProvider { + public abstract readonly blockchain: EvmBlockchainName; + + protected readonly onChainProxyService = new OnChainProxyService(); + + protected get walletAddress(): string { + return Injector.web3PrivateService.getWeb3Private(CHAIN_TYPE.EVM).address; + } + + protected get web3Public(): EvmWeb3Public { + return Injector.web3PublicService.getWeb3Public(this.blockchain); + } + + public abstract calculate( + from: PriceTokenAmount, + to: PriceToken, + options?: OnChainCalculationOptions + ): Promise; + + protected async handleProxyContract( + from: PriceTokenAmount, + fullOptions: RequiredOnChainCalculationOptions + ): Promise<{ + fromWithoutFee: PriceTokenAmount; + proxyFeeInfo: OnChainProxyFeeInfo | undefined; + }> { + let fromWithoutFee: PriceTokenAmount; + let proxyFeeInfo: OnChainProxyFeeInfo | undefined; + if (fullOptions.useProxy) { + proxyFeeInfo = await this.onChainProxyService.getFeeInfo( + from, + fullOptions.providerAddress + ); + fromWithoutFee = getFromWithoutFee(from, proxyFeeInfo.platformFee.percent); + } else { + fromWithoutFee = from; + } + return { + fromWithoutFee, + proxyFeeInfo + }; + } + + protected async getGasPriceInfo(): Promise { + return getGasPriceInfo(this.blockchain); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/models/gas-price-info.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/models/gas-price-info.ts new file mode 100644 index 0000000..460ade2 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/models/gas-price-info.ts @@ -0,0 +1,8 @@ +import BigNumber from 'bignumber.js'; + +export interface GasPriceInfo { + readonly gasPrice: BigNumber; + readonly gasPriceInEth: BigNumber; + readonly gasPriceInUsd: BigNumber; + readonly maxFeePerGas?: BigNumber; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/on-chain-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/on-chain-provider.ts new file mode 100644 index 0000000..d9334e3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/on-chain-provider.ts @@ -0,0 +1,53 @@ +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { parseError } from 'src/common/utils/errors'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3Public } from 'src/core/blockchain/web3-public-service/web3-public/web3-public'; +import { HttpClient } from 'src/core/http-client/models/http-client'; +import { Injector } from 'src/core/injector/injector'; +import { OnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { OnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade'; + +/** + * Abstract class for all on-chain trade providers. + */ +export abstract class OnChainProvider { + public static parseError(err: unknown): RubicSdkError { + return parseError(err, 'Cannot calculate on-chain trade'); + } + + /** + * Provider blockchain. + */ + public abstract readonly blockchain: BlockchainName; + + public readonly supportReceiverAddress: boolean = true; + + /** + * Type of provider. + */ + public abstract get type(): OnChainTradeType; + + protected abstract get walletAddress(): string; + + protected get web3Public(): Web3Public { + return Injector.web3PublicService.getWeb3Public(this.blockchain); + } + + protected get httpClient(): HttpClient { + return Injector.httpClient; + } + + /** + * Calculates on-chain trade. + * @param from Token to sell with input amount. + * @param to Token to get. + * @param options Additional options. + */ + public abstract calculate( + from: PriceTokenAmount, + to: PriceToken, + options?: OnChainCalculationOptions + ): Promise; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/tron-on-chain-provider/constants/tron-provider-default-options.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/tron-on-chain-provider/constants/tron-provider-default-options.ts new file mode 100644 index 0000000..f20e019 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/tron-on-chain-provider/constants/tron-provider-default-options.ts @@ -0,0 +1,9 @@ +import { TronWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/tron-web3-pure'; +import { RequiredOnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { providerDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/constants/provider-default-options'; + +export const tronProviderDefaultOptions: RequiredOnChainCalculationOptions = { + ...providerDefaultOptions, + gasCalculation: 'disabled', + providerAddress: TronWeb3Pure.EMPTY_ADDRESS +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/tron-on-chain-provider/tron-on-chain-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/tron-on-chain-provider/tron-on-chain-provider.ts new file mode 100644 index 0000000..f766dc2 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/tron-on-chain-provider/tron-on-chain-provider.ts @@ -0,0 +1,26 @@ +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { BLOCKCHAIN_NAME, TronBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { CHAIN_TYPE } from 'src/core/blockchain/models/chain-type'; +import { TronWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/tron-web3-public/tron-web3-public'; +import { Injector } from 'src/core/injector/injector'; +import { OnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { TronOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/tron-on-chain-trade/tron-on-chain-trade'; +import { OnChainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/on-chain-provider'; + +export abstract class TronOnChainProvider extends OnChainProvider { + public readonly blockchain = BLOCKCHAIN_NAME.TRON; + + protected get walletAddress(): string { + return Injector.web3PrivateService.getWeb3Private(CHAIN_TYPE.TRON).address; + } + + protected get web3Public(): TronWeb3Public { + return Injector.web3PublicService.getWeb3Public(this.blockchain); + } + + public abstract calculate( + from: PriceTokenAmount, + to: PriceToken, + options?: OnChainCalculationOptions + ): Promise; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/abi.ts new file mode 100644 index 0000000..e57981f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/abi.ts @@ -0,0 +1,11 @@ +import { AbiItem } from 'web3-utils'; + +export const pancakeRouterAbi: AbiItem[] = [ + { + inputs: [{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' }], + name: 'multicall', + outputs: [{ internalType: 'bytes[]', name: 'results', type: 'bytes[]' }], + stateMutability: 'payable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/models/pancake-router-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/models/pancake-router-trade-struct.ts new file mode 100644 index 0000000..31d0239 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/models/pancake-router-trade-struct.ts @@ -0,0 +1,8 @@ +import { TradeType } from '@pancakeswap/sdk'; +import { SmartRouterTrade } from '@pancakeswap/smart-router/evm/v3-router/types'; +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export interface PancakeRouterTradeStruct extends EvmOnChainTradeStruct { + readonly trade: SmartRouterTrade; + readonly dexContractAddress: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/pancake-router-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/pancake-router-provider.ts new file mode 100644 index 0000000..a0d9435 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/pancake-router-provider.ts @@ -0,0 +1,229 @@ +import { Native as PancakeNative, Token as PancakeToken, TradeType } from '@pancakeswap/sdk'; +import { Pool, SmartRouter } from '@pancakeswap/smart-router/evm'; +import { Currency, CurrencyAmount } from '@pancakeswap/swap-sdk-core'; +import BigNumber from 'bignumber.js'; +import { GraphQLClient } from 'graphql-request'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, Token } from 'src/common/tokens'; +import { combineOptions } from 'src/common/utils/options'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { OnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { OnChainProxyFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { getGasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info'; +import { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options'; +import { EvmOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/evm-on-chain-provider'; +import { PancakeRouterTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/models/pancake-router-trade-struct'; +import { PancakeRouterTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/pancake-router-trade'; +import { UniswapV2CalculationOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-calculation-options'; +import { Chain, createPublicClient, http, PublicClient } from 'viem'; + +export abstract class PancakeRouterProvider extends EvmOnChainProvider { + public abstract readonly blockchain: EvmBlockchainName; + + protected abstract readonly chain: Chain; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.PANCAKE_SWAP; + } + + protected readonly defaultOptions: UniswapV2CalculationOptions = { + ...evmProviderDefaultOptions, + deadlineMinutes: 20, + disableMultihops: false + }; + + protected abstract readonly dexAddress: string; + + protected abstract readonly v3subgraphAddress: string; + + protected abstract readonly v2subgraphAddress: string; + + protected abstract readonly maxHops: number; + + protected abstract readonly maxSplits: number; + + public async calculate( + from: PriceTokenAmount, + to: PriceToken, + options?: OnChainCalculationOptions + ): Promise { + const fullOptions = combineOptions(options, this.defaultOptions); + let proxyFeeInfo: OnChainProxyFeeInfo | undefined; + let weiAmountWithoutFee = from.stringWeiAmount; + + if (fullOptions.useProxy) { + const proxyContractInfo = await this.handleProxyContract( + new PriceTokenAmount({ + ...from.asStruct, + weiAmount: from.weiAmount + }), + fullOptions + ); + proxyFeeInfo = proxyContractInfo.proxyFeeInfo; + weiAmountWithoutFee = proxyContractInfo.fromWithoutFee.stringWeiAmount; + } + + const fromChainId = blockchainId[from.blockchain]; + const currencyA = from.isNative + ? PancakeNative.onChain(fromChainId) + : new PancakeToken(fromChainId, from.address, from.decimals, from.symbol, from.name); + + const toChainId = blockchainId[to.blockchain]; + const currencyB = to.isNative + ? PancakeNative.onChain(toChainId) + : new PancakeToken(toChainId, to.address, to.decimals, to.symbol, to.name); + + const fromCurrency = CurrencyAmount.fromRawAmount(currencyA, weiAmountWithoutFee); + const quoteProvider = SmartRouter.createQuoteProvider({ + // @ts-ignore + onChainProvider: () => this.createPublicClient() + }); + const pools = await this.getPools(currencyA, currencyB); + + const trade = await SmartRouter.getBestTrade( + fromCurrency, + currencyB, + TradeType.EXACT_INPUT, + { + gasPriceWei: () => this.createPublicClient().getGasPrice(), + maxHops: this.maxHops, + maxSplits: this.maxSplits, + poolProvider: SmartRouter.createStaticPoolProvider(pools), + quoteProvider, + quoterOptimization: true + } + ); + if (!trade) { + throw new RubicSdkError(''); + } + + const toAmount = trade.outputAmount.toFixed(); + const toToken = new PriceTokenAmount({ + ...to.asStruct, + tokenAmount: new BigNumber(toAmount) + }); + + const path = await this.getPath(from, to, trade.routes?.[0]?.path || []); + const tradeStruct: PancakeRouterTradeStruct = { + from, + to: toToken, + path, + slippageTolerance: fullOptions.slippageTolerance, + gasFeeInfo: null, + useProxy: fullOptions.useProxy, + proxyFeeInfo, + fromWithoutFee: from, + withDeflation: fullOptions.withDeflation, + usedForCrossChain: fullOptions.usedForCrossChain, + trade, + dexContractAddress: this.dexAddress + }; + if (options?.gasCalculation === 'calculate') { + try { + const gasPriceInfo = await this.getGasPriceInfo(); + const gasLimit = await PancakeRouterTrade.getGasLimit( + tradeStruct, + fullOptions.providerAddress + ); + tradeStruct.gasFeeInfo = getGasFeeInfo(gasLimit, gasPriceInfo!); + } catch {} + } + + return new PancakeRouterTrade(tradeStruct, fullOptions.providerAddress); + } + + private async getPools(currencyA: Currency, currencyB: Currency): Promise { + const v3SubgraphClient = new GraphQLClient(this.v3subgraphAddress); + const v2SubgraphClient = new GraphQLClient(this.v2subgraphAddress); + + const pairs = SmartRouter.getPairCombinations(currencyA, currencyB); + + const allPools = await Promise.allSettled([ + // // @ts-ignore + SmartRouter.getStablePoolsOnChain(pairs, () => this.createPublicClient()), + SmartRouter.getV2CandidatePools({ + // @ts-ignore + onChainProvider: () => this.createPublicClient(), + // @ts-ignore + v2SubgraphProvider: () => v2SubgraphClient, + // @ts-ignore + v3SubgraphProvider: () => v3SubgraphClient, + currencyA, + currencyB + }), + SmartRouter.getV3CandidatePools({ + // @ts-ignore + onChainProvider: () => this.createPublicClient(), + // @ts-ignore + subgraphProvider: () => v3SubgraphClient, + currencyA, + currencyB, + subgraphCacheFallback: false + }) + ]); + + const fulfilledPools = allPools.reduce((acc, pool) => { + if (pool.status === 'fulfilled') { + return [...acc, ...pool.value]; + } + return acc; + }, [] as Pool[]); + + return fulfilledPools.flat(); + } + + private createPublicClient(): PublicClient { + const transportUrl = Injector.web3PublicService.rpcProvider[this.blockchain]?.rpcList[0]!; + + if (this.blockchain === BLOCKCHAIN_NAME.POLYGON_ZKEVM) { + return createPublicClient({ + chain: { + ...this.chain, + contracts: { + multicall3: { + address: '0xcA11bde05977b3631167028862bE2a173976CA11', + blockCreated: 57746 + } + } + }, + transport: http(transportUrl), + batch: { + multicall: { + batchSize: 512 + } + } + }); + } + + return createPublicClient({ + chain: this.chain, + transport: http(transportUrl), + batch: { + multicall: { + batchSize: 1024 * 200 + } + } + }); + } + + private async getPath(fromToken: Token, toToken: Token, route: Currency[]): Promise { + const path = [fromToken]; + if (route.length > 2) { + const addresses = route + .slice(1, -1) + .map(token => ('address' in token && token.address) || EvmWeb3Pure.EMPTY_ADDRESS); + const tokens = await Token.createTokens(addresses, fromToken.blockchain); + path.push(...tokens); + } + path.push(toToken); + return path; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/pancake-router-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/pancake-router-trade.ts new file mode 100644 index 0000000..1805118 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/pancake-router-trade.ts @@ -0,0 +1,84 @@ +import { Percent, TradeType } from '@pancakeswap/sdk'; +import { SwapRouter } from '@pancakeswap/smart-router/evm'; +import { SmartRouterTrade } from '@pancakeswap/smart-router/evm/v3-router/types'; +import BigNumber from 'bignumber.js'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { PancakeRouterTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/models/pancake-router-trade-struct'; + +export class PancakeRouterTrade extends EvmOnChainTrade { + public static async getGasLimit( + tradeStruct: PancakeRouterTradeStruct, + providerAddress: string + ): Promise { + const fromBlockchain = tradeStruct.from.blockchain; + const walletAddress = + Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address; + if (!walletAddress) { + return null; + } + + try { + const transactionConfig = await new PancakeRouterTrade( + tradeStruct, + providerAddress + ).encode({ fromAddress: walletAddress }); + + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const gasLimit = await web3Public.getEstimatedGasByData( + walletAddress, + transactionConfig.to, + { + data: transactionConfig.data, + value: transactionConfig.value + } + ); + + if (!gasLimit?.isFinite()) { + return null; + } + return gasLimit; + } catch (err) { + console.debug(err); + return null; + } + } + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.PANCAKE_SWAP; + } + + public readonly dexContractAddress: string; + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + const slippage = Number.parseInt(String(this.slippageTolerance * 100)); + const slippagePercent = new Percent(slippage, 100); + const payload = SwapRouter.swapCallParameters(this.trade, { + slippageTolerance: slippagePercent, + ...(options.receiverAddress && { recipient: options.receiverAddress as `0x${string}` }) + }); + const config = { + to: this.dexContractAddress, + value: payload.value, + data: payload.calldata + }; + + return { tx: config, toAmount: this.to.stringWeiAmount }; + } + + private readonly trade: SmartRouterTrade; + + constructor(tradeStruct: PancakeRouterTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + this.trade = tradeStruct.trade; + this.dexContractAddress = tradeStruct.dexContractAddress; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/quickswap-v3/quickswap-v3-quoter-controller.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/quickswap-v3/quickswap-v3-quoter-controller.ts new file mode 100644 index 0000000..e378667 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/quickswap-v3/quickswap-v3-quoter-controller.ts @@ -0,0 +1,82 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, Token } from 'src/common/tokens'; +import { notNull } from 'src/common/utils/object'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/models/algebra-route'; + +interface GetQuoterMethodsDataOptions { + routesTokens: Token[]; + to: Token; + exact: Exact; + weiAmount: string; + maxTransitTokens: number; +} + +/** + * Works with requests, related to Uniswap v3 liquidity pools. + */ +export class QuickswapV3QuoterController extends AlgebraQuoterController { + public async getAllRoutes( + from: PriceToken, + to: PriceToken, + exact: Exact, + weiAmount: string, + routeMaxTransitTokens: number + ): Promise { + const routesTokens = (await this.getOrCreateRouterTokens()).filter( + token => !token.isEqualToTokens([from, to]) + ); + + const options: Omit = { + routesTokens, + to, + exact, + weiAmount + }; + const quoterMethodsData = [...Array(routeMaxTransitTokens + 1)] + .map((_, maxTransitTokens) => + this.getQuoterMethodsData( + { + ...options, + maxTransitTokens + }, + [from] + ) + ) + .flat(); + + try { + const results = await Promise.allSettled<{ amountOut: string }>( + quoterMethodsData.map(data => + this.web3Public.callContractMethod<{ amountOut: string }>( + this.quoterContractAddress, + this.quoterContractABI, + data.methodData.methodName, + data.methodData.methodArguments + ) + ) + ); + + return results + .map((promiseResponce, index) => { + if (promiseResponce.status === 'fulfilled') { + const quoter = quoterMethodsData?.[index]; + if (!quoter) { + throw new RubicSdkError('Quoter has to be defined'); + } + return { + outputAbsoluteAmount: new BigNumber(promiseResponce.value.amountOut!), + path: quoter.path + }; + } + return null; + }) + .filter(notNull); + } catch (error) { + console.debug(error); + return []; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/router-support-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/router-support-abi.ts new file mode 100644 index 0000000..6313cce --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/router-support-abi.ts @@ -0,0 +1,93 @@ +import { AbiItem } from 'web3-utils'; + +export const routerSupportAbi: AbiItem[] = [ + { + inputs: [ + { internalType: 'address', name: 'tokenA', type: 'address' }, + { internalType: 'address', name: 'tokenB', type: 'address' }, + { internalType: 'address[]', name: 'factories', type: 'address[]' }, + { internalType: 'address[]', name: 'baseTokens', type: 'address[]' }, + { internalType: 'address', name: 'master', type: 'address' }, + { internalType: 'address', name: 'account', type: 'address' } + ], + name: 'getRoutePools', + outputs: [ + { + components: [ + { + components: [ + { internalType: 'address', name: 'pool', type: 'address' }, + { internalType: 'address', name: 'tokenA', type: 'address' }, + { internalType: 'address', name: 'tokenB', type: 'address' }, + { internalType: 'uint16', name: 'poolType', type: 'uint16' }, + { internalType: 'uint256', name: 'reserveA', type: 'uint256' }, + { internalType: 'uint256', name: 'reserveB', type: 'uint256' }, + { internalType: 'uint24', name: 'swapFeeAB', type: 'uint24' }, + { internalType: 'uint24', name: 'swapFeeBA', type: 'uint24' } + ], + internalType: 'struct RouteHelper.RoutePool[]', + name: 'poolsDirect', + type: 'tuple[]' + }, + { + components: [ + { internalType: 'address', name: 'pool', type: 'address' }, + { internalType: 'address', name: 'tokenA', type: 'address' }, + { internalType: 'address', name: 'tokenB', type: 'address' }, + { internalType: 'uint16', name: 'poolType', type: 'uint16' }, + { internalType: 'uint256', name: 'reserveA', type: 'uint256' }, + { internalType: 'uint256', name: 'reserveB', type: 'uint256' }, + { internalType: 'uint24', name: 'swapFeeAB', type: 'uint24' }, + { internalType: 'uint24', name: 'swapFeeBA', type: 'uint24' } + ], + internalType: 'struct RouteHelper.RoutePool[]', + name: 'poolsA', + type: 'tuple[]' + }, + { + components: [ + { internalType: 'address', name: 'pool', type: 'address' }, + { internalType: 'address', name: 'tokenA', type: 'address' }, + { internalType: 'address', name: 'tokenB', type: 'address' }, + { internalType: 'uint16', name: 'poolType', type: 'uint16' }, + { internalType: 'uint256', name: 'reserveA', type: 'uint256' }, + { internalType: 'uint256', name: 'reserveB', type: 'uint256' }, + { internalType: 'uint24', name: 'swapFeeAB', type: 'uint24' }, + { internalType: 'uint24', name: 'swapFeeBA', type: 'uint24' } + ], + internalType: 'struct RouteHelper.RoutePool[]', + name: 'poolsB', + type: 'tuple[]' + }, + { + components: [ + { internalType: 'address', name: 'pool', type: 'address' }, + { internalType: 'address', name: 'tokenA', type: 'address' }, + { internalType: 'address', name: 'tokenB', type: 'address' }, + { internalType: 'uint16', name: 'poolType', type: 'uint16' }, + { internalType: 'uint256', name: 'reserveA', type: 'uint256' }, + { internalType: 'uint256', name: 'reserveB', type: 'uint256' }, + { internalType: 'uint24', name: 'swapFeeAB', type: 'uint24' }, + { internalType: 'uint24', name: 'swapFeeBA', type: 'uint24' } + ], + internalType: 'struct RouteHelper.RoutePool[]', + name: 'poolsBase', + type: 'tuple[]' + } + ], + internalType: 'struct RouteHelper.RoutePools', + name: 'routePools', + type: 'tuple' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'isContract', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abi.ts new file mode 100644 index 0000000..3a49579 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abi.ts @@ -0,0 +1,201 @@ +import { AbiItem } from 'web3-utils'; + +export const syncSwapAbi: AbiItem[] = [ + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: 'address', + name: 'pool', + type: 'address' + }, + { + internalType: 'bytes', + name: 'data', + type: 'bytes' + }, + { + internalType: 'address', + name: 'callback', + type: 'address' + }, + { + internalType: 'bytes', + name: 'callbackData', + type: 'bytes' + } + ], + internalType: 'struct IRouter.SwapStep[]', + name: 'steps', + type: 'tuple[]' + }, + { + internalType: 'address', + name: 'tokenIn', + type: 'address' + }, + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + } + ], + internalType: 'struct IRouter.SwapPath[]', + name: 'paths', + type: 'tuple[]' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swap', + outputs: [ + { + components: [ + { + internalType: 'address', + name: 'token', + type: 'address' + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256' + } + ], + internalType: 'struct IPool.TokenAmount', + name: 'amountOut', + type: 'tuple' + } + ], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: 'address', + name: 'pool', + type: 'address' + }, + { + internalType: 'bytes', + name: 'data', + type: 'bytes' + }, + { + internalType: 'address', + name: 'callback', + type: 'address' + }, + { + internalType: 'bytes', + name: 'callbackData', + type: 'bytes' + } + ], + internalType: 'struct IRouter.SwapStep[]', + name: 'steps', + type: 'tuple[]' + }, + { + internalType: 'address', + name: 'tokenIn', + type: 'address' + }, + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + } + ], + internalType: 'struct IRouter.SwapPath[]', + name: 'paths', + type: 'tuple[]' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + }, + { + components: [ + { + internalType: 'address', + name: 'token', + type: 'address' + }, + { + internalType: 'uint256', + name: 'approveAmount', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + }, + { + internalType: 'uint8', + name: 'v', + type: 'uint8' + }, + { + internalType: 'bytes32', + name: 'r', + type: 'bytes32' + }, + { + internalType: 'bytes32', + name: 's', + type: 'bytes32' + } + ], + internalType: 'struct IRouter.SplitPermitParams', + name: 'permit', + type: 'tuple' + } + ], + name: 'swapWithPermit', + outputs: [ + { + components: [ + { + internalType: 'address', + name: 'token', + type: 'address' + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256' + } + ], + internalType: 'struct IPool.TokenAmount', + name: 'amountOut', + type: 'tuple' + } + ], + stateMutability: 'payable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abstract-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abstract-provider.ts new file mode 100644 index 0000000..41eb5f9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abstract-provider.ts @@ -0,0 +1,161 @@ +import BigNumber from 'bignumber.js'; +import { NotSupportedTokensError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, Token } from 'src/common/tokens'; +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { combineOptions } from 'src/common/utils/options'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { createTokenNativeAddressProxy } from 'src/features/common/utils/token-native-address-proxy'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { OnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { getGasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info'; +import { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options'; +import { EvmOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/evm-on-chain-provider'; +import { SyncSwapAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abstract-trade'; +import { SyncSwapFactory } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-factory'; +import { SyncSwapPathFactory } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-path-factory'; +import { SyncSwapRouter } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-router'; +import { RoutePools } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/typings'; + +export abstract class SyncSwapAbstractProvider extends EvmOnChainProvider { + public abstract blockchain: EvmBlockchainName; + + protected abstract dexContractAddress: string; + + protected abstract routerHelperContract: string; + + protected abstract vault: string; + + protected abstract factories: string[]; + + protected abstract routeTokens: string[]; + + protected abstract masterAddress: string; + + protected abstract maxTransitTokens: number; + + private readonly defaultOptions = evmProviderDefaultOptions; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SYNC_SWAP; + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options?: OnChainCalculationOptions + ): Promise { + const fromAddress = + options?.useProxy || this.defaultOptions.useProxy + ? rubicProxyContractAddress[from.blockchain].gateway + : this.walletAddress; + const fullOptions = combineOptions(options, { + ...this.defaultOptions, + fromAddress + }); + const fromProxy = createTokenNativeAddressProxy( + from, + wrappedNativeTokensList[from.blockchain]!.address + ); + const toProxy = createTokenNativeAddressProxy( + toToken, + wrappedNativeTokensList[from.blockchain]!.address + ); + const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract( + fromProxy, + fullOptions + ); + + const availablePools = await this.getAvailablePools(fromProxy, toProxy); + if (!availablePools) { + throw new NotSupportedTokensError(); + } + + const paths = SyncSwapPathFactory.findAllPossiblePaths( + fromProxy.address, + toProxy.address, + availablePools + ); + + const filteredPaths = await SyncSwapPathFactory.getBestPath( + paths, + fromWithoutFee.stringWeiAmount, + this.blockchain + ); + + const sortedPaths = filteredPaths.filter(item => item.length <= this.maxTransitTokens + 1); + + const bestRoute = await SyncSwapRouter.findBestAmountsForPathsExactIn( + sortedPaths, + fromWithoutFee.stringWeiAmount, + this.blockchain + ); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: new BigNumber(bestRoute.amountOut.toString()) + }); + + const transitAddresses = bestRoute.pathsWithAmounts[0]!.stepsWithAmount.slice(1).map( + step => step.tokenIn + ); + const transitTokens = await Token.createTokens(transitAddresses, from.blockchain); + + const tradeStruct = { + from, + to, + slippageTolerance: fullOptions.slippageTolerance, + gasFeeInfo: null, + useProxy: fullOptions.useProxy, + proxyFeeInfo, + fromWithoutFee, + withDeflation: fullOptions.withDeflation, + usedForCrossChain: fullOptions.usedForCrossChain, + path: [from, ...transitTokens, toToken], + bestPathWithAmounts: bestRoute + }; + + try { + const gasPriceInfo = await this.getGasPriceInfo(); + const gasLimit = await SyncSwapAbstractTrade.getGasLimit( + tradeStruct, + this.dexContractAddress, + fullOptions.providerAddress + ); + const gasFeeInfo = getGasFeeInfo(gasLimit, gasPriceInfo); + + return new SyncSwapAbstractTrade( + { ...tradeStruct, gasFeeInfo }, + fullOptions.providerAddress, + this.dexContractAddress + ); + } catch { + return new SyncSwapAbstractTrade( + tradeStruct, + fullOptions.providerAddress, + this.dexContractAddress + ); + } + } + + private async getAvailablePools( + from: PriceTokenAmount, + toToken: PriceToken + ): Promise { + return SyncSwapFactory.fetchRoutePools( + from.address, + toToken.address, + this.walletAddress || EvmWeb3Pure.EMPTY_ADDRESS, + this.vault, + this.factories.map(address => address.toLowerCase()), + this.routeTokens.map(address => address.toLowerCase()), + this.masterAddress, + this.routerHelperContract, + this.blockchain + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abstract-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abstract-trade.ts new file mode 100644 index 0000000..47891b0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abstract-trade.ts @@ -0,0 +1,144 @@ +import { ZERO_ADDRESS } from '@1inch/limit-order-protocol-utils'; +import BigNumber from 'bignumber.js'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { deadlineMinutesTimestamp } from 'src/common/utils/options'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { createTokenNativeAddressProxy } from 'src/features/common/utils/token-native-address-proxy'; +import { oneinchApiParams } from 'src/features/on-chain/calculation-manager/providers/aggregators/1inch/constants/constants'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; +import { syncSwapAbi } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abi'; +import { BestPathsWithAmounts } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/typings'; + +export class SyncSwapAbstractTrade extends EvmOnChainTrade { + public readonly dexContractAddress: string; + + private readonly bestPathWithAmounts: BestPathsWithAmounts; + + /** @internal */ + public static async getGasLimit( + tradeStruct: EvmOnChainTradeStruct & { bestPathWithAmounts: BestPathsWithAmounts }, + dexContractAddress: string, + providerAddress: string + ): Promise { + const fromBlockchain = tradeStruct.from.blockchain; + const walletAddress = + Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address; + if (!walletAddress) { + return null; + } + + try { + const transactionConfig = await new SyncSwapAbstractTrade( + tradeStruct, + providerAddress || EvmWeb3Pure.EMPTY_ADDRESS, + dexContractAddress + ).encode({ fromAddress: walletAddress }); + + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const gasLimit = ( + await web3Public.batchEstimatedGas(walletAddress, [transactionConfig]) + )[0]; + + if (!gasLimit?.isFinite()) { + return null; + } + return gasLimit; + } catch (_err) { + return null; + } + } + + private readonly nativeSupportedFromWithoutFee: PriceTokenAmount; + + private readonly nativeSupportedTo: PriceTokenAmount; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SYNC_SWAP; + } + + constructor( + tradeStruct: EvmOnChainTradeStruct & { bestPathWithAmounts: BestPathsWithAmounts }, + providerAddress: string, + dexContractAddress: string + ) { + super(tradeStruct, providerAddress); + + this.nativeSupportedFromWithoutFee = createTokenNativeAddressProxy( + tradeStruct.fromWithoutFee, + oneinchApiParams.nativeAddress + ); + this.nativeSupportedTo = createTokenNativeAddressProxy( + tradeStruct.to, + oneinchApiParams.nativeAddress + ); + this.bestPathWithAmounts = tradeStruct.bestPathWithAmounts; + this.dexContractAddress = dexContractAddress; + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + await this.checkFromAddress(options.fromAddress, true); + await this.checkReceiverAddress(options.receiverAddress); + + const params = this.getCallParameters(options?.receiverAddress); + const gasParams = this.getGasParams(options); + const value = this.from.isNative ? this.from.stringWeiAmount : '0'; + + const config = EvmWeb3Pure.encodeMethodCall( + this.dexContractAddress, + syncSwapAbi, + 'swap', + params, + value, + gasParams + ); + + return { tx: config, toAmount: this.to.stringWeiAmount }; + } + + public getCallParameters(receiverAddress?: string): unknown[] { + const paths = this.bestPathWithAmounts.pathsWithAmounts.map(path => { + const pathTokenInRoute = path.stepsWithAmount[0]!.tokenIn; + const pathTokenIn = this.from.isNative ? ZERO_ADDRESS : pathTokenInRoute; + + return { + steps: path.stepsWithAmount.map((step, i) => { + const isLastStep = i === path.stepsWithAmount.length - 1; + const stepTo = isLastStep + ? receiverAddress || this.walletAddress + : path.stepsWithAmount[i + 1]!.pool.pool; + + let withdrawMode = 0; + if (isLastStep) { + withdrawMode = this.to.isNative ? 1 : 2; + } + + const data = EvmWeb3Pure.encodeParameters( + ['address', 'address', 'uint8'], + [step.tokenIn, stepTo, withdrawMode] + ); + + return { + pool: step.pool.pool, + data, + callback: ZERO_ADDRESS, + callbackData: '0x' + }; + }), + + tokenIn: pathTokenIn, + amountIn: path.amountIn + }; + }); + return [paths, this.toTokenAmountMin.stringWeiAmount, String(deadlineMinutesTimestamp(30))]; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-stable-pool.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-stable-pool.ts new file mode 100644 index 0000000..d3c7680 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-stable-pool.ts @@ -0,0 +1,43 @@ +import { AbiItem } from 'web3-utils'; + +export const syncSwapStablePool: AbiItem[] = [ + { + inputs: [], + name: 'token0', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'token0PrecisionMultiplier', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'token1PrecisionMultiplier', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/constants.ts new file mode 100644 index 0000000..b49ec65 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/constants.ts @@ -0,0 +1,18 @@ +import { BigNumber } from 'ethers'; + +export const MAX_LOOP_LIMIT = 256; +export const MAX_FEE = BigNumber.from(100000); // 1e5 +export const STABLE_POOL_A = BigNumber.from(1000); +export const ZERO = BigNumber.from(0); +export const ONE = BigNumber.from(1); +export const TWO = BigNumber.from(2); +export const THREE = BigNumber.from(3); +export const FOUR = BigNumber.from(4); +export const granularity = 10; // div to 10 parts + +export const UINT128_MAX = BigNumber.from(2).pow(128).sub(1); +export const UINT256_MAX = BigNumber.from(2).pow(256).sub(1); + +// @TODO SyncSwap +export const MAX_XP = BigNumber.from(2).pow(256).sub(1); +export const ETHER = BigNumber.from(10).pow(18); diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-factory.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-factory.ts new file mode 100644 index 0000000..571ef87 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-factory.ts @@ -0,0 +1,97 @@ +import { BigNumber } from 'ethers'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Injector } from 'src/core/injector/injector'; +import { routerSupportAbi } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/router-support-abi'; +import { + RoutePoolData, + RoutePools, + RoutePoolsBlockchain +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/typings'; + +type StringRoutePoolData = Omit & { + reserveA: string; + reserveB: string; +}; + +interface RoutePoolsBlockchainResponse { + poolsA: StringRoutePoolData[]; + poolsB: StringRoutePoolData[]; + poolsBase: StringRoutePoolData[]; + poolsDirect: StringRoutePoolData[]; +} + +export class SyncSwapFactory { + private static normalizePool(vault: string, pool: RoutePoolData): RoutePoolData { + return { + ...pool, + vault, + tokenA: pool.tokenA.toLowerCase(), + tokenB: pool.tokenB.toLowerCase() + }; + } + + private static normalizePools(vault: string, pools: RoutePoolData[]): RoutePoolData[] { + return [...pools].map(pool => SyncSwapFactory.normalizePool(vault, pool)); + } + + private static transformResponse(routes: RoutePoolsBlockchainResponse): RoutePoolsBlockchain { + const fn: (el: StringRoutePoolData) => RoutePoolData = el => ({ + ...el, + reserveA: BigNumber.from(el.reserveA), + reserveB: BigNumber.from(el.reserveB) + }); + return { + poolsDirect: routes.poolsDirect.map(fn), + poolsA: routes.poolsA.map(fn), + poolsB: routes.poolsB.map(fn), + poolsBase: routes.poolsBase.map(fn) + }; + } + + public static async fetchRoutePools( + tokenA: string, + tokenB: string, + account: string, + vault: string, + factories: string[], + routeTokens: string[], + masterAddress: string, + routerHelperAddress: string, + blockchain: EvmBlockchainName + ): Promise { + try { + const web3Public = Injector.web3PublicService.getWeb3Public(blockchain); + const response = (await web3Public.callContractMethod( + routerHelperAddress, + routerSupportAbi, + 'getRoutePools', + [tokenA, tokenB, factories, routeTokens, masterAddress, account] + )) as unknown as RoutePoolsBlockchainResponse; + + const payload = SyncSwapFactory.transformResponse(response); + const poolsDirect = SyncSwapFactory.normalizePools(vault, payload.poolsDirect); + + const [directPoolClassic, directPoolStable] = [poolsDirect[0]!, poolsDirect[1]!]; + const directPoolOptimal = + directPoolClassic.reserveA > directPoolStable.reserveA.mul(2) + ? directPoolClassic + : directPoolStable; + + return { + directPoolOptimal, + routeTokens, + tokenA, + tokenB, + timestamp: Date.now(), + pools: { + poolsDirect, + poolsA: SyncSwapFactory.normalizePools(vault, payload.poolsA), + poolsB: SyncSwapFactory.normalizePools(vault, payload.poolsB), + poolsBase: SyncSwapFactory.normalizePools(vault, payload.poolsBase) + } + }; + } catch (error) { + return null; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-path-factory.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-path-factory.ts new file mode 100644 index 0000000..df2de73 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-path-factory.ts @@ -0,0 +1,302 @@ +import { BigNumber } from 'ethers'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { SyncSwapRouter } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-router'; + +import { MAX_FEE, STABLE_POOL_A, ZERO } from './constants'; +import { GetAmountParams, Path, RoutePoolData, RoutePools, Step } from './typings'; + +export class SyncSwapPathFactory { + private static readonly liquidityMinReserve = BigNumber.from(1000); + + public static hasLiquidity( + routePool: RoutePoolData, + tokenInAddress?: string, + amountOut?: BigNumber + ): boolean { + if (tokenInAddress && amountOut) { + const reserveOut = + routePool.tokenA === tokenInAddress ? routePool.reserveB : routePool.reserveA; + return reserveOut.gt(amountOut); + } + + return ( + routePool.reserveA.gte(SyncSwapPathFactory.liquidityMinReserve) && + routePool.reserveB.gte(SyncSwapPathFactory.liquidityMinReserve) + ); + } + + private static createPath(steps: { routePool: RoutePoolData; tokenInAddress: string }[]): Path { + return steps.map(({ routePool, tokenInAddress }) => { + const step: Step = { + pool: routePool, + tokenIn: tokenInAddress, + swapFee: + tokenInAddress === routePool.tokenA ? routePool.swapFeeAB : routePool.swapFeeBA + }; + + return step; + }); + } + + private static getPoolKey( + tokenAAddress: string, + tokenBAddress: string, + poolType: string + ): string { + return tokenAAddress < tokenBAddress + ? `${tokenAAddress}:${tokenBAddress}:${poolType}` + : `${tokenBAddress}:${tokenAAddress}:${poolType}`; + } + + private static getPathsWith1Hop( + poolsA: RoutePoolData[], + poolsB: RoutePoolData[], + tokenIn: string, + baseToken: string + ): Path[] { + const paths: Path[] = []; + + for (const poolA of poolsA) { + for (const poolB of poolsB) { + if (poolA && poolB) { + paths.push( + SyncSwapPathFactory.createPath([ + { routePool: poolA, tokenInAddress: tokenIn }, + { routePool: poolB, tokenInAddress: baseToken } + ]) + ); + } + } + } + + return paths; + } + + private static getPathsWith2Hops( + poolsA: RoutePoolData[], + poolsBase: RoutePoolData[], + poolsB: RoutePoolData[], + tokenIn: string, + baseToken1: string, + baseToken2: string + ): Path[] { + const paths: Path[] = []; + + for (const poolA of poolsA) { + for (const poolBase of poolsBase) { + for (const poolB of poolsB) { + if (poolA && poolBase && poolB) { + paths.push( + SyncSwapPathFactory.createPath([ + { routePool: poolA, tokenInAddress: tokenIn }, + { routePool: poolBase, tokenInAddress: baseToken1 }, + { routePool: poolB, tokenInAddress: baseToken2 } + ]) + ); + } + } + } + } + + return paths; + } + + public static findAllPossiblePaths( + tokenIn: string, + tokenOut: string, + routePools: RoutePools, + enableHops = true + ): Path[] { + const { + pools: { poolsDirect, poolsA, poolsB, poolsBase }, + routeTokens + } = routePools; + const paths: Path[] = []; + + for (const pool of poolsDirect) { + if (SyncSwapPathFactory.hasLiquidity(pool, tokenIn)) { + const path = SyncSwapPathFactory.createPath([ + { + routePool: pool, + tokenInAddress: tokenIn + } + ]); + + paths.push(path); + } + } + + if (!enableHops) return paths; + + const poolsByPoolKey: Map = new Map(); + for (const pool of [...poolsA, ...poolsB, ...poolsBase]) { + if (SyncSwapPathFactory.hasLiquidity(pool)) { + poolsByPoolKey.set( + SyncSwapPathFactory.getPoolKey(pool.tokenA, pool.tokenB, pool.poolType), + pool + ); + } + } + + for (const baseToken of routeTokens) { + if (baseToken === tokenIn || baseToken === tokenOut) { + // eslint-disable-next-line no-continue + continue; + } + + const poolA1 = poolsByPoolKey.get( + SyncSwapPathFactory.getPoolKey(tokenIn, baseToken, '1') + ); + const poolA2 = poolsByPoolKey.get( + SyncSwapPathFactory.getPoolKey(tokenIn, baseToken, '2') + ); + + const poolB1 = poolsByPoolKey.get( + SyncSwapPathFactory.getPoolKey(baseToken, tokenOut, '1') + ); + const poolB2 = poolsByPoolKey.get( + SyncSwapPathFactory.getPoolKey(baseToken, tokenOut, '2') + ); + + paths.push( + ...SyncSwapPathFactory.getPathsWith1Hop( + [poolA1, poolA2].filter(Boolean) as RoutePoolData[], + [poolB1, poolB2].filter(Boolean) as RoutePoolData[], + tokenIn, + baseToken + ) + ); + } + + for (const baseToken1 of routeTokens) { + if (baseToken1 === tokenIn || baseToken1 === tokenOut) { + // eslint-disable-next-line no-continue + continue; + } + const poolA1 = poolsByPoolKey.get( + SyncSwapPathFactory.getPoolKey(tokenIn, baseToken1, '1') + ); + const poolA2 = poolsByPoolKey.get( + SyncSwapPathFactory.getPoolKey(tokenIn, baseToken1, '2') + ); + + for (const baseToken2 of routeTokens) { + if ( + baseToken2 === tokenIn || + baseToken2 === tokenOut || + baseToken2 === baseToken1 + ) { + // eslint-disable-next-line no-continue + continue; + } + + const poolBase1 = poolsByPoolKey.get( + SyncSwapPathFactory.getPoolKey(baseToken1, baseToken2, '1') + ); + const poolBase2 = poolsByPoolKey.get( + SyncSwapPathFactory.getPoolKey(baseToken1, baseToken2, '2') + ); + + const poolB1 = poolsByPoolKey.get( + SyncSwapPathFactory.getPoolKey(baseToken2, tokenOut, '1') + ); + const poolB2 = poolsByPoolKey.get( + SyncSwapPathFactory.getPoolKey(baseToken2, tokenOut, '2') + ); + + paths.push( + ...SyncSwapPathFactory.getPathsWith2Hops( + [poolA1, poolA2].filter(Boolean) as RoutePoolData[], + [poolBase1, poolBase2].filter(Boolean) as RoutePoolData[], + [poolB1, poolB2].filter(Boolean) as RoutePoolData[], + tokenIn, + baseToken1, + baseToken2 + ) + ); + } + } + + return paths; + } + + public static async getBestPath( + paths: Path[], + amountIn: string, + blockchainName: EvmBlockchainName + ): Promise { + const pathAmountIn = BigNumber.from(amountIn); + const pathsWithAmount = await Promise.all( + paths.map(async (path, index) => ({ + pathWithAmounts: await SyncSwapRouter.calculatePathAmountsByInput( + path, + pathAmountIn, + false, + blockchainName + ), + index + })) + ); + const sortedIndexes = pathsWithAmount + .sort((next, prev) => { + if (!prev.pathWithAmounts) { + return next.pathWithAmounts?.amountOut ? -1 : 1; + } + if (!next.pathWithAmounts) { + return 1; + } + return prev.pathWithAmounts.amountOut.lte(next.pathWithAmounts.amountOut) ? 1 : -1; + }) + .map(path => path.index); + return paths.filter((_el, index) => sortedIndexes.slice(-4).includes(index)); + } + + private static getQuoteOutStable(params: GetAmountParams): BigNumber { + const multiplier = 10000; + + const adjustedReserveIn = params.reserveIn + .mul(params.tokenInPrecisionMultiplier!) + .mul(multiplier); + const adjustedReserveOut = params.reserveOut + .mul(params.tokenOutPrecisionMultiplier!) + .mul(multiplier); + + const amountIn = params.amount; + const feeDeductedAmountIn = amountIn.sub(amountIn.mul(params.swapFee).div(MAX_FEE)); + + const d = SyncSwapRouter.computeDFromAdjustedBalances( + STABLE_POOL_A, + adjustedReserveIn, + adjustedReserveOut, + false + ); + + const x = adjustedReserveIn.add( + feeDeductedAmountIn.mul(params.tokenInPrecisionMultiplier!) + ); + const y = SyncSwapRouter.getY(STABLE_POOL_A, x, d); + const dy = adjustedReserveOut.sub(y).sub(1); + + return dy.div(params.tokenOutPrecisionMultiplier!); + } + + private static getQuoteOutClassic(params: GetAmountParams): BigNumber { + const amountIn = params.amount; + const multiplier = 100000; + + const amountInWithFee = amountIn.mul(MAX_FEE.sub(params.swapFee)); + return amountInWithFee + .mul(params.reserveOut.mul(multiplier)) + .div(params.reserveIn.mul(multiplier).mul(MAX_FEE)); + } + + public static calculateQuoteOut(params: GetAmountParams): BigNumber { + if (params.amount.isZero()) { + return ZERO; + } + + return params.stable + ? SyncSwapPathFactory.getQuoteOutStable(params) + : SyncSwapPathFactory.getQuoteOutClassic(params); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-router.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-router.ts new file mode 100644 index 0000000..601ee0b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-router.ts @@ -0,0 +1,640 @@ +import { BigNumber } from 'ethers'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { Cache } from 'src/common/utils/decorators'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Injector } from 'src/core/injector/injector'; +import { syncSwapStablePool } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-stable-pool'; +import { + ETHER, + FOUR, + granularity, + MAX_FEE, + MAX_LOOP_LIMIT, + MAX_XP, + ONE, + STABLE_POOL_A, + THREE, + TWO, + UINT128_MAX, + UINT256_MAX, + ZERO +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/constants'; +import { SyncSwapPathFactory } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/sync-swap-path-factory'; +import { + BestPathsWithAmounts, + GetAmountParams, + GroupAmounts, + Path, + PathWithAmounts, + routeDirection, + RoutePoolData, + Step, + StepWithAmount +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/typings'; + +export class SyncSwapRouter { + public static async findBestAmountsForPathsExactIn( + paths: Path[], + amountInString: string, + blockchainName: EvmBlockchainName, + _ts?: number + ): Promise { + const amountIn = BigNumber.from(amountInString); + const pathAmounts: BigNumber[][] = await SyncSwapRouter.splitAmount(amountIn, paths.length); + + const groups: GroupAmounts[] = []; + const groupPromises: Promise[] = []; + + for (const amounts of pathAmounts) { + const promise = new Promise((resolve, reject) => { + SyncSwapRouter.calculateGroupAmounts(paths, amounts, blockchainName).then(group => { + if (group === null) { + reject(new Error('expired')); + } else { + groups.push(group); + resolve(true); + } + }); + }); + + groupPromises.push(promise); + } + + await Promise.all(groupPromises); + + let bestPathsWithAmounts: PathWithAmounts[] = []; + let bestAmountOut: BigNumber = ZERO; + let bestQuoteOut: BigNumber = ZERO; + let bestPriceImpact: BigNumber | null = null; + + for (const group of groups) { + const groupAmountOut = group.amountOut; + + if (!groupAmountOut.isZero() && !group.quoteOut.isZero()) { + const amountLoss = group.quoteOut.sub(groupAmountOut); + const groupPriceImpact = amountLoss.mul(ETHER).div(group.quoteOut); + + if (bestPriceImpact === null || groupPriceImpact.lt(bestPriceImpact)) { + bestPriceImpact = groupPriceImpact; + } + + if (groupAmountOut.gt(bestAmountOut)) { + bestPathsWithAmounts = group.pathsWithAmounts; + bestAmountOut = groupAmountOut; + bestQuoteOut = group.quoteOut; + } + } + } + + return { + found: bestAmountOut !== null, + direction: routeDirection.EXACT_IN, + pathsWithAmounts: bestPathsWithAmounts, + amountIn, + amountOut: bestAmountOut, + quote: bestQuoteOut, + bestPriceImpact, + gas: ZERO + }; + } + + private static copyStep(step: Step): Step { + const pool: RoutePoolData = step.pool; + + return { + pool: { + isAlpha: pool.isAlpha, + vault: pool.vault, + pool: pool.pool, + tokenA: pool.tokenA, + tokenB: pool.tokenB, + poolType: pool.poolType, + reserveA: BigNumber.from(pool.reserveA), + reserveB: BigNumber.from(pool.reserveB), + swapFeeAB: pool.swapFeeAB, + swapFeeBA: pool.swapFeeBA + }, + tokenIn: step.tokenIn, + swapFee: step.swapFee + }; + } + + private static getAmountOutClassic(params: GetAmountParams, checkOverflow: boolean): BigNumber { + const amountIn = params.amount; + const reserveIn = params.reserveIn; + if (checkOverflow && reserveIn.add(amountIn).gt(UINT128_MAX)) { + throw Error('overflow'); + } + + const amountInWithFee = amountIn.mul(MAX_FEE.sub(params.swapFee)); + if (checkOverflow && amountInWithFee.gt(UINT256_MAX)) { + throw Error('overflow'); + } + + const numerator = amountInWithFee.mul(params.reserveOut); + if (checkOverflow && numerator.gt(UINT256_MAX)) { + throw Error('overflow'); + } + + const denominator = params.reserveIn.mul(MAX_FEE).add(amountInWithFee); + if (checkOverflow && denominator.gt(UINT256_MAX)) { + throw Error('overflow'); + } + + return numerator.div(denominator); + } + + public static computeDFromAdjustedBalances( + A: BigNumber, + xp0: BigNumber, + xp1: BigNumber, + checkOverflow: boolean + ): BigNumber { + const s = xp0.add(xp1); + + if (s.isZero()) { + return ZERO; + } + let prevD; + let d = s; + const nA = A.mul(TWO); + + for (let i = 0; i < MAX_LOOP_LIMIT; i++) { + const dSq = d.mul(d); + + if (checkOverflow && dSq.gt(UINT256_MAX)) { + throw Error('overflow'); + } + + const d2 = dSq.div(xp0).mul(d); + if (checkOverflow && d2.gt(UINT256_MAX)) { + throw Error('overflow'); + } + + const dP = d2.div(xp1).div(FOUR); + prevD = d; + + const d0 = nA.mul(s).add(dP.mul(TWO)).mul(d); + if (checkOverflow && d0.gt(UINT256_MAX)) { + throw Error('overflow'); + } + + d = d0.div(nA.sub(ONE).mul(d).add(dP.mul(THREE))); + + if (d.sub(prevD).abs().lte(ONE)) { + return d; + } + } + + return d; + } + + public static getY(A: BigNumber, x: BigNumber, d: BigNumber): BigNumber { + const nA = A.mul(TWO); + + const c = d.mul(d).div(x.mul(TWO)).mul(d).div(nA.mul(TWO)); + + const b = d.div(nA).add(x); + + let yPrev; + let y = d; + + for (let i = 0; i < MAX_LOOP_LIMIT; i++) { + yPrev = y; + y = y.mul(y).add(c).div(y.mul(TWO).add(b).sub(d)); + + if (y.sub(yPrev).abs().lte(ONE)) { + break; + } + } + + return y; + } + + private static getAmountOutStable(params: GetAmountParams, checkOverflow: boolean): BigNumber { + const adjustedReserveIn = params.reserveIn.mul(params.tokenInPrecisionMultiplier!); + if (checkOverflow && adjustedReserveIn.gt(MAX_XP)) { + throw Error('overflow'); + } + const adjustedReserveOut = params.reserveOut.mul(params.tokenOutPrecisionMultiplier!); + if (checkOverflow && adjustedReserveOut.gt(MAX_XP)) { + throw Error('overflow'); + } + + const amountIn = params.amount; + const feeDeductedAmountIn = amountIn.sub(amountIn.mul(params.swapFee).div(MAX_FEE)); + const d = SyncSwapRouter.computeDFromAdjustedBalances( + STABLE_POOL_A, + adjustedReserveIn, + adjustedReserveOut, + checkOverflow + ); + + const x = adjustedReserveIn.add( + feeDeductedAmountIn.mul(params.tokenInPrecisionMultiplier!) + ); + const y = SyncSwapRouter.getY(STABLE_POOL_A, x, d); + const dy = adjustedReserveOut.sub(y).sub(1); + + return dy.div(params.tokenOutPrecisionMultiplier!); + } + + private static calculateAmountOut(params: GetAmountParams, checkOverflow: boolean): BigNumber { + if (params.amount.isZero()) { + return ZERO; + } + + let amountOut; + try { + if (params.stable) { + amountOut = SyncSwapRouter.getAmountOutStable(params, checkOverflow); + } else { + amountOut = SyncSwapRouter.getAmountOutClassic(params, checkOverflow); + } + } catch (error: unknown) { + return ZERO; + } + + return amountOut; + } + + private static splitAmounts2(amount: BigNumber, granularity: number): BigNumber[][] { + const oneSplit = amount.div(granularity); + if (oneSplit.isZero()) { + return []; + } + + const amounts: BigNumber[][] = []; + + for (let i = 0; i <= granularity; i++) { + const a = oneSplit.mul(i); + const b = oneSplit.mul(granularity - i); + + amounts.push([a, b]); + } + + return amounts; + } + + private static splitAmounts3(amount: BigNumber, granularity: number): BigNumber[][] { + const oneSplit = amount.div(granularity); + if (oneSplit.isZero()) { + return []; + } + + const amounts: BigNumber[][] = []; + + for (let i = 0; i <= granularity; i++) { + const a = oneSplit.mul(i); + + const remaining = granularity - i; + for (let j = 0; j <= remaining; j++) { + const b = oneSplit.mul(j); + const c = oneSplit.mul(remaining - j); + + amounts.push([a, b, c]); + } + } + + return amounts; + } + + private static splitAmounts4(amount: BigNumber, granularity: number): BigNumber[][] { + const oneSplit = amount.div(granularity); + if (oneSplit.isZero()) { + return []; + } + + const amounts: BigNumber[][] = []; + + for (let i = 0; i <= granularity; i++) { + const a = oneSplit.mul(i); + + const remaining = granularity - i; + for (let j = 0; j <= remaining; j++) { + const b = oneSplit.mul(j); + + const remaining2 = remaining - j; + for (let k = 0; k <= remaining2; k++) { + const c = oneSplit.mul(k); + const d = oneSplit.mul(remaining2 - k); + + amounts.push([a, b, c, d]); + } + } + } + + return amounts; + } + + private static splitAmounts5(amount: BigNumber, granularity: number): BigNumber[][] { + const oneSplit = amount.div(granularity); + if (oneSplit.isZero()) { + return []; + } + + const amounts: BigNumber[][] = []; + + for (let i = 0; i <= granularity; i++) { + const a = oneSplit.mul(i); + + const remaining = granularity - i; + for (let j = 0; j <= remaining; j++) { + const b = oneSplit.mul(j); + + const remaining2 = remaining - j; + for (let k = 0; k <= remaining2; k++) { + const c = oneSplit.mul(k); + + const remaining3 = remaining2 - k; + for (let l = 0; l <= remaining3; l++) { + const d = oneSplit.mul(l); + const e = oneSplit.mul(remaining3 - l); + + amounts.push([a, b, c, d, e]); + } + } + } + } + + return amounts; + } + + private static fixSplitAmounts(amount: BigNumber, amounts: BigNumber[][]): BigNumber[][] { + for (const group of amounts) { + let sum: BigNumber = ZERO; + + for (const amount of group) { + sum = sum.add(amount); + } + + if (!sum.eq(amount)) { + const diff: BigNumber = amount.sub(sum); + + for (const amount of group) { + // only add diff to non-zero amount + if (!amount.isZero()) { + group[0] = group[0]!.add(diff); + break; + } + } + } + } + + return amounts; + } + + private static async splitAmount(amount: BigNumber, parts: number): Promise { + if (parts === 0) { + return []; + } + + if (parts === 1) { + return [[amount]]; + } + + if (parts === 2) { + return SyncSwapRouter.fixSplitAmounts( + amount, + SyncSwapRouter.splitAmounts2(amount, granularity) + ); + } + + if (parts === 3) { + return SyncSwapRouter.fixSplitAmounts( + amount, + SyncSwapRouter.splitAmounts3(amount, granularity) + ); + } + + if (parts === 4) { + return SyncSwapRouter.fixSplitAmounts( + amount, + SyncSwapRouter.splitAmounts4(amount, granularity) + ); + } + + if (parts === 5) { + return SyncSwapRouter.fixSplitAmounts( + amount, + SyncSwapRouter.splitAmounts5(amount, granularity) + ); + } + + throw Error('Unsupported split parts'); + } + + private static async calculateAmountOutForStep( + step: Step, + amountIn: BigNumber, + quoteIn: BigNumber, + blockchainName: EvmBlockchainName + ): Promise<[BigNumber, BigNumber, Step | null]> { + const isTokenAIn = step.pool.tokenA === step.tokenIn; + const [reserveIn, reserveOut] = isTokenAIn + ? [step.pool.reserveA, step.pool.reserveB] + : [step.pool.reserveB, step.pool.reserveA]; + + let tokenInPrecisionMultiplier: BigNumber; + let tokenOutPrecisionMultiplier: BigNumber; + + // create multiplier for stable pools + const stable = step.pool.poolType === '2'; + if (stable) { + const [tokenInAddress] = isTokenAIn + ? [step.pool.tokenA, step.pool.tokenB] + : [step.pool.tokenB, step.pool.tokenA]; + const { fromPrecisionMultiplier, toPrecisionMultiplier } = await this.getPoolPrecision( + step.pool.pool, + tokenInAddress, + blockchainName + ); + + tokenInPrecisionMultiplier = BigNumber.from(fromPrecisionMultiplier); + tokenOutPrecisionMultiplier = BigNumber.from(toPrecisionMultiplier); + } else { + tokenInPrecisionMultiplier = ZERO; + tokenOutPrecisionMultiplier = ZERO; + } + + const swapFee = BigNumber.from(step.swapFee); + const amountOut = SyncSwapRouter.calculateAmountOut( + { + stable, + amount: amountIn, + reserveIn, + reserveOut, + swapFee, + tokenInPrecisionMultiplier, + tokenOutPrecisionMultiplier + }, + true + ); + + let quoteOut: BigNumber = ZERO; + let updatedStep: Step | null = null; + if (!amountOut.isZero()) { + updatedStep = SyncSwapRouter.copyStep(step); + if (isTokenAIn) { + updatedStep.pool.reserveA = step.pool.reserveA.add(amountIn); + updatedStep.pool.reserveB = step.pool.reserveB.sub(amountOut); + } else { + updatedStep.pool.reserveB = step.pool.reserveB.add(amountIn); + updatedStep.pool.reserveA = step.pool.reserveA.sub(amountOut); + } + + quoteOut = SyncSwapPathFactory.calculateQuoteOut({ + stable, + amount: quoteIn, + reserveIn, + reserveOut, + swapFee, + tokenInPrecisionMultiplier, + tokenOutPrecisionMultiplier + }); + } + + return [amountOut, quoteOut, updatedStep]; + } + + public static async calculatePathAmountsByInput( + path: Path, + amountIn: BigNumber, + _updateReserves: boolean, + blockchainName: EvmBlockchainName + ): Promise { + const stepsWithAmount: StepWithAmount[] = []; + let amountInNext: BigNumber = amountIn; + let quoteInNext: BigNumber = amountIn; + + for (let i = 0; i < path.length; i++) { + const step: Step = path[i]!; + + const [stepAmountOut, stepQuoteOut, updatedStep] = + // eslint-disable-next-line no-await-in-loop + await SyncSwapRouter.calculateAmountOutForStep( + step, + amountInNext, + quoteInNext, + blockchainName + ); + + if (stepAmountOut.isZero()) { + return null; + } + stepsWithAmount.push({ + ...step, + updatedStep, + amountIn: amountInNext + }); + + amountInNext = stepAmountOut; + quoteInNext = stepQuoteOut; + } + + const pathAmountOut = amountInNext; + const pathQuoteOut = quoteInNext; + + return { + direction: routeDirection.EXACT_IN, + stepsWithAmount, + amountOut: pathAmountOut, + amountIn, + quote: pathQuoteOut + }; + } + + private static async calculateGroupAmounts( + paths: Path[], + amounts: BigNumber[], + blockchainName: EvmBlockchainName + ): Promise { + const pathsWithAmounts: PathWithAmounts[] = []; + let amountOut: BigNumber = ZERO; + let quoteOut: BigNumber = ZERO; + const groupPaths = paths; + + const lastSteps: Map = new Map(); + + for (let i = 0; i < groupPaths.length; i++) { + const pathAmountIn: BigNumber = amounts[i]!; + if (pathAmountIn.isZero()) { + // eslint-disable-next-line no-continue + continue; + } + + const path: Path = groupPaths[i]!; + + for (let j = 0; j < path.length; j++) { + const lastStep: Step | undefined = lastSteps.get(path[j]!.pool.pool); + if (lastStep !== undefined) { + path[j] = SyncSwapRouter.copyStep(lastStep); + } + } + + const pathWithAmounts: PathWithAmounts | null = + // eslint-disable-next-line no-await-in-loop + await SyncSwapRouter.calculatePathAmountsByInput( + path, + pathAmountIn, + true, + blockchainName + ); + + if (pathWithAmounts != null) { + for (const step of pathWithAmounts.stepsWithAmount) { + if (step.updatedStep !== null) { + lastSteps.set(step.pool.pool, step.updatedStep); + } + } + + pathsWithAmounts.push(pathWithAmounts); + amountOut = amountOut.add(pathWithAmounts.amountOut); + quoteOut = quoteOut.add(pathWithAmounts.quote); + } + } + + return { + pathsWithAmounts, + amountOut, + quoteOut + }; + } + + @Cache + private static async getPoolPrecision( + address: string, + fromAddress: string, + blockchainName: EvmBlockchainName + ): Promise<{ fromPrecisionMultiplier: string; toPrecisionMultiplier: string }> { + const web3Public = Injector.web3PublicService.getWeb3Public(blockchainName); + + const token0 = await web3Public.callContractMethod( + address, + syncSwapStablePool, + 'token0', + [] + ); + const token0PM = await web3Public.callContractMethod( + address, + syncSwapStablePool, + 'token0PrecisionMultiplier', + [] + ); + const token1PM = await web3Public.callContractMethod( + address, + syncSwapStablePool, + 'token1PrecisionMultiplier', + [] + ); + return compareAddresses(token0, fromAddress) + ? { + fromPrecisionMultiplier: token0PM, + toPrecisionMultiplier: token1PM + } + : { + fromPrecisionMultiplier: token1PM, + toPrecisionMultiplier: token0PM + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/typings.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/typings.ts new file mode 100644 index 0000000..bde2743 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/utils/typings.ts @@ -0,0 +1,100 @@ +import { BigNumber } from 'ethers'; + +export interface RoutePools { + directPoolOptimal: RoutePoolData; + routeTokens: string[]; + tokenA: string; + tokenB: string; + timestamp: number; + pools: RoutePoolsBlockchain; +} + +export interface RoutePoolData { + isAlpha?: boolean; + vault?: string; + pool: string; + poolType: string; + reserveA: BigNumber; + reserveB: BigNumber; + swapFeeAB: string; + swapFeeBA: string; + tokenA: string; + tokenB: string; +} + +export interface RoutePoolsBlockchain { + poolsA: RoutePoolData[]; + poolsB: RoutePoolData[]; + poolsBase: RoutePoolData[]; + poolsDirect: RoutePoolData[]; +} + +export interface GroupAmounts { + pathsWithAmounts: PathWithAmounts[]; + amountOut: BigNumber; + quoteOut: BigNumber; +} + +export interface GetAmountParams { + stable: boolean; + amount: BigNumber; + reserveIn: BigNumber; + reserveOut: BigNumber; + swapFee: BigNumber; + tokenInPrecisionMultiplier?: BigNumber; + tokenOutPrecisionMultiplier?: BigNumber; +} + +export interface SwapRoute { + tokenIn: string; + tokenOut: string; + found: boolean; + amountIn: BigNumber; + amountOut: BigNumber; + quote: BigNumber; + paths: PathWithAmounts[]; + priceImpact: BigNumber; + gas: BigNumber; +} + +export interface Step { + pool: RoutePoolData; + swapFee: string; + // tokenA: string; + // tokenB: string; + tokenIn: string; +} + +export type Path = Step[]; + +export interface StepWithAmount extends Step { + amountIn: BigNumber; + updatedStep: Step | null; + // amountOut: BigNumber; +} + +export const routeDirection = { + EXACT_IN: 'EXACT_IN', + EXACT_OUT: 'EXACT_OUT' +} as const; + +export type RouteDirection = (typeof routeDirection)[keyof typeof routeDirection]; + +export interface PathWithAmounts { + direction: RouteDirection; + stepsWithAmount: StepWithAmount[]; + amountIn: BigNumber; + amountOut: BigNumber; + quote: BigNumber; +} + +export type BestPathsWithAmounts = { + found: boolean; + direction: RouteDirection; + pathsWithAmounts: PathWithAmounts[]; + amountIn: BigNumber; + amountOut: BigNumber; + quote: BigNumber; + bestPriceImpact: BigNumber | null; + gas: BigNumber; +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/EDDY_SWAP_METHOD.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/EDDY_SWAP_METHOD.ts new file mode 100644 index 0000000..7cfc9df --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/EDDY_SWAP_METHOD.ts @@ -0,0 +1,23 @@ +import { + EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD, + EXACT_OUTPUT_SWAP_METHOD, + ExactInputOutputSwapMethodsList, + RegularSwapMethodsList, + SwapMethodsList +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/SWAP_METHOD'; + +export const EDDY_EXACT_INPUT_REGULAR_SWAP_METHOD: RegularSwapMethodsList = { + TOKENS_TO_TOKENS: 'swapEddyTokensForTokens', + ETH_TO_TOKENS: 'swapEddyExactETHForTokens', + TOKENS_TO_ETH: 'swapEddyExactTokensForEth' +}; + +export const EDDY_EXACT_INPUT_SWAP_METHOD: SwapMethodsList = { + ...EDDY_EXACT_INPUT_REGULAR_SWAP_METHOD, + ...EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD +}; + +export const EDDY_SWAP_METHOD: ExactInputOutputSwapMethodsList = { + input: EDDY_EXACT_INPUT_SWAP_METHOD, + output: EXACT_OUTPUT_SWAP_METHOD +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/SWAP_METHOD.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/SWAP_METHOD.ts new file mode 100644 index 0000000..c3a9c56 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/SWAP_METHOD.ts @@ -0,0 +1,58 @@ +export type RegularSwapMethod = 'TOKENS_TO_TOKENS' | 'ETH_TO_TOKENS' | 'TOKENS_TO_ETH'; +export type SupportingFeeSwapMethod = + | 'TOKENS_TO_TOKENS_SUPPORTING_FEE' + | 'ETH_TO_TOKENS_SUPPORTING_FEE' + | 'TOKENS_TO_ETH_SUPPORTING_FEE'; + +export type RegularSwapMethodsList = Record; +export type SupportingFeeSwapMethodsList = Record; +export type SwapMethodsList = RegularSwapMethodsList & SupportingFeeSwapMethodsList; +export type ExactInputOutputSwapMethodsList = { + input: SwapMethodsList; + output: SwapMethodsList; +}; + +export const EXACT_INPUT_REGULAR_SWAP_METHOD: RegularSwapMethodsList = { + TOKENS_TO_TOKENS: 'swapExactTokensForTokens', + ETH_TO_TOKENS: 'swapExactETHForTokens', + TOKENS_TO_ETH: 'swapExactTokensForETH' +}; + +export const EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD: SupportingFeeSwapMethodsList = { + TOKENS_TO_TOKENS_SUPPORTING_FEE: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + ETH_TO_TOKENS_SUPPORTING_FEE: 'swapExactETHForTokensSupportingFeeOnTransferTokens', + TOKENS_TO_ETH_SUPPORTING_FEE: 'swapExactTokensForETHSupportingFeeOnTransferTokens' +}; + +export const EXACT_INPUT_SWAP_METHOD: SwapMethodsList = { + ...EXACT_INPUT_REGULAR_SWAP_METHOD, + ...EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD +}; + +export const EXACT_OUTPUT_REGULAR_SWAP_METHOD: RegularSwapMethodsList = { + TOKENS_TO_TOKENS: 'swapTokensForExactTokens', + ETH_TO_TOKENS: 'swapETHForExactTokens', + TOKENS_TO_ETH: 'swapTokensForExactETH' +}; + +export const EXACT_OUTPUT_SUPPORTING_FEE_SWAP_METHOD: SupportingFeeSwapMethodsList = { + TOKENS_TO_TOKENS_SUPPORTING_FEE: 'swapTokensForExactTokensSupportingFeeOnTransferTokens', + ETH_TO_TOKENS_SUPPORTING_FEE: 'swapETHForExactTokensSupportingFeeOnTransferTokens', + TOKENS_TO_ETH_SUPPORTING_FEE: 'swapTokensForExactETHSupportingFeeOnTransferTokens' +}; + +export const EXACT_OUTPUT_SWAP_METHOD: SwapMethodsList = { + ...EXACT_OUTPUT_REGULAR_SWAP_METHOD, + ...EXACT_OUTPUT_SUPPORTING_FEE_SWAP_METHOD +}; + +export const SWAP_METHOD: ExactInputOutputSwapMethodsList = { + input: EXACT_INPUT_SWAP_METHOD, + output: EXACT_OUTPUT_SWAP_METHOD +}; + +export const SUPPORTING_FEE_SWAP_METHODS_MAPPING = { + TOKENS_TO_TOKENS: 'TOKENS_TO_TOKENS_SUPPORTING_FEE', + ETH_TO_TOKENS: 'ETH_TO_TOKENS_SUPPORTING_FEE', + TOKENS_TO_ETH: 'TOKENS_TO_ETH_SUPPORTING_FEE' +} as const; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/default-estimated-gas.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/default-estimated-gas.ts new file mode 100644 index 0000000..d1e38b1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/default-estimated-gas.ts @@ -0,0 +1,23 @@ +import BigNumber from 'bignumber.js'; +import { DefaultEstimatedGas } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/default-estimated-gas'; + +export const defaultEstimatedGas: DefaultEstimatedGas = { + tokensToTokens: [ + new BigNumber(120_000), + new BigNumber(220_000), + new BigNumber(300_000), + new BigNumber(400_000) + ], + tokensToEth: [ + new BigNumber(150_000), + new BigNumber(240_000), + new BigNumber(320_000), + new BigNumber(400_000) + ], + ethToTokens: [ + new BigNumber(180_000), + new BigNumber(240_000), + new BigNumber(320_000), + new BigNumber(400_000) + ] +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/dex-trader/dex-trader-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/dex-trader/dex-trader-abi.ts new file mode 100644 index 0000000..128c622 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/dex-trader/dex-trader-abi.ts @@ -0,0 +1,336 @@ +import { AbiItem } from 'web3-utils'; + +export const DEX_TRADER_ABI = [ + { + inputs: [], + name: 'getAmountsIn', + outputs: [], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'getAmountsOut', + outputs: [], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapETHForExactTokens', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapExactETHForTokens', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapExactETHForTokensSupportingFeeOnTransferTokens', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapExactTokensForETHSupportingFeeOnTransferTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapExactTokensForTokens', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapExactTokensForETH', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountInMax', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapTokensForExactETH', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountInMax', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapTokensForExactTokens', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/dex-trader/dex-trader-swap-method.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/dex-trader/dex-trader-swap-method.ts new file mode 100644 index 0000000..171767e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/dex-trader/dex-trader-swap-method.ts @@ -0,0 +1,45 @@ +import { + ExactInputOutputSwapMethodsList, + RegularSwapMethodsList, + SupportingFeeSwapMethodsList, + SwapMethodsList +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/SWAP_METHOD'; + +export const ETH_EXACT_INPUT_REGULAR_SWAP_METHOD: RegularSwapMethodsList = { + TOKENS_TO_TOKENS: 'swapExactTokensForTokens', + ETH_TO_TOKENS: 'swapExactETHForTokens', + TOKENS_TO_ETH: 'swapExactTokensForETH' +}; + +export const ETH_EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD: SupportingFeeSwapMethodsList = { + TOKENS_TO_TOKENS_SUPPORTING_FEE: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + ETH_TO_TOKENS_SUPPORTING_FEE: 'swapExactETHForTokensSupportingFeeOnTransferTokens', + TOKENS_TO_ETH_SUPPORTING_FEE: 'swapExactTokensForETHSupportingFeeOnTransferTokens' +}; + +export const ETH_EXACT_INPUT_SWAP_METHOD: SwapMethodsList = { + ...ETH_EXACT_INPUT_REGULAR_SWAP_METHOD, + ...ETH_EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD +}; + +export const ETH_EXACT_OUTPUT_REGULAR_SWAP_METHOD: RegularSwapMethodsList = { + TOKENS_TO_TOKENS: 'swapTokensForExactTokens', + ETH_TO_TOKENS: 'swapETHForExactTokens', + TOKENS_TO_ETH: 'swapTokensForExactETH' +}; + +export const ETH_EXACT_OUTPUT_SUPPORTING_FEE_SWAP_METHOD: SupportingFeeSwapMethodsList = { + TOKENS_TO_TOKENS_SUPPORTING_FEE: 'swapTokensForExactTokensSupportingFeeOnTransferTokens', + ETH_TO_TOKENS_SUPPORTING_FEE: 'swapETHForExactTokensSupportingFeeOnTransferTokens', + TOKENS_TO_ETH_SUPPORTING_FEE: 'swapTokensForExactETHSupportingFeeOnTransferTokens' +}; + +export const ETH_EXACT_OUTPUT_SWAP_METHOD: SwapMethodsList = { + ...ETH_EXACT_OUTPUT_REGULAR_SWAP_METHOD, + ...ETH_EXACT_OUTPUT_SUPPORTING_FEE_SWAP_METHOD +}; + +export const ETH_SWAP_METHOD: ExactInputOutputSwapMethodsList = { + input: ETH_EXACT_INPUT_SWAP_METHOD, + output: ETH_EXACT_OUTPUT_SWAP_METHOD +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/uniswap-v2-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/uniswap-v2-abi.ts new file mode 100644 index 0000000..c493b2e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/uniswap-v2-abi.ts @@ -0,0 +1,483 @@ +import { AbiItem } from 'web3-utils'; + +export const defaultUniswapV2Abi = [ + { + inputs: [], + name: 'WETH', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'factory', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'reserveIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'reserveOut', + type: 'uint256' + } + ], + name: 'getAmountIn', + outputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + } + ], + stateMutability: 'pure', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'reserveIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'reserveOut', + type: 'uint256' + } + ], + name: 'getAmountOut', + outputs: [ + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256' + } + ], + stateMutability: 'pure', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + } + ], + name: 'getAmountsIn', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + } + ], + name: 'getAmountsOut', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountA', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'reserveA', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'reserveB', + type: 'uint256' + } + ], + name: 'quote', + outputs: [ + { + internalType: 'uint256', + name: 'amountB', + type: 'uint256' + } + ], + stateMutability: 'pure', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapETHForExactTokens', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapExactETHForTokens', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapExactETHForTokensSupportingFeeOnTransferTokens', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapExactTokensForETH', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapExactTokensForETHSupportingFeeOnTransferTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapExactTokensForTokens', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountInMax', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapTokensForExactETH', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountInMax', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapTokensForExactTokens', + outputs: [ + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]' + } + ], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/aerodrome-route-method-arguments.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/aerodrome-route-method-arguments.ts new file mode 100644 index 0000000..05de289 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/aerodrome-route-method-arguments.ts @@ -0,0 +1,3 @@ +export type AerodromeRoutePoolArgument = [string, string, boolean, string]; + +export type AerodromeRoutesMethodArgument = [string, AerodromeRoutePoolArgument[]]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/default-estimated-gas.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/default-estimated-gas.ts new file mode 100644 index 0000000..f3c71d0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/default-estimated-gas.ts @@ -0,0 +1,7 @@ +import BigNumber from 'bignumber.js'; + +export interface DefaultEstimatedGas { + tokensToTokens: BigNumber[]; + tokensToEth: BigNumber[]; + ethToTokens: BigNumber[]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/route-method-arguments.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/route-method-arguments.ts new file mode 100644 index 0000000..0dc4c93 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/route-method-arguments.ts @@ -0,0 +1,7 @@ +import { AerodromeRoutesMethodArgument } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/aerodrome-route-method-arguments'; + +export type DefaultRoutesMethodArgument = [string, string[]]; + +export type ExtendedRoutesMethodArguments = + | DefaultRoutesMethodArgument[] + | AerodromeRoutesMethodArgument[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-calculated-info.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-calculated-info.ts new file mode 100644 index 0000000..7cccc43 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-calculated-info.ts @@ -0,0 +1,11 @@ +import BigNumber from 'bignumber.js'; +import { UniswapRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-route'; + +export interface UniswapCalculatedInfo { + route: UniswapRoute; + estimatedGas?: BigNumber; +} + +export interface UniswapCalculatedInfoWithProfit extends UniswapCalculatedInfo { + profit: BigNumber; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-route.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-route.ts new file mode 100644 index 0000000..edb505e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-route.ts @@ -0,0 +1,9 @@ +import BigNumber from 'bignumber.js'; +import { Token } from 'src/common/tokens'; +import { AerodromeRoutePoolArgument } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/aerodrome-route-method-arguments'; + +export interface UniswapRoute { + readonly path: ReadonlyArray; + readonly outputAbsoluteAmount: BigNumber; + readonly routPoolInfo?: AerodromeRoutePoolArgument[]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-calculation-options.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-calculation-options.ts new file mode 100644 index 0000000..92221a1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-calculation-options.ts @@ -0,0 +1,7 @@ +import { RequiredOnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { MarkRequired } from 'ts-essentials'; + +export type UniswapV2CalculationOptions = MarkRequired< + RequiredOnChainCalculationOptions, + 'deadlineMinutes' | 'disableMultihops' +>; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration.ts new file mode 100644 index 0000000..a8c408d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration.ts @@ -0,0 +1,5 @@ +export interface UniswapV2ProviderConfiguration { + readonly maxTransitTokens: number; + readonly routingProvidersAddresses: string[]; + readonly wethAddress: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-trade-class.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-trade-class.ts new file mode 100644 index 0000000..c2b9924 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-trade-class.ts @@ -0,0 +1,8 @@ +import { AbstractConstructorParameters, Constructor } from 'src/common/utils/types'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; + +export type UniswapV2TradeClass = Constructor< + AbstractConstructorParameters, + T +> & + Omit; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-trade-struct.ts new file mode 100644 index 0000000..5e3336a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-trade-struct.ts @@ -0,0 +1,11 @@ +import { Token } from 'src/common/tokens'; +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { AerodromeRoutePoolArgument } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/aerodrome-route-method-arguments'; + +export interface UniswapV2TradeStruct extends EvmOnChainTradeStruct { + exact: Exact; + wrappedPath: ReadonlyArray | Token[]; + deadlineMinutes: number; + routPoolInfo: AerodromeRoutePoolArgument[] | undefined; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/path-factory.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/path-factory.ts new file mode 100644 index 0000000..ca8be7d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/path-factory.ts @@ -0,0 +1,305 @@ +import BigNumber from 'bignumber.js'; +import { InsufficientLiquidityError, RubicSdkError } from 'src/common/errors'; +import { PriceToken, Token } from 'src/common/tokens'; +import { Cache } from 'src/common/utils/decorators'; +import { notNull } from 'src/common/utils/object'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { BatchCall } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/batch-call'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { OnChainProxyFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { DefaultRoutesMethodArgument } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/route-method-arguments'; +import { + UniswapCalculatedInfo, + UniswapCalculatedInfoWithProfit +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-calculated-info'; +import { UniswapRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-route'; +import { UniswapV2CalculationOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-calculation-options'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; +import { UniswapV2TradeClass } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-trade-class'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { getFromToTokensAmountsByExact } from 'src/features/on-chain/calculation-manager/providers/dexes/common/utils/get-from-to-tokens-amounts-by-exact'; +import { hasLengthAtLeast } from 'src/features/on-chain/calculation-manager/utils/type-guards'; + +export interface PathFactoryStruct { + readonly from: PriceToken; + readonly to: PriceToken; + readonly weiAmount: BigNumber; + readonly exact: Exact; + readonly options: UniswapV2CalculationOptions; + readonly proxyFeeInfo: OnChainProxyFeeInfo | undefined; +} + +export interface UniswapV2AbstractProviderStruct { + readonly UniswapV2TradeClass: UniswapV2TradeClass; + readonly providerSettings: UniswapV2ProviderConfiguration; +} + +export class PathFactory { + private readonly web3Public: EvmWeb3Public; + + protected readonly from: PriceToken; + + protected readonly to: PriceToken; + + private readonly weiAmount: BigNumber; + + protected readonly exact: Exact; + + private readonly options: UniswapV2CalculationOptions; + + private readonly proxyFeeInfo: OnChainProxyFeeInfo | undefined; + + protected readonly UniswapV2TradeClass: UniswapV2TradeClass; + + protected readonly routingProvidersAddresses: ReadonlyArray; + + protected readonly maxTransitTokens: number; + + private get walletAddress(): string | undefined { + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(this.from.blockchain).address; + } + + @Cache + protected get stringWeiAmount(): string { + return this.weiAmount.toFixed(0); + } + + constructor( + uniswapProviderStruct: UniswapV2AbstractProviderStruct, + pathFactoryStruct: PathFactoryStruct + ) { + this.web3Public = Injector.web3PublicService.getWeb3Public( + pathFactoryStruct.from.blockchain + ); + + this.from = pathFactoryStruct.from; + this.to = pathFactoryStruct.to; + this.weiAmount = pathFactoryStruct.weiAmount; + this.exact = pathFactoryStruct.exact; + this.options = pathFactoryStruct.options; + this.UniswapV2TradeClass = uniswapProviderStruct.UniswapV2TradeClass; + this.routingProvidersAddresses = + uniswapProviderStruct.providerSettings.routingProvidersAddresses; + this.maxTransitTokens = pathFactoryStruct.options.disableMultihops + ? 0 + : uniswapProviderStruct.providerSettings.maxTransitTokens; + this.proxyFeeInfo = pathFactoryStruct.proxyFeeInfo; + } + + public async getAmountAndPath( + gasPriceInUsd: BigNumber | undefined + ): Promise { + const allRoutes = await this.getAllRoutes(); + const sortedRoutes = allRoutes + .filter(route => route.outputAbsoluteAmount.gt(0)) + .sort( + (a, b) => + b.outputAbsoluteAmount.comparedTo(a.outputAbsoluteAmount) * + (this.exact === 'input' ? 1 : -1) + ); + if (sortedRoutes.length === 0) { + throw new InsufficientLiquidityError(); + } + + if (this.options.gasCalculation === 'disabled') { + if (!hasLengthAtLeast(sortedRoutes, 1)) { + throw new RubicSdkError('Routes array length has to be bigger than 0'); + } + return { + route: sortedRoutes[0] + }; + } + + if ( + this.options.gasCalculation === 'rubicOptimisation' && + this.to.price?.isFinite() && + gasPriceInUsd + ) { + const gasLimits = this.getDefaultGases(sortedRoutes); + + if (this.walletAddress) { + const gasRequests = await Promise.all(this.getGasRequests(sortedRoutes)); + const estimatedGasLimits = await this.web3Public.batchEstimatedGas( + this.walletAddress, + gasRequests + ); + estimatedGasLimits.forEach((elem, index) => { + if (elem?.isFinite()) { + gasLimits[index] = elem; + } + }); + } + + const routesWithProfit: UniswapCalculatedInfoWithProfit[] = sortedRoutes.map( + (route, index) => { + const estimatedGas = gasLimits[index]; + if (!estimatedGas) { + throw new RubicSdkError('Estimated gas has to be defined'); + } + const gasFeeInUsd = estimatedGas.multipliedBy(gasPriceInUsd); + let profit: BigNumber; + if (this.exact === 'input') { + profit = Web3Pure.fromWei(route.outputAbsoluteAmount, this.to.decimals) + .multipliedBy(this.to.price) + .minus(gasFeeInUsd); + } else { + profit = Web3Pure.fromWei(route.outputAbsoluteAmount, this.from.decimals) + .multipliedBy(this.from.price) + .multipliedBy(-1) + .minus(gasFeeInUsd); + } + + return { + route, + estimatedGas, + profit + }; + } + ); + + const sortedByProfitRoutes = routesWithProfit.sort((a, b) => + b.profit.comparedTo(a.profit) + ); + + if (!sortedByProfitRoutes?.[0]) { + throw new RubicSdkError('Profit routes array length has to be bigger than 0'); + } + + return sortedByProfitRoutes[0]; + } + + let gasLimit = this.getDefaultGases(sortedRoutes.slice(0, 1))[0]; + + if (this.walletAddress) { + const callData = await this.getGasRequests(sortedRoutes.slice(0, 1))[0]; + if (!callData) { + throw new RubicSdkError('Call data has to be defined'); + } + const estimatedGas = ( + await this.web3Public.batchEstimatedGas(this.walletAddress, [callData]) + )[0]; + if (estimatedGas?.isFinite()) { + gasLimit = estimatedGas; + } + } + + if (!sortedRoutes?.[0]) { + throw new RubicSdkError('Routes length has to be bigger than 0'); + } + + return { + route: sortedRoutes[0], + estimatedGas: gasLimit + }; + } + + private getGasRequests(routes: UniswapRoute[]): Promise[] { + return this.getTradesByRoutes(routes).map(trade => trade.getEstimatedGasCallData()); + } + + private getDefaultGases(routes: UniswapRoute[]): BigNumber[] { + return this.getTradesByRoutes(routes).map(trade => trade.getDefaultEstimatedGas()); + } + + private getTradesByRoutes(routes: UniswapRoute[]): UniswapV2AbstractTrade[] { + return routes.map(route => { + const { from, to, fromWithoutFee } = getFromToTokensAmountsByExact( + this.from, + this.to, + this.exact, + this.weiAmount, + this.weiAmount, + route.outputAbsoluteAmount + ); + + return new this.UniswapV2TradeClass( + { + from, + to, + path: route.path, + routPoolInfo: route?.routPoolInfo, + wrappedPath: route.path, + exact: this.exact, + deadlineMinutes: this.options.deadlineMinutes, + slippageTolerance: this.options.slippageTolerance, + gasFeeInfo: null, + useProxy: this.options.useProxy, + proxyFeeInfo: this.proxyFeeInfo, + fromWithoutFee, + withDeflation: { from: { isDeflation: false }, to: { isDeflation: false } } + }, + EvmWeb3Pure.EMPTY_ADDRESS + ); + }); + } + + protected async getAllRoutes(): Promise { + const transitTokens = await Token.createTokens( + this.routingProvidersAddresses, + this.from.blockchain + ); + + const vertexes: Token[] = transitTokens.filter( + elem => !elem.isEqualTo(this.from) && !elem.isEqualTo(this.to) + ); + + const initialPath = [this.from]; + const routesPaths: Token[][] = []; + const routesMethodArguments: DefaultRoutesMethodArgument[] = []; + + const recGraphVisitor = (path: Token[], transitTokensLimit: number): void => { + if (path.length === transitTokensLimit + 1) { + const finalPath = path.concat(this.to); + routesPaths.push(finalPath); + routesMethodArguments.push([ + this.stringWeiAmount, + Token.tokensToAddresses(finalPath) + ]); + return; + } + + vertexes + .filter(vertex => path.every(token => !token.isEqualTo(vertex))) + .forEach(vertex => { + const extendedPath = path.concat(vertex); + recGraphVisitor(extendedPath, transitTokensLimit); + }); + }; + + for (let i = 0; i <= this.maxTransitTokens; i++) { + recGraphVisitor(initialPath, i); + } + + const responses = await this.UniswapV2TradeClass.callForRoutes( + this.from.blockchain, + this.exact, + routesMethodArguments + ); + + const tokens = responses.map((response, index) => { + if (!response.success || !response.output) { + return null; + } + const amounts = response.output; + + const numberAmount = this.exact === 'input' ? amounts[amounts.length - 1] : amounts[0]; + if (!numberAmount) { + throw new RubicSdkError('Amount has to be defined'); + } + const outputAbsoluteAmount = new BigNumber(numberAmount); + + const path = routesPaths?.[index]; + if (!path) { + throw new RubicSdkError('Path has to be defined'); + } + + return { outputAbsoluteAmount, path }; + }); + + return tokens.filter(notNull); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider.ts new file mode 100644 index 0000000..9908315 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider.ts @@ -0,0 +1,212 @@ +import BigNumber from 'bignumber.js'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { combineOptions } from 'src/common/utils/options'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { + createTokenNativeAddressProxy, + createTokenNativeAddressProxyInPathStartAndEnd +} from 'src/features/common/utils/token-native-address-proxy'; +import { OnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { OnChainProxyFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { getGasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info'; +import { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options'; +import { EvmOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/evm-on-chain-provider'; +import { GasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/models/gas-price-info'; +import { UniswapCalculatedInfo } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-calculated-info'; +import { UniswapV2CalculationOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-calculation-options'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; +import { UniswapV2TradeClass } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-trade-class'; +import { UniswapV2TradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-trade-struct'; +import { PathFactory } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/path-factory'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { getFromToTokensAmountsByExact } from 'src/features/on-chain/calculation-manager/providers/dexes/common/utils/get-from-to-tokens-amounts-by-exact'; + +export abstract class UniswapV2AbstractProvider< + T extends UniswapV2AbstractTrade = UniswapV2AbstractTrade +> extends EvmOnChainProvider { + /** @internal */ + public abstract readonly UniswapV2TradeClass: UniswapV2TradeClass; + + /** @internal */ + public abstract readonly providerSettings: UniswapV2ProviderConfiguration; + + public get type(): OnChainTradeType { + return this.UniswapV2TradeClass.type; + } + + protected readonly defaultOptions: UniswapV2CalculationOptions = { + ...evmProviderDefaultOptions, + deadlineMinutes: 20, + disableMultihops: false + }; + + public async calculate( + from: PriceTokenAmount, + to: PriceToken, + options?: OnChainCalculationOptions + ): Promise { + return this.calculateDifficultTrade(from, to, from.weiAmount, 'input', options); + } + + /** + * Calculates trade, based on amount, user wants to get. + * @param from Token to sell. + * @param to Token to get with output amount. + * @param options Additional options. + */ + public async calculateExactOutput( + from: PriceToken, + to: PriceTokenAmount, + options?: OnChainCalculationOptions + ): Promise { + return this.calculateDifficultTrade(from, to, to.weiAmount, 'output', options); + } + + /** + * Calculates input amount, based on amount, user wants to get. + * @param from Token to sell. + * @param to Token to get with output amount. + * @param options Additional options. + */ + public async calculateExactOutputAmount( + from: PriceToken, + to: PriceTokenAmount, + options?: OnChainCalculationOptions + ): Promise { + return (await this.calculateExactOutput(from, to, options)).from.tokenAmount; + } + + /** + * Calculates on-chain trade. + * @param fromToken Token to sell. + * @param toToken Token to get. + * @param weiAmount Amount to sell or to get in wei. + * @param exact Defines, whether to call 'exactInput' or 'exactOutput' method. + * @param options Additional options. + */ + public async calculateDifficultTrade( + fromToken: PriceToken, + toToken: PriceToken, + weiAmount: BigNumber, + exact: Exact, + options?: OnChainCalculationOptions + ): Promise { + const fullOptions = combineOptions(options, this.defaultOptions); + + if (fromToken.blockchain === BLOCKCHAIN_NAME.METIS && fromToken.isNative) { + fromToken = new PriceToken({ + ...fromToken.asStruct, + address: '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000' + }); + } + + if (toToken.blockchain === BLOCKCHAIN_NAME.METIS && toToken.isNative) { + toToken = new PriceToken({ + ...toToken.asStruct, + address: '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000' + }); + } + + let weiAmountWithoutFee = weiAmount; + let proxyFeeInfo: OnChainProxyFeeInfo | undefined; + if (fullOptions.useProxy) { + const proxyContractInfo = await this.handleProxyContract( + new PriceTokenAmount({ + ...fromToken.asStruct, + weiAmount + }), + fullOptions + ); + weiAmountWithoutFee = proxyContractInfo.fromWithoutFee.weiAmount; + proxyFeeInfo = proxyContractInfo.proxyFeeInfo; + } + + const fromProxy = createTokenNativeAddressProxy( + fromToken, + this.providerSettings.wethAddress + ); + const toProxy = createTokenNativeAddressProxy(toToken, this.providerSettings.wethAddress); + + let gasPriceInfo: GasPriceInfo | undefined; + if (fullOptions.gasCalculation !== 'disabled') { + try { + gasPriceInfo = await this.getGasPriceInfo(); + } catch {} + } + + const { route, estimatedGas } = await this.getAmountAndPath( + fromProxy, + toProxy, + weiAmountWithoutFee, + exact, + fullOptions, + proxyFeeInfo, + gasPriceInfo?.gasPriceInUsd + ); + + const { from, to, fromWithoutFee } = getFromToTokensAmountsByExact( + fromToken, + toToken, + exact, + weiAmount, + weiAmountWithoutFee, + route.outputAbsoluteAmount + ); + + const wrappedPath = route.path; + const routPoolInfo = route?.routPoolInfo; + const path = createTokenNativeAddressProxyInPathStartAndEnd( + wrappedPath, + EvmWeb3Pure.nativeTokenAddress + ); + const tradeStruct: UniswapV2TradeStruct = { + from, + to, + exact, + path, + routPoolInfo, + wrappedPath, + deadlineMinutes: fullOptions.deadlineMinutes, + slippageTolerance: fullOptions.slippageTolerance, + gasFeeInfo: null, + useProxy: fullOptions.useProxy, + proxyFeeInfo, + fromWithoutFee, + withDeflation: fullOptions.withDeflation, + usedForCrossChain: fullOptions.usedForCrossChain + }; + + if (fullOptions.gasCalculation === 'disabled') { + return new this.UniswapV2TradeClass(tradeStruct, fullOptions.providerAddress); + } + + const gasFeeInfo = getGasFeeInfo(estimatedGas, gasPriceInfo!); + return new this.UniswapV2TradeClass( + { ...tradeStruct, gasFeeInfo }, + fullOptions.providerAddress + ); + } + + protected async getAmountAndPath( + from: PriceToken, + to: PriceToken, + weiAmount: BigNumber, + exact: Exact, + options: UniswapV2CalculationOptions, + proxyFeeInfo: OnChainProxyFeeInfo | undefined, + gasPriceInUsd: BigNumber | undefined + ): Promise { + const pathFactory = new PathFactory(this, { + from, + to, + weiAmount, + exact, + options, + proxyFeeInfo + }); + return pathFactory.getAmountAndPath(gasPriceInUsd); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade.ts new file mode 100644 index 0000000..d03ccfc --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade.ts @@ -0,0 +1,314 @@ +import BigNumber from 'bignumber.js'; +import { LowSlippageDeflationaryTokenError, RubicSdkError } from 'src/common/errors'; +import { Token } from 'src/common/tokens'; +import { Cache } from 'src/common/utils/decorators'; +import { parseError } from 'src/common/utils/errors'; +import { tryExecuteAsync } from 'src/common/utils/functions'; +import { deadlineMinutesTimestamp } from 'src/common/utils/options'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { BatchCall } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/batch-call'; +import { ContractMulticallResponse } from 'src/core/blockchain/web3-public-service/web3-public/models/contract-multicall-response'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { defaultEstimatedGas } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/default-estimated-gas'; +import { + ExactInputOutputSwapMethodsList, + RegularSwapMethod, + SUPPORTING_FEE_SWAP_METHODS_MAPPING, + SWAP_METHOD +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/SWAP_METHOD'; +import { defaultUniswapV2Abi } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/uniswap-v2-abi'; +import { AerodromeRoutePoolArgument } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/aerodrome-route-method-arguments'; +import { DefaultEstimatedGas } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/default-estimated-gas'; +import { ExtendedRoutesMethodArguments } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/route-method-arguments'; +import { UniswapV2TradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-trade-struct'; +import { AbiItem } from 'web3-utils'; + +export abstract class UniswapV2AbstractTrade extends EvmOnChainTrade { + /** @internal */ + @Cache + public static getDexContractAddress(blockchain: BlockchainName): string { + // see https://github.com/microsoft/TypeScript/issues/34516 + // @ts-ignore + const instance = new this( + { + from: { blockchain }, + wrappedPath: [{ isNative: () => false }, { isNative: () => false }] + }, + false + ); + if (!instance.dexContractAddress) { + throw new RubicSdkError('Trying to read abstract class field'); + } + return instance.dexContractAddress; + } + + public static get type(): OnChainTradeType { + throw new RubicSdkError(`Static TRADE_TYPE getter is not implemented by ${this.name}`); + } + + /** @internal */ + public static readonly contractAbi: AbiItem[] = defaultUniswapV2Abi; + + /** @internal */ + public static readonly swapMethods: ExactInputOutputSwapMethodsList = SWAP_METHOD; + + /** @internal */ + public static readonly defaultEstimatedGasInfo: DefaultEstimatedGas = defaultEstimatedGas; + + public static callForRoutes( + blockchain: EvmBlockchainName, + exact: Exact, + routesMethodArguments: ExtendedRoutesMethodArguments + ): Promise[]> { + const web3Public = Injector.web3PublicService.getWeb3Public(blockchain); + const methodName = exact === 'input' ? 'getAmountsOut' : 'getAmountsIn'; + return web3Public.multicallContractMethod( + this.getDexContractAddress(blockchain), + this.contractAbi, + methodName, + routesMethodArguments + ); + } + + /** + * Deadline for transaction in minutes. + */ + public readonly deadlineMinutes: number; + + /** + * @internal + * Path with wrapped native address. + */ + public readonly wrappedPath: ReadonlyArray; + + public readonly routPoolInfo: AerodromeRoutePoolArgument[] | undefined; + + /** + * Defines, whether to call 'exactInput' or 'exactOutput' method. + */ + public readonly exact: Exact; + + public get type(): OnChainTradeType { + return (this.constructor as typeof UniswapV2AbstractTrade).type; + } + + protected get deadlineMinutesTimestamp(): number { + return deadlineMinutesTimestamp(this.deadlineMinutes); + } + + protected get nativeValueToSend(): string | undefined { + if (this.from.isNative) { + return this.getAmountInAndAmountOut().amountIn; + } + return '0'; + } + + private get regularSwapMethod(): string { + return (this.constructor).swapMethods[this.exact][ + this.regularSwapMethodKey + ]; + } + + private get supportedFeeSwapMethod(): string { + return (this.constructor).swapMethods[this.exact][ + SUPPORTING_FEE_SWAP_METHODS_MAPPING[this.regularSwapMethodKey] + ]; + } + + private get regularSwapMethodKey(): RegularSwapMethod { + if (this.from.isNative) { + return 'ETH_TO_TOKENS'; + } + if (this.to.isNative) { + return 'TOKENS_TO_ETH'; + } + return 'TOKENS_TO_TOKENS'; + } + + public constructor(tradeStruct: UniswapV2TradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this.deadlineMinutes = tradeStruct.deadlineMinutes; + this.exact = tradeStruct.exact; + this.wrappedPath = tradeStruct.wrappedPath; + this.routPoolInfo = tradeStruct.routPoolInfo; + } + + protected getAmountInAndAmountOut(): { amountIn: string; amountOut: string } { + let amountIn = this.fromWithoutFee.stringWeiAmount; + let amountOut = this.toTokenAmountMin.stringWeiAmount; + + if (this.exact === 'output') { + amountIn = this.fromWithoutFee.weiAmountPlusSlippage(this.slippageTolerance).toFixed(0); + amountOut = this.to.stringWeiAmount; + } + + return { amountIn, amountOut }; + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + await this.checkFromAddress(options.fromAddress, true); + await this.checkReceiverAddress(options.receiverAddress); + + if (options.supportFee === undefined) { + const needApprove = await this.needApprove(options.fromAddress); + if (needApprove) { + throw new RubicSdkError( + 'To use `encode` function, token must be approved for wallet' + ); + } + + try { + await this.checkBalance(); + } catch (_err) { + throw new RubicSdkError( + 'To use `encode` function, wallet must have enough balance or you must provider `supportFee` parameter in options.' + ); + } + } + + const methodName = await this.getMethodName( + options, + options.fromAddress, + options.supportFee + ); + const gasParams = this.getGasParams(options); + + const config = EvmWeb3Pure.encodeMethodCall( + this.dexContractAddress, + (this.constructor).contractAbi, + methodName, + this.getCallParameters(options.receiverAddress || options.fromAddress), + this.nativeValueToSend, + gasParams + ); + + return { tx: config, toAmount: this.to.stringWeiAmount }; + } + + protected getSwapError(err: Error & { code: number }): Error { + if (this.isDeflationError()) { + throw new LowSlippageDeflationaryTokenError(); + } + throw parseError(err); + } + + protected getCallParameters(receiverAddress?: string): unknown[] { + const { amountIn, amountOut } = this.getAmountInAndAmountOut(); + const amountParameters = this.from.isNative ? [amountOut] : [amountIn, amountOut]; + + return [ + ...amountParameters, + this.wrappedPath.map(t => t.address), + receiverAddress || this.walletAddress, + this.deadlineMinutesTimestamp + ]; + } + + protected async getMethodName( + options: SwapTransactionOptions, + fromAddress?: string, + supportFee?: boolean + ): Promise { + if (supportFee === false) { + return this.regularSwapMethod; + } + if (supportFee === true) { + return this.supportedFeeSwapMethod; + } + + const regularParameters = this.getSwapParametersByMethod(this.regularSwapMethod, options); + const supportedFeeParameters = this.getSwapParametersByMethod( + this.supportedFeeSwapMethod, + options + ); + + const regularMethodResult = await tryExecuteAsync( + this.web3Public.callContractMethod, + this.convertSwapParametersToCallParameters(regularParameters, fromAddress) + ); + + const feeMethodResult = await tryExecuteAsync( + this.web3Public.callContractMethod, + this.convertSwapParametersToCallParameters(supportedFeeParameters, fromAddress) + ); + + if (regularMethodResult.success) { + if (feeMethodResult.success) { + return this.regularSwapMethod; + } + throw new LowSlippageDeflationaryTokenError(); + } + + if (feeMethodResult.success) { + return this.supportedFeeSwapMethod; + } + + throw parseError(regularMethodResult.error); + } + + protected getSwapParametersByMethod( + method: string, + options: SwapTransactionOptions + ): Parameters['executeContractMethod']> { + const value = this.nativeValueToSend; + + return [ + this.dexContractAddress, + (this.constructor).contractAbi, + method, + this.getCallParameters(options?.receiverAddress), + { value } + ]; + } + + private convertSwapParametersToCallParameters( + parameters: Parameters['executeContractMethod']>, + fromAddress?: string + ): Parameters['callContractMethod']> { + return parameters.slice(0, 4).concat([ + { + from: fromAddress || this.walletAddress, + ...(parameters[4]?.value && { value: parameters[4]?.value }) + } + ]) as Parameters['callContractMethod']>; + } + + /** @internal */ + public async getEstimatedGasCallData(): Promise { + return this.encode({ fromAddress: this.walletAddress, supportFee: false }); + } + + /** @internal */ + public getDefaultEstimatedGas(): BigNumber { + const transitTokensNumber = this.wrappedPath.length - 2; + let methodName: keyof DefaultEstimatedGas = 'tokensToTokens'; + if (this.from.isNative) { + methodName = 'ethToTokens'; + } + if (this.to.isNative) { + methodName = 'tokensToEth'; + } + + const constructor = this.constructor; + const gasLimitAmount = + constructor.defaultEstimatedGasInfo[methodName]?.[transitTokensNumber]; + if (!gasLimitAmount) { + throw new RubicSdkError('Gas limit has to be defined'); + } + + const gasLimit = gasLimitAmount.toFixed(0); + return new BigNumber(gasLimit); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/constants/swap-router-contract-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/constants/swap-router-contract-abi.ts new file mode 100644 index 0000000..18830a6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/constants/swap-router-contract-abi.ts @@ -0,0 +1,109 @@ +import { AbiItem } from 'web3-utils'; + +export const UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS = '0xE592427A0AEce92De3Edee1F18E0157C05861564'; + +export const UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI = [ + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactInputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' }, + { internalType: 'uint160', name: 'sqrtPriceLimitX96', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactInputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactOutputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' }, + { internalType: 'uint160', name: 'sqrtPriceLimitX96', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactOutputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' }], + name: 'multicall', + outputs: [{ internalType: 'bytes[]', name: 'results', type: 'bytes[]' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountMinimum', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'unwrapWETH9', + outputs: [], + stateMutability: 'payable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-calculated-info.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-calculated-info.ts new file mode 100644 index 0000000..67b220d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-calculated-info.ts @@ -0,0 +1,12 @@ +import BigNumber from 'bignumber.js'; +import { UniswapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-route'; + +export interface UniswapV3CalculatedInfo { + route: UniswapV3Route; + estimatedGas?: BigNumber; +} + +export interface UniswapV3CalculatedInfoWithProfit extends UniswapV3CalculatedInfo { + estimatedGas: BigNumber; + profit: BigNumber; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-route.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-route.ts new file mode 100644 index 0000000..d43a7ea --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-route.ts @@ -0,0 +1,14 @@ +import { LiquidityPool } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/models/liquidity-pool'; +import { UniswapV3AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route'; + +export interface UniswapV3Route extends UniswapV3AlgebraRoute { + /** + * List of pools' contract addresses to use in a trade's route. + */ + poolsPath: LiquidityPool[]; + + /** + * From token address. + */ + initialTokenAddress: string; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration.ts new file mode 100644 index 0000000..36821b2 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration.ts @@ -0,0 +1,13 @@ +import { FeeAmount } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/models/liquidity-pool'; + +export interface UniswapV3RouterLiquidityPool { + poolAddress: string; + tokenSymbolA: TokenSymbol; + tokenSymbolB: TokenSymbol; + fee: FeeAmount; +} + +export interface UniswapV3RouterConfiguration { + readonly tokens: Record; + readonly liquidityPools: UniswapV3RouterLiquidityPool[]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-trade-class.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-trade-class.ts new file mode 100644 index 0000000..909f2fd --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-trade-class.ts @@ -0,0 +1,8 @@ +import { AbstractConstructorParameters, Constructor } from 'src/common/utils/types'; +import { UniswapV3AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-trade'; + +export type UniswapV3TradeClass = Constructor< + AbstractConstructorParameters, + T +> & + Omit; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-trade-struct.ts new file mode 100644 index 0000000..5f8b574 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-trade-struct.ts @@ -0,0 +1,6 @@ +import { UniswapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-route'; +import { UniswapV3AlgebraTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; + +export interface UniswapV3TradeStruct extends UniswapV3AlgebraTradeStruct { + route: UniswapV3Route; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-provider.ts new file mode 100644 index 0000000..c601937 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-provider.ts @@ -0,0 +1,83 @@ +import { RubicSdkError } from 'src/common/errors'; +import { Token } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { createTokenNativeAddressProxyInPathStartAndEnd } from 'src/features/common/utils/token-native-address-proxy'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { + UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI, + UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/constants/swap-router-contract-abi'; +import { UniswapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-route'; +import { UniswapV3RouterConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration'; +import { UniswapV3TradeClass } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-trade-class'; +import { UniswapV3AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-trade'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; +import { UniswapV3AlgebraTradeStructOmitPath } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UniswapV3AlgebraAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider'; + +export abstract class UniswapV3AbstractProvider< + T extends UniswapV3AbstractTrade = UniswapV3AbstractTrade +> extends UniswapV3AlgebraAbstractProvider { + public readonly contractAddress = UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI; + + protected abstract readonly OnChainTradeClass: UniswapV3TradeClass; + + protected abstract readonly routerConfiguration: UniswapV3RouterConfiguration; + + protected readonly isRubicOptimisationEnabled: boolean = false; + + protected abstract readonly quoterController: UniswapV3QuoterController; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.UNI_SWAP_V3; + } + + protected createTradeInstance( + tradeStruct: UniswapV3AlgebraTradeStructOmitPath, + route: UniswapV3Route, + providerAddress: string + ): T { + const path = this.extractPath(route); + return new this.OnChainTradeClass( + { + ...tradeStruct, + path, + route + }, + providerAddress + ); + } + + private extractPath(route: UniswapV3Route): ReadonlyArray { + const initialPool = route.poolsPath[0]; + if (!initialPool) { + throw new RubicSdkError('Initial pool has to be defined'); + } + const path: Token[] = [ + compareAddresses(initialPool.token0.address, route.initialTokenAddress) + ? initialPool.token0 + : initialPool.token1 + ]; + + const lastToken = path[path.length - 1]; + if (!lastToken) { + throw new RubicSdkError('Last token has to be defined'); + } + + route.poolsPath.forEach(pool => { + path.push( + !compareAddresses(pool.token0.address, lastToken.address) + ? pool.token0 + : pool.token1 + ); + }); + + return createTokenNativeAddressProxyInPathStartAndEnd(path, EvmWeb3Pure.nativeTokenAddress); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-trade.ts new file mode 100644 index 0000000..b3bda7c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-trade.ts @@ -0,0 +1,94 @@ +import { RubicSdkError } from 'src/common/errors'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { + UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI, + UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/constants/swap-router-contract-abi'; +import { UniswapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-route'; +import { UniswapV3TradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-trade-struct'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; +import { UnwrapWethMethodName } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/unwrapWethMethodName'; +import { UniswapV3AlgebraAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade'; + +export abstract class UniswapV3AbstractTrade extends UniswapV3AlgebraAbstractTrade { + public readonly dexContractAddress: string = UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI; + + protected readonly unwrapWethMethodName: UnwrapWethMethodName = 'unwrapWETH9'; + + public readonly route: UniswapV3Route; + + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.UNI_SWAP_V3; + } + + public constructor(tradeStruct: UniswapV3TradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this.route = tradeStruct.route; + } + + /** + * Returns swap `exactInput` method's name and arguments to use in Swap contract. + */ + protected getSwapRouterExactInputMethodData(walletAddress: string): MethodData { + const amountParams = this.getAmountParams(); + + if (this.route.poolsPath.length === 1) { + const methodName = this.exact === 'input' ? 'exactInputSingle' : 'exactOutputSingle'; + + const pool = this.route.poolsPath[0]; + if (!pool) { + throw new RubicSdkError('Initial pool has to be defined'); + } + const toTokenAddress = compareAddresses( + pool.token0.address, + this.route.initialTokenAddress + ) + ? pool.token1.address + : pool.token0.address; + + if (!this.route?.poolsPath?.[0]) { + throw new RubicSdkError('PoolsPath[0] has to be defined'); + } + + return { + methodName, + methodArguments: [ + [ + this.route.initialTokenAddress, + toTokenAddress, + this.route.poolsPath[0].fee, + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams, + 0 + ] + ] + }; + } + + const methodName = this.exact === 'input' ? 'exactInput' : 'exactOutput'; + + return { + methodName, + methodArguments: [ + [ + UniswapV3QuoterController.getEncodedPoolsPath( + this.route.poolsPath, + this.route.initialTokenAddress + ), + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams + ] + ] + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/constants/factory-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/constants/factory-contract-data.ts new file mode 100644 index 0000000..c04d415 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/constants/factory-contract-data.ts @@ -0,0 +1,17 @@ +import { AbiItem } from 'web3-utils'; + +export const FACTORY_CONTRACT_ADDRESS = '0x1F98431c8aD98523631AE4a59f267346ea31F984'; + +export const FACTORY_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'address', name: '', type: 'address' }, + { internalType: 'address', name: '', type: 'address' }, + { internalType: 'uint24', name: '', type: 'uint24' } + ], + name: 'getPool', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/constants/quoter-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/constants/quoter-contract-data.ts new file mode 100644 index 0000000..25c24ca --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/constants/quoter-contract-data.ts @@ -0,0 +1,52 @@ +import { AbiItem } from 'web3-utils'; + +export const UNISWAP_V3_QUOTER_CONTRACT_ADDRESS = '0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6'; + +export const UNISWAP_V3_QUOTER_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' } + ], + name: 'quoteExactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint160', name: 'sqrtPriceLimitX96', type: 'uint160' } + ], + name: 'quoteExactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' } + ], + name: 'quoteExactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint160', name: 'sqrtPriceLimitX96', type: 'uint160' } + ], + name: 'quoteExactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/fusionx-uniswap-v3-quoter-controller.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/fusionx-uniswap-v3-quoter-controller.ts new file mode 100644 index 0000000..f683726 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/fusionx-uniswap-v3-quoter-controller.ts @@ -0,0 +1,212 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { Token } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { Cache } from 'src/common/utils/decorators'; +import { notNull } from 'src/common/utils/object'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { UniswapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-route'; +import { UniswapV3RouterConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration'; +import { + FeeAmount, + LiquidityPool +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/models/liquidity-pool'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; +import { FUSIONX_UNISWAP_V3_FACTORY_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/constants/router-configuration'; +import { + FUSIONX_QUOTER_CONTRACT_ABI, + FUSIONX_QUOTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/utils/quoter-controller/constants/quoter-contract-data'; + +interface GetQuoterMethodsDataOptions { + routesLiquidityPools: LiquidityPool[]; + from: Token; + to: Token; + exact: Exact; + weiAmount: string; + maxTransitTokens: number; +} +export class FusionXUniswapV3QuoterController extends UniswapV3QuoterController { + protected readonly feeAmounts: FeeAmount[] = [100, 500, 10000]; + + constructor( + blockchain: EvmBlockchainName, + routerConfiguration: UniswapV3RouterConfiguration + ) { + super( + blockchain, + routerConfiguration, + FUSIONX_QUOTER_CONTRACT_ADDRESS, + FUSIONX_QUOTER_CONTRACT_ABI, + FUSIONX_UNISWAP_V3_FACTORY_CONTRACT_ADDRESS + ); + } + + /** + * Returns swap method's name and arguments to pass it to Quoter contract. + * @param poolsPath Pools, included in the route. + * @param from From token. + * @param to To token. + * @param exact Is exact input or output trade. + * @param weiAmount Amount of tokens to trade. + */ + @Cache + protected static getQuoterMethodData( + poolsPath: LiquidityPool[], + from: Token, + to: Token, + exact: Exact, + weiAmount: string + ): { + poolsPath: LiquidityPool[]; + methodData: MethodData; + } { + if (poolsPath.length === 1 && poolsPath?.[0]) { + const methodName = + exact === 'input' ? 'quoteExactInputSingle' : 'quoteExactOutputSingle'; + const sqrtPriceLimitX96 = 0; + return { + poolsPath, + methodData: { + methodName, + methodArguments: [ + from.address, + to.address, + weiAmount, + poolsPath[0].fee, + sqrtPriceLimitX96 + ] + } + }; + } + + const methodName = exact === 'input' ? 'quoteExactInput' : 'quoteExactOutput'; + const tokensPath = exact === 'input' ? poolsPath : poolsPath.reverse(); + const initialTokenAddress = exact === 'input' ? from.address : to.address; + return { + poolsPath, + methodData: { + methodName, + methodArguments: [ + UniswapV3QuoterController.getEncodedPoolsPath(tokensPath, initialTokenAddress), + weiAmount + ] + } + }; + } + + public async getAllRoutes( + from: Token, + to: Token, + exact: Exact, + weiAmount: string, + routeMaxTransitTokens: number + ): Promise { + const routesLiquidityPools = await this.getAllLiquidityPools(from, to); + const options: Omit = { + routesLiquidityPools, + from, + to, + exact, + weiAmount + }; + const quoterMethodsData = [...Array(routeMaxTransitTokens + 1)] + .map((_, maxTransitTokens) => + this.getQuoterMethodsData( + { + ...options, + maxTransitTokens + }, + [], + from.address + ) + ) + .flat(); + + const results = await this.web3Public.multicallContractMethods< + string | { returnedAmount: string } + >( + this.quoterContractAddress, + this.quoterContractABI, + quoterMethodsData.map(quoterMethodData => { + if (quoterMethodData.methodData.methodName.toLowerCase().includes('single')) { + return { + methodName: quoterMethodData.methodData.methodName, + methodArguments: [quoterMethodData.methodData.methodArguments] + }; + } + + return quoterMethodData.methodData; + }) + ); + + return results + .map((result, index) => { + const pool = quoterMethodsData?.[index]; + if (!pool) { + throw new RubicSdkError('Pool has to be defined'); + } + if (result.success) { + return { + outputAbsoluteAmount: new BigNumber( + result?.output! instanceof Object + ? result?.output?.returnedAmount + : result.output! + ), + poolsPath: pool.poolsPath, + initialTokenAddress: from.address + }; + } + return null; + }) + .filter(notNull); + } + + /** + * Returns swap methods' names and arguments, built with passed pools' addresses, to use it in Quoter contract. + */ + protected getQuoterMethodsData( + options: GetQuoterMethodsDataOptions, + path: LiquidityPool[], + lastTokenAddress: string + ): { poolsPath: LiquidityPool[]; methodData: MethodData }[] { + const { routesLiquidityPools, from, to, exact, weiAmount, maxTransitTokens } = options; + + if (path.length === maxTransitTokens) { + const pools = routesLiquidityPools.filter(pool => + pool.isPoolWithTokens(lastTokenAddress, to.address) + ); + return pools.map(pool => + FusionXUniswapV3QuoterController.getQuoterMethodData( + path.concat(pool), + from, + to, + exact, + weiAmount + ) + ); + } + + return routesLiquidityPools + .filter(pool => !path.includes(pool)) + .map(pool => { + const methodsData: { poolsPath: LiquidityPool[]; methodData: MethodData }[] = []; + if (compareAddresses(pool.token0.address, lastTokenAddress)) { + const extendedPath = path.concat(pool); + methodsData.push( + ...this.getQuoterMethodsData(options, extendedPath, pool.token1.address) + ); + } + if (compareAddresses(pool.token1.address, lastTokenAddress)) { + const extendedPath = path.concat(pool); + methodsData.push( + ...this.getQuoterMethodsData(options, extendedPath, pool.token0.address) + ); + } + return methodsData; + }) + .flat(); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/horizondex-uniswap-v3-quoter-controller.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/horizondex-uniswap-v3-quoter-controller.ts new file mode 100644 index 0000000..949dcbc --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/horizondex-uniswap-v3-quoter-controller.ts @@ -0,0 +1,210 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { Token } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { Cache } from 'src/common/utils/decorators'; +import { notNull } from 'src/common/utils/object'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { UniswapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-route'; +import { UniswapV3RouterConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration'; +import { + FeeAmount, + LiquidityPool +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/models/liquidity-pool'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; +import { + HORIZONDEX_QUOTER_CONTRACT_ABI, + HORIZONDEX_QUOTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/utils/quoter-controller/constants/quoter-contract-data'; + +interface GetQuoterMethodsDataOptions { + routesLiquidityPools: LiquidityPool[]; + from: Token; + to: Token; + exact: Exact; + weiAmount: string; + maxTransitTokens: number; +} +export class HorizondexUniswapV3QuoterController extends UniswapV3QuoterController { + protected readonly feeAmounts: FeeAmount[] = [8, 40, 300, 1000]; + + constructor( + blockchain: EvmBlockchainName, + routerConfiguration: UniswapV3RouterConfiguration + ) { + super( + blockchain, + routerConfiguration, + HORIZONDEX_QUOTER_CONTRACT_ADDRESS, + HORIZONDEX_QUOTER_CONTRACT_ABI + ); + } + + /** + * Returns swap method's name and arguments to pass it to Quoter contract. + * @param poolsPath Pools, included in the route. + * @param from From token. + * @param to To token. + * @param exact Is exact input or output trade. + * @param weiAmount Amount of tokens to trade. + */ + @Cache + protected static getQuoterMethodData( + poolsPath: LiquidityPool[], + from: Token, + to: Token, + exact: Exact, + weiAmount: string + ): { + poolsPath: LiquidityPool[]; + methodData: MethodData; + } { + if (poolsPath.length === 1 && poolsPath?.[0]) { + const methodName = + exact === 'input' ? 'quoteExactInputSingle' : 'quoteExactOutputSingle'; + const sqrtPriceLimitX96 = 0; + return { + poolsPath, + methodData: { + methodName, + methodArguments: [ + from.address, + to.address, + weiAmount, + poolsPath[0].fee, + sqrtPriceLimitX96 + ] + } + }; + } + + const methodName = exact === 'input' ? 'quoteExactInput' : 'quoteExactOutput'; + const tokensPath = exact === 'input' ? poolsPath : poolsPath.reverse(); + const initialTokenAddress = exact === 'input' ? from.address : to.address; + return { + poolsPath, + methodData: { + methodName, + methodArguments: [ + UniswapV3QuoterController.getEncodedPoolsPath(tokensPath, initialTokenAddress), + weiAmount + ] + } + }; + } + + public async getAllRoutes( + from: Token, + to: Token, + exact: Exact, + weiAmount: string, + routeMaxTransitTokens: number + ): Promise { + const routesLiquidityPools = await this.getAllLiquidityPools(from, to); + const options: Omit = { + routesLiquidityPools, + from, + to, + exact, + weiAmount + }; + const quoterMethodsData = [...Array(routeMaxTransitTokens + 1)] + .map((_, maxTransitTokens) => + this.getQuoterMethodsData( + { + ...options, + maxTransitTokens + }, + [], + from.address + ) + ) + .flat(); + + const results = await this.web3Public.multicallContractMethods< + string | { returnedAmount: string } + >( + this.quoterContractAddress, + this.quoterContractABI, + quoterMethodsData.map(quoterMethodData => { + if (quoterMethodData.methodData.methodName.toLowerCase().includes('single')) { + return { + methodName: quoterMethodData.methodData.methodName, + methodArguments: [quoterMethodData.methodData.methodArguments] + }; + } + + return quoterMethodData.methodData; + }) + ); + + return results + .map((result, index) => { + const pool = quoterMethodsData?.[index]; + if (!pool) { + throw new RubicSdkError('Pool has to be defined'); + } + if (result.success) { + return { + outputAbsoluteAmount: new BigNumber( + result?.output! instanceof Object + ? result?.output?.returnedAmount + : result.output! + ), + poolsPath: pool.poolsPath, + initialTokenAddress: from.address + }; + } + return null; + }) + .filter(notNull); + } + + /** + * Returns swap methods' names and arguments, built with passed pools' addresses, to use it in Quoter contract. + */ + protected getQuoterMethodsData( + options: GetQuoterMethodsDataOptions, + path: LiquidityPool[], + lastTokenAddress: string + ): { poolsPath: LiquidityPool[]; methodData: MethodData }[] { + const { routesLiquidityPools, from, to, exact, weiAmount, maxTransitTokens } = options; + + if (path.length === maxTransitTokens) { + const pools = routesLiquidityPools.filter(pool => + pool.isPoolWithTokens(lastTokenAddress, to.address) + ); + return pools.map(pool => + HorizondexUniswapV3QuoterController.getQuoterMethodData( + path.concat(pool), + from, + to, + exact, + weiAmount + ) + ); + } + + return routesLiquidityPools + .filter(pool => !path.includes(pool)) + .map(pool => { + const methodsData: { poolsPath: LiquidityPool[]; methodData: MethodData }[] = []; + if (compareAddresses(pool.token0.address, lastTokenAddress)) { + const extendedPath = path.concat(pool); + methodsData.push( + ...this.getQuoterMethodsData(options, extendedPath, pool.token1.address) + ); + } + if (compareAddresses(pool.token1.address, lastTokenAddress)) { + const extendedPath = path.concat(pool); + methodsData.push( + ...this.getQuoterMethodsData(options, extendedPath, pool.token0.address) + ); + } + return methodsData; + }) + .flat(); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/models/liquidity-pool.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/models/liquidity-pool.ts new file mode 100644 index 0000000..a6a97e2 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/models/liquidity-pool.ts @@ -0,0 +1,30 @@ +import { Token } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; + +export type FeeAmount = 8 | 40 | 100 | 300 | 500 | 1000 | 3000 | 10000; + +/** + * Represents liquidity pool in uni v3. + */ +export class LiquidityPool { + constructor( + public readonly address: string, + public readonly token0: Token, + public readonly token1: Token, + public readonly fee: FeeAmount + ) {} + + /** + * Checks if the pool contains passed tokens. + * @param tokenA First token address. + * @param tokenB Second token address. + */ + public isPoolWithTokens(tokenA: string, tokenB: string): boolean { + return ( + (compareAddresses(this.token0.address, tokenA) && + compareAddresses(this.token1.address, tokenB)) || + (compareAddresses(this.token1.address, tokenA) && + compareAddresses(this.token0.address, tokenB)) + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller.ts new file mode 100644 index 0000000..ded0b0a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller.ts @@ -0,0 +1,352 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { Token } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { Cache } from 'src/common/utils/decorators/cache-decorator/cache.decorator'; +import { notNull } from 'src/common/utils/object'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Public } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/evm-web3-public'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { UniswapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-route'; +import { UniswapV3RouterConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration'; +import { + FACTORY_CONTRACT_ABI, + FACTORY_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/constants/factory-contract-data'; +import { + UNISWAP_V3_QUOTER_CONTRACT_ABI, + UNISWAP_V3_QUOTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/constants/quoter-contract-data'; +import { + FeeAmount, + LiquidityPool +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/models/liquidity-pool'; +import { UniswapV3AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-quoter-controller'; +import { AbiItem } from 'web3-utils'; + +interface GetQuoterMethodsDataOptions { + routesLiquidityPools: LiquidityPool[]; + from: Token; + to: Token; + exact: Exact; + weiAmount: string; + maxTransitTokens: number; +} + +/** + * Works with requests, related to Uniswap v3 liquidity pools. + */ +export class UniswapV3QuoterController extends UniswapV3AlgebraQuoterController { + /** + * Converts uni v3 route to encoded bytes string to pass it to contract. + * Structure of encoded string: '0x${tokenAddress_0}${toHex(fee_0)}${tokenAddress_1}${toHex(fee_1)}...${tokenAddress_n}. + * toHex(fee_i) must be of length 6, so leading zeroes are added. + * @param pools Liquidity pools, included in route. + * @param initialTokenAddress From token address. + * @returns string Encoded string. + */ + @Cache + public static getEncodedPoolsPath(pools: LiquidityPool[], initialTokenAddress: string): string { + let contractPath = initialTokenAddress.slice(2).toLowerCase(); + let lastTokenAddress = initialTokenAddress; + pools.forEach(pool => { + contractPath += pool.fee.toString(16).padStart(6, '0'); + const newToken = compareAddresses(pool.token0.address, lastTokenAddress) + ? pool.token1 + : pool.token0; + contractPath += newToken.address.slice(2).toLowerCase(); + lastTokenAddress = newToken.address; + }); + return `0x${contractPath}`; + } + + /** + * Returns swap method's name and arguments to pass it to Quoter contract. + * @param poolsPath Pools, included in the route. + * @param from From token. + * @param to To token. + * @param exact Is exact input or output trade. + * @param weiAmount Amount of tokens to trade. + */ + @Cache + protected static getQuoterMethodData( + poolsPath: LiquidityPool[], + from: Token, + to: Token, + exact: Exact, + weiAmount: string + ): { + poolsPath: LiquidityPool[]; + methodData: MethodData; + } { + if (poolsPath.length === 1 && poolsPath?.[0]) { + const methodName = + exact === 'input' ? 'quoteExacМоtInputSingle' : 'quoteExactOutputSingle'; + const sqrtPriceLimitX96 = 0; + return { + poolsPath, + methodData: { + methodName, + methodArguments: [ + from.address, + to.address, + poolsPath[0].fee, + weiAmount, + sqrtPriceLimitX96 + ] + } + }; + } + + const methodName = exact === 'input' ? 'quoteExactInput' : 'quoteExactOutput'; + const tokensPath = exact === 'input' ? poolsPath : poolsPath.reverse(); + const initialTokenAddress = exact === 'input' ? from.address : to.address; + return { + poolsPath, + methodData: { + methodName, + methodArguments: [ + UniswapV3QuoterController.getEncodedPoolsPath(tokensPath, initialTokenAddress), + weiAmount + ] + } + }; + } + + private routerTokens: Token[] | undefined; + + private routerLiquidityPools: LiquidityPool[] | undefined; + + protected readonly feeAmounts: FeeAmount[] = [500, 3000, 10000]; + + protected get web3Public(): EvmWeb3Public { + return Injector.web3PublicService.getWeb3Public(this.blockchain); + } + + constructor( + protected readonly blockchain: EvmBlockchainName, + protected readonly routerConfiguration: UniswapV3RouterConfiguration, + protected readonly quoterContractAddress: string = UNISWAP_V3_QUOTER_CONTRACT_ADDRESS, + protected readonly quoterContractABI: AbiItem[] = UNISWAP_V3_QUOTER_CONTRACT_ABI, + protected readonly factoryAddress: string = FACTORY_CONTRACT_ADDRESS + ) { + super(); + } + + protected async getOrCreateRouterTokensAndLiquidityPools(): Promise<{ + routerTokens: Token[]; + routerLiquidityPools: LiquidityPool[]; + }> { + if (!this.routerTokens || !this.routerLiquidityPools) { + const tokens: Token[] = await Token.createTokens( + Object.values(this.routerConfiguration.tokens), + this.blockchain + ); + const liquidityPools: LiquidityPool[] = this.routerConfiguration.liquidityPools.map( + liquidityPool => { + const tokenA = tokens.find( + token => token.symbol === liquidityPool.tokenSymbolA + )!; + const tokenB = tokens.find( + token => token.symbol === liquidityPool.tokenSymbolB + )!; + return new LiquidityPool( + liquidityPool.poolAddress, + tokenA, + tokenB, + liquidityPool.fee + ); + } + ); + + this.routerTokens = tokens; + this.routerLiquidityPools = liquidityPools; + } + + return { + routerTokens: this.routerTokens, + routerLiquidityPools: this.routerLiquidityPools + }; + } + + /** + * Returns all liquidity pools, containing passed tokens addresses, and concatenates with most popular pools. + */ + @Cache({ + maxAge: 1000 * 60 * 10 + }) + protected async getAllLiquidityPools( + firstToken: Token, + secondToken: Token + ): Promise { + const { routerTokens, routerLiquidityPools } = + await this.getOrCreateRouterTokensAndLiquidityPools(); + + let getPoolsMethodArguments: { tokenA: Token; tokenB: Token; fee: FeeAmount }[] = []; + getPoolsMethodArguments.push( + ...Object.values(routerTokens) + .filter(routerToken => !routerToken.isEqualToTokens([firstToken, secondToken])) + .map(routerToken => + this.feeAmounts + .map(fee => [ + { tokenA: firstToken, tokenB: routerToken, fee }, + { tokenA: secondToken, tokenB: routerToken, fee } + ]) + .flat() + ) + .flat() + ); + getPoolsMethodArguments.push( + ...this.feeAmounts.map(fee => ({ + tokenA: firstToken, + tokenB: secondToken, + fee + })) + ); + getPoolsMethodArguments = getPoolsMethodArguments.filter( + methodArguments => + !routerLiquidityPools.find( + pool => + pool.isPoolWithTokens( + methodArguments.tokenA.address, + methodArguments.tokenB.address + ) && pool.fee === methodArguments.fee + ) + ); + + const poolsAddresses = ( + await this.web3Public.multicallContractMethod( + this.factoryAddress, + FACTORY_CONTRACT_ABI, + 'getPool', + getPoolsMethodArguments.map(methodArguments => [ + methodArguments.tokenA.address, + methodArguments.tokenB.address, + methodArguments.fee + ]) + ) + ).map(result => result.output!); + + return poolsAddresses + .map((poolAddress, index) => { + const poolMethodArguments = getPoolsMethodArguments?.[index]; + if (!poolMethodArguments) { + throw new RubicSdkError('Method arguments array for pool has to be defined'); + } + if (!EvmWeb3Pure.isEmptyAddress(poolAddress)) { + return new LiquidityPool( + poolAddress, + poolMethodArguments.tokenA, + poolMethodArguments.tokenB, + poolMethodArguments.fee + ); + } + return null; + }) + .filter(notNull) + .concat(routerLiquidityPools); + } + + public async getAllRoutes( + from: Token, + to: Token, + exact: Exact, + weiAmount: string, + routeMaxTransitTokens: number + ): Promise { + const routesLiquidityPools = await this.getAllLiquidityPools(from, to); + const options: Omit = { + routesLiquidityPools, + from, + to, + exact, + weiAmount + }; + const quoterMethodsData = [...Array(routeMaxTransitTokens + 1)] + .map((_, maxTransitTokens) => + this.getQuoterMethodsData( + { + ...options, + maxTransitTokens + }, + [], + from.address + ) + ) + .flat(); + + return this.web3Public + .multicallContractMethods( + this.quoterContractAddress, + this.quoterContractABI, + quoterMethodsData.map(quoterMethodData => quoterMethodData.methodData) + ) + .then(results => { + return results + .map((result, index) => { + const pool = quoterMethodsData?.[index]; + if (!pool) { + throw new RubicSdkError('Pool has to be defined'); + } + if (result.success) { + return { + outputAbsoluteAmount: new BigNumber(result.output!), + poolsPath: pool.poolsPath, + initialTokenAddress: from.address + }; + } + return null; + }) + .filter(notNull); + }); + } + + /** + * Returns swap methods' names and arguments, built with passed pools' addresses, to use it in Quoter contract. + */ + protected getQuoterMethodsData( + options: GetQuoterMethodsDataOptions, + path: LiquidityPool[], + lastTokenAddress: string + ): { poolsPath: LiquidityPool[]; methodData: MethodData }[] { + const { routesLiquidityPools, from, to, exact, weiAmount, maxTransitTokens } = options; + + if (path.length === maxTransitTokens) { + const pools = routesLiquidityPools.filter(pool => + pool.isPoolWithTokens(lastTokenAddress, to.address) + ); + return pools.map(pool => + UniswapV3QuoterController.getQuoterMethodData( + path.concat(pool), + from, + to, + exact, + weiAmount + ) + ); + } + + return routesLiquidityPools + .filter(pool => !path.includes(pool)) + .map(pool => { + const methodsData: { poolsPath: LiquidityPool[]; methodData: MethodData }[] = []; + if (compareAddresses(pool.token0.address, lastTokenAddress)) { + const extendedPath = path.concat(pool); + methodsData.push( + ...this.getQuoterMethodsData(options, extendedPath, pool.token1.address) + ); + } + if (compareAddresses(pool.token1.address, lastTokenAddress)) { + const extendedPath = path.concat(pool); + methodsData.push( + ...this.getQuoterMethodsData(options, extendedPath, pool.token0.address) + ); + } + return methodsData; + }) + .flat(); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/constants/estimated-gas.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/constants/estimated-gas.ts new file mode 100644 index 0000000..0cb115d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/constants/estimated-gas.ts @@ -0,0 +1,9 @@ +import BigNumber from 'bignumber.js'; + +export const WETH_TO_ETH_ESTIMATED_GAS = new BigNumber(50_000); + +export const DEFAULT_ESTIMATED_GAS = [ + new BigNumber(320_000), + new BigNumber(420_000), + new BigNumber(520_000) +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/create-trade-instance.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/create-trade-instance.ts new file mode 100644 index 0000000..e66d1aa --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/create-trade-instance.ts @@ -0,0 +1,9 @@ +import { UniswapV3AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route'; +import { UniswapV3AlgebraTradeStructOmitPath } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UniswapV3AlgebraAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade'; + +export type CreateTradeInstance = ( + tradeStruct: UniswapV3AlgebraTradeStructOmitPath, + route: UniswapV3AlgebraRoute, + providerAddress: string +) => UniswapV3AlgebraAbstractTrade; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-calculated-info.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-calculated-info.ts new file mode 100644 index 0000000..b6c7bc9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-calculated-info.ts @@ -0,0 +1,12 @@ +import BigNumber from 'bignumber.js'; +import { UniswapV3AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route'; + +export interface UniswapV3AlgebraCalculatedInfo { + route: UniswapV3AlgebraRoute; + estimatedGas?: BigNumber; +} + +export interface UniswapV3AlgebraCalculatedInfoWithProfit extends UniswapV3AlgebraCalculatedInfo { + estimatedGas: BigNumber; + profit: BigNumber; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-calculation-options.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-calculation-options.ts new file mode 100644 index 0000000..93af8da --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-calculation-options.ts @@ -0,0 +1,7 @@ +import { RequiredOnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { MarkRequired } from 'ts-essentials'; + +export type UniswapV3AlgebraCalculationOptions = MarkRequired< + RequiredOnChainCalculationOptions, + 'deadlineMinutes' | 'disableMultihops' +>; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration.ts new file mode 100644 index 0000000..cfdbb8d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration.ts @@ -0,0 +1,4 @@ +export interface UniswapV3AlgebraProviderConfiguration { + readonly wethAddress: string; + readonly maxTransitTokens: number; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-quoter-controller.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-quoter-controller.ts new file mode 100644 index 0000000..1993c6b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-quoter-controller.ts @@ -0,0 +1,29 @@ +import { PriceToken } from 'src/common/tokens'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { UniswapV3AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route'; +import { AbiItem } from 'web3-utils'; + +export abstract class UniswapV3AlgebraQuoterController { + protected abstract readonly quoterContractAddress: string; + + protected abstract readonly quoterContractABI: AbiItem[]; + + protected abstract readonly blockchain: EvmBlockchainName; + + /** + * Returns all routes between given tokens with output amount. + * @param from From token. + * @param to To token. + * @param exact Is exact input or output trade. + * @param weiAmount Amount of tokens to trade. + * @param routeMaxTransitTokens Max amount of transit tokens. + */ + public abstract getAllRoutes( + from: PriceToken, + to: PriceToken, + exact: Exact, + weiAmount: string, + routeMaxTransitTokens: number + ): Promise; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route.ts new file mode 100644 index 0000000..d9bae4b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route.ts @@ -0,0 +1,8 @@ +import BigNumber from 'bignumber.js'; + +export interface UniswapV3AlgebraRoute { + /** + * Resulting value in Wei. + */ + outputAbsoluteAmount: BigNumber; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct.ts new file mode 100644 index 0000000..aa791eb --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct.ts @@ -0,0 +1,9 @@ +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; + +export interface UniswapV3AlgebraTradeStruct extends EvmOnChainTradeStruct { + exact: Exact; + deadlineMinutes: number; +} + +export type UniswapV3AlgebraTradeStructOmitPath = Omit; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/unwrapWethMethodName.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/unwrapWethMethodName.ts new file mode 100644 index 0000000..b6b1568 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/unwrapWethMethodName.ts @@ -0,0 +1 @@ +export type UnwrapWethMethodName = 'unwrapWETH9' | 'unwrapWNativeToken' | 'unwrapWeth' | 'WETH9'; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider.ts new file mode 100644 index 0000000..7b26194 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider.ts @@ -0,0 +1,290 @@ +import BigNumber from 'bignumber.js'; +import { InsufficientLiquidityError, RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { combineOptions } from 'src/common/utils/options'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { createTokenNativeAddressProxy } from 'src/features/common/utils/token-native-address-proxy'; +import { OnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { OnChainProxyFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { getGasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info'; +import { CamelotArbitrumTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/camelot-arbitrum/camelot-arbitrum-trade'; +import { BerachainTestnetAlgebraTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/berachain-testnet/berachain-testnet-algebra/berachain-testnet-algebra-trade'; +import { BlastFenixTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/blast/fenix-blast/blast-fenix-trade'; +import { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options'; +import { EvmOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/evm-on-chain-provider'; +import { GasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/models/gas-price-info'; +import { UniswapV3TradeClass } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-trade-class'; +import { + UniswapV3AlgebraCalculatedInfo, + UniswapV3AlgebraCalculatedInfoWithProfit +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-calculated-info'; +import { UniswapV3AlgebraCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-calculation-options'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; +import { UniswapV3AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-quoter-controller'; +import { UniswapV3AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route'; +import { UniswapV3AlgebraTradeStructOmitPath } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UniswapV3AlgebraAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade'; +import { getFromToTokensAmountsByExact } from 'src/features/on-chain/calculation-manager/providers/dexes/common/utils/get-from-to-tokens-amounts-by-exact'; +import { ModeAlgebraTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/mode-algebra-trade'; +import { AlgebraTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/algebra-trade'; +import { QuickSwapV3Trade } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/quick-swap-v3-trade'; +import { QuickSwapV3PolygonZKEVMTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/quick-swap-v3-trade'; +import { UniSwapV3ScrollSepoliaTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/uni-swap-v3-scroll-sepolia-trade'; +import { AbiItem } from 'web3-utils'; + +import { AlgebraIntegralTrade } from '../../arthera-testnet/algebra-integral/algebra-integral-trade'; + +export abstract class UniswapV3AlgebraAbstractProvider< + T extends UniswapV3AlgebraAbstractTrade = UniswapV3AlgebraAbstractTrade +> extends EvmOnChainProvider { + protected abstract readonly contractAbi: AbiItem[]; + + protected abstract readonly contractAddress: string; + + protected abstract readonly OnChainTradeClass: + | UniswapV3TradeClass + | typeof AlgebraTrade + | typeof AlgebraIntegralTrade + | typeof QuickSwapV3Trade + | typeof QuickSwapV3PolygonZKEVMTrade + | typeof CamelotArbitrumTrade + | typeof UniSwapV3ScrollSepoliaTrade + | typeof BerachainTestnetAlgebraTrade + | typeof ModeAlgebraTrade + | typeof BlastFenixTrade; + + protected abstract readonly quoterController: UniswapV3AlgebraQuoterController; + + protected abstract readonly providerConfiguration: UniswapV3AlgebraProviderConfiguration; + + protected readonly isRubicOptimisationEnabled: boolean = false; + + protected readonly defaultOptions: UniswapV3AlgebraCalculationOptions = { + ...evmProviderDefaultOptions, + deadlineMinutes: 20, + disableMultihops: false + }; + + protected abstract createTradeInstance( + tradeStruct: UniswapV3AlgebraTradeStructOmitPath, + route: UniswapV3AlgebraRoute, + providerAddress: string + ): T; + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options?: OnChainCalculationOptions + ): Promise { + return this.calculateDifficultTrade(from, toToken, 'input', from.weiAmount, options); + } + + /** + * Calculates trade, based on amount, user wants to get. + * @param fromToken Token to sell. + * @param to Token to get with output amount. + * @param options Additional options. + */ + public async calculateExactOutput( + fromToken: PriceToken, + to: PriceTokenAmount, + options?: OnChainCalculationOptions + ): Promise { + return this.calculateDifficultTrade(fromToken, to, 'output', to.weiAmount, options); + } + + /** + * Calculates input amount, based on amount, user wants to get. + * @param fromToken Token to sell. + * @param to Token to get with output amount. + * @param options Additional options. + */ + public async calculateExactOutputAmount( + fromToken: PriceToken, + to: PriceTokenAmount, + options?: OnChainCalculationOptions + ): Promise { + return (await this.calculateExactOutput(fromToken, to, options)).from.tokenAmount; + } + + protected async calculateDifficultTrade( + fromToken: PriceToken, + toToken: PriceToken, + exact: Exact, + weiAmount: BigNumber, + options?: OnChainCalculationOptions + ): Promise { + const fullOptions = combineOptions(options, this.defaultOptions); + + let weiAmountWithoutFee = weiAmount; + let proxyFeeInfo: OnChainProxyFeeInfo | undefined; + if (fullOptions.useProxy) { + const proxyContractInfo = await this.handleProxyContract( + new PriceTokenAmount({ + ...fromToken.asStruct, + weiAmount + }), + fullOptions + ); + weiAmountWithoutFee = proxyContractInfo.fromWithoutFee.weiAmount; + proxyFeeInfo = proxyContractInfo.proxyFeeInfo; + } + + const fromClone = createTokenNativeAddressProxy( + fromToken, + this.providerConfiguration.wethAddress + ); + const toClone = createTokenNativeAddressProxy( + toToken, + this.providerConfiguration.wethAddress + ); + + let gasPriceInfo: GasPriceInfo | undefined; + if (fullOptions.gasCalculation !== 'disabled') { + try { + gasPriceInfo = await this.getGasPriceInfo(); + } catch {} + } + + const { route, estimatedGas } = await this.getRoute( + fromClone, + toClone, + exact, + weiAmountWithoutFee, + fullOptions, + gasPriceInfo?.gasPriceInUsd + ); + + const { from, to, fromWithoutFee } = getFromToTokensAmountsByExact( + fromToken, + toToken, + exact, + weiAmount, + weiAmountWithoutFee, + route.outputAbsoluteAmount + ); + + const tradeStruct: UniswapV3AlgebraTradeStructOmitPath = { + from, + to, + gasFeeInfo: null, + exact, + slippageTolerance: fullOptions.slippageTolerance, + deadlineMinutes: fullOptions.deadlineMinutes, + useProxy: fullOptions.useProxy, + proxyFeeInfo, + fromWithoutFee, + withDeflation: fullOptions.withDeflation, + usedForCrossChain: fullOptions.usedForCrossChain + }; + if (fullOptions.gasCalculation === 'disabled') { + return this.createTradeInstance(tradeStruct, route, fullOptions.providerAddress); + } + + const gasFeeInfo = getGasFeeInfo(estimatedGas, gasPriceInfo!); + return this.createTradeInstance( + { + ...tradeStruct, + gasFeeInfo + }, + route, + fullOptions.providerAddress + ); + } + + protected async getRoute( + from: PriceToken, + to: PriceToken, + exact: Exact, + weiAmount: BigNumber, + options: UniswapV3AlgebraCalculationOptions, + gasPriceInUsd?: BigNumber + ): Promise { + const routes = ( + await this.quoterController.getAllRoutes( + from, + to, + exact, + weiAmount.toFixed(0), + options.disableMultihops ? 0 : this.providerConfiguration.maxTransitTokens + ) + ).sort( + (a, b) => + b.outputAbsoluteAmount.comparedTo(a.outputAbsoluteAmount) * + (exact === 'input' ? 1 : -1) + ); + + if (routes.length === 0) { + throw new InsufficientLiquidityError(); + } + + if (options.gasCalculation === 'disabled' && routes?.[0]) { + return { + route: routes[0] + }; + } + + if ( + this.isRubicOptimisationEnabled && + options.gasCalculation === 'rubicOptimisation' && + to.price?.isFinite() && + gasPriceInUsd + ) { + const estimatedGasLimits = await this.OnChainTradeClass.estimateGasLimitForRoutes( + from, + to, + exact, + weiAmount, + options, + routes, + this.createTradeInstance + ); + + const calculatedProfits: UniswapV3AlgebraCalculatedInfoWithProfit[] = routes.map( + (route, index) => { + const estimatedGas = estimatedGasLimits[index]; + if (!estimatedGas) { + throw new RubicSdkError('Estimated gas has have to be defined'); + } + const gasFeeInUsd = gasPriceInUsd!.multipliedBy(estimatedGas); + const profit = Web3Pure.fromWei(route.outputAbsoluteAmount, to.decimals) + .multipliedBy(to.price) + .minus(gasFeeInUsd); + + return { + route, + estimatedGas, + profit + }; + } + ); + + const sortedRoutes = calculatedProfits.sort((a, b) => b.profit.comparedTo(a.profit))[0]; + if (!sortedRoutes) { + throw new RubicSdkError('Sorted routes have to be defined'); + } + + return sortedRoutes; + } + + const route = routes[0]; + if (!route) { + throw new RubicSdkError('Route has to be defined'); + } + const estimatedGas = await this.OnChainTradeClass.estimateGasLimitForRoute( + from, + to, + exact, + weiAmount, + options, + route, + this.createTradeInstance.bind(this) + ); + return { + route, + estimatedGas + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade.ts new file mode 100644 index 0000000..4c4add4 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade.ts @@ -0,0 +1,285 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { deadlineMinutesTimestamp } from 'src/common/utils/options'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { BatchCall } from 'src/core/blockchain/web3-public-service/web3-public/evm-web3-public/models/batch-call'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { OnChainTradeType } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { UniswapV3AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-trade'; +import { + DEFAULT_ESTIMATED_GAS, + WETH_TO_ETH_ESTIMATED_GAS +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/constants/estimated-gas'; +import { CreateTradeInstance } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/create-trade-instance'; +import { UniswapV3AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route'; +import { + UniswapV3AlgebraTradeStruct, + UniswapV3AlgebraTradeStructOmitPath +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UnwrapWethMethodName } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/unwrapWethMethodName'; +import { getFromToTokensAmountsByExact } from 'src/features/on-chain/calculation-manager/providers/dexes/common/utils/get-from-to-tokens-amounts-by-exact'; +import { AbiItem } from 'web3-utils'; + +interface EstimateGasOptions { + slippageTolerance: number; + deadlineMinutes: number; +} + +export abstract class UniswapV3AlgebraAbstractTrade extends EvmOnChainTrade { + public static get type(): OnChainTradeType { + throw new RubicSdkError(`Static TRADE_TYPE getter is not implemented by ${this.name}`); + } + + public static async estimateGasLimitForRoute( + fromToken: PriceToken, + toToken: PriceToken, + exact: Exact, + weiAmount: BigNumber, + options: EstimateGasOptions, + route: UniswapV3AlgebraRoute, + createTradeInstance: CreateTradeInstance + ): Promise { + const { from, to } = getFromToTokensAmountsByExact( + fromToken, + toToken, + exact, + weiAmount, + weiAmount, + route.outputAbsoluteAmount + ); + + const estimateGasParams = await this.getEstimateGasParams( + from, + to, + exact, + options, + route, + createTradeInstance + ); + let gasLimit = estimateGasParams.defaultGasLimit; + + const walletAddress = Injector.web3PrivateService.getWeb3PrivateByBlockchain( + from.blockchain + ).address; + if (walletAddress && estimateGasParams.callData) { + const web3Public = Injector.web3PublicService.getWeb3Public(fromToken.blockchain); + const estimatedGas = ( + await web3Public.batchEstimatedGas(walletAddress, [estimateGasParams.callData]) + )[0]; + if (estimatedGas?.isFinite()) { + gasLimit = estimatedGas; + } + } + + return gasLimit; + } + + public static async estimateGasLimitForRoutes( + fromToken: PriceToken, + toToken: PriceToken, + exact: Exact, + weiAmount: BigNumber, + options: EstimateGasOptions, + routes: UniswapV3AlgebraRoute[], + createTradeInstance: CreateTradeInstance + ): Promise { + const routesEstimateGasParams = await Promise.all( + routes.map(route => { + const { from, to } = getFromToTokensAmountsByExact( + fromToken, + toToken, + exact, + weiAmount, + weiAmount, + route.outputAbsoluteAmount + ); + return this.getEstimateGasParams( + from, + to, + exact, + options, + route, + createTradeInstance + ); + }) + ); + const gasLimits = routesEstimateGasParams.map( + estimateGasParams => estimateGasParams.defaultGasLimit + ); + + const walletAddress = Injector.web3PrivateService.getWeb3PrivateByBlockchain( + fromToken.blockchain + ).address; + if ( + walletAddress && + routesEstimateGasParams.every(estimateGasParams => estimateGasParams.callData) + ) { + const web3Public = Injector.web3PublicService.getWeb3Public(fromToken.blockchain); + const estimatedGasLimits = await web3Public.batchEstimatedGas( + walletAddress, + routesEstimateGasParams.map(estimateGasParams => estimateGasParams.callData!) + ); + estimatedGasLimits.forEach((elem, index) => { + if (elem?.isFinite()) { + gasLimits[index] = elem; + } + }); + } + + return gasLimits; + } + + private static getEstimateGasParams( + from: PriceTokenAmount, + to: PriceTokenAmount, + exact: Exact, + options: EstimateGasOptions, + route: UniswapV3AlgebraRoute, + createTradeInstance: CreateTradeInstance + ): Promise<{ + callData: BatchCall | null; + defaultGasLimit: BigNumber; + }> { + return createTradeInstance( + { + from, + to, + exact, + slippageTolerance: options.slippageTolerance, + deadlineMinutes: options.deadlineMinutes + } as UniswapV3AlgebraTradeStructOmitPath, + route, + EvmWeb3Pure.EMPTY_ADDRESS + ).getEstimateGasParams(); + } + + protected abstract readonly contractAbi: AbiItem[]; + + protected abstract readonly unwrapWethMethodName: UnwrapWethMethodName; + + protected readonly exact: Exact; + + public deadlineMinutes: number; + + public get type(): OnChainTradeType { + return (this.constructor).type; + } + + protected get deadlineMinutesTimestamp(): number { + return deadlineMinutesTimestamp(this.deadlineMinutes); + } + + private get defaultEstimatedGas(): BigNumber { + const estimatedGas = DEFAULT_ESTIMATED_GAS[this.path.length - 2]; + if (!estimatedGas) { + throw new RubicSdkError('Default estimated gas has to be defined'); + } + return estimatedGas.plus(this.to.isNative ? WETH_TO_ETH_ESTIMATED_GAS : 0); + } + + protected constructor(tradeStruct: UniswapV3AlgebraTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this.exact = tradeStruct.exact; + this.deadlineMinutes = tradeStruct.deadlineMinutes; + } + + protected getAmountParams(): [string, string] { + if (this.exact === 'input') { + const amountOutMin = this.to.weiAmountMinusSlippage(this.slippageTolerance).toFixed(0); + return [this.fromWithoutFee.stringWeiAmount, amountOutMin]; + } + + const amountInMax = this.fromWithoutFee + .weiAmountPlusSlippage(this.slippageTolerance) + .toFixed(0); + return [this.to.stringWeiAmount, amountInMax]; + } + + /** + * Returns swap `exactInput` method's name and arguments to use in `swap contract`. + */ + protected abstract getSwapRouterExactInputMethodData(walletAddress: string): MethodData; + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + await this.checkFromAddress(options.fromAddress, true); + await this.checkReceiverAddress(options.receiverAddress); + + const { methodName, methodArguments } = this.getSwapRouterMethodData( + options.receiverAddress || options.fromAddress + ); + const gasParams = this.getGasParams(options); + + const config = EvmWeb3Pure.encodeMethodCall( + this.dexContractAddress, + this.contractAbi, + methodName, + methodArguments, + this.fromWithoutFee.isNative ? this.fromWithoutFee.stringWeiAmount : '0', + gasParams + ); + + return { tx: config, toAmount: this.to.stringWeiAmount }; + } + + private getSwapRouterMethodData(fromAddress?: string): MethodData { + if (!this.to.isNative) { + const { methodName: exactInputMethodName, methodArguments: exactInputMethodArguments } = + this.getSwapRouterExactInputMethodData(fromAddress || this.walletAddress); + return { + methodName: exactInputMethodName, + methodArguments: exactInputMethodArguments + }; + } + + const { methodName: exactInputMethodName, methodArguments: exactInputMethodArguments } = + this.getSwapRouterExactInputMethodData(EvmWeb3Pure.EMPTY_ADDRESS); + const exactInputMethodEncoded = EvmWeb3Pure.encodeFunctionCall( + this.contractAbi, + exactInputMethodName, + exactInputMethodArguments + ); + + const amountOutMin = this.to.weiAmountMinusSlippage(this.slippageTolerance).toFixed(0); + const unwrapWETHMethodEncoded = EvmWeb3Pure.encodeFunctionCall( + this.contractAbi, + this.unwrapWethMethodName, + [amountOutMin, fromAddress || this.walletAddress] + ); + + return { + methodName: 'multicall', + methodArguments: [[exactInputMethodEncoded, unwrapWETHMethodEncoded]] + }; + } + + /** + * Returns encoded data of estimated gas function and default estimated gas. + */ + private async getEstimateGasParams(): Promise<{ + callData: BatchCall | null; + defaultGasLimit: BigNumber; + }> { + try { + const transactionConfig = await this.encode({ fromAddress: this.walletAddress }); + return { + callData: transactionConfig, + defaultGasLimit: this.defaultEstimatedGas + }; + } catch (_err) { + return { + callData: null, + defaultGasLimit: this.defaultEstimatedGas + }; + } + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/utils/get-from-to-tokens-amounts-by-exact.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/utils/get-from-to-tokens-amounts-by-exact.ts new file mode 100644 index 0000000..ca9ff39 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/utils/get-from-to-tokens-amounts-by-exact.ts @@ -0,0 +1,34 @@ +import BigNumber from 'bignumber.js'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { BlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; + +export function getFromToTokensAmountsByExact( + fromToken: PriceToken, + toToken: PriceToken, + exact: Exact, + initialWeiAmount: BigNumber, + weiAmountWithoutFee: BigNumber, + routeWeiAmount: BigNumber +): { + from: PriceTokenAmount; + to: PriceTokenAmount; + fromWithoutFee: PriceTokenAmount; +} { + const fromAmount = exact === 'input' ? initialWeiAmount : routeWeiAmount; + const toAmount = exact === 'output' ? initialWeiAmount : routeWeiAmount; + const from = new PriceTokenAmount({ + ...fromToken.asStruct, + weiAmount: fromAmount + }); + const to = new PriceTokenAmount({ + ...toToken.asStruct, + weiAmount: toAmount + }); + const fromWithoutFee = new PriceTokenAmount({ + ...fromToken.asStruct, + weiAmount: weiAmountWithoutFee + }); + + return { from, to, fromWithoutFee }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/constants/omni-pool-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/constants/omni-pool-abi.ts new file mode 100644 index 0000000..15b71bd --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/constants/omni-pool-abi.ts @@ -0,0 +1,38 @@ +import { AbiItem } from 'web3-utils'; + +export const omniPoolAbi: AbiItem[] = [ + { + inputs: [ + { + internalType: 'uint256', + name: '_fromAsset', + type: 'uint256' + }, + { + internalType: 'uint256', + name: '_toAsset', + type: 'uint256' + }, + { + internalType: 'int256', + name: '_fromAmount', + type: 'int256' + } + ], + name: 'quoteFrom', + outputs: [ + { + internalType: 'uint256', + name: 'actualToAmount', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'lpFeeAmount', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/constants/vooi-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/constants/vooi-abi.ts new file mode 100644 index 0000000..e34b3b6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/constants/vooi-abi.ts @@ -0,0 +1,21 @@ +import { AbiItem } from 'web3-utils'; + +export const vooiAbi: AbiItem[] = [ + { + inputs: [ + { internalType: 'uint256', name: '_fromID', type: 'uint256' }, + { internalType: 'uint256', name: '_toID', type: 'uint256' }, + { internalType: 'uint256', name: '_fromAmount', type: 'uint256' }, + { internalType: 'uint256', name: '_minToAmount', type: 'uint256' }, + { internalType: 'address', name: '_to', type: 'address' }, + { internalType: 'uint256', name: '_deadline', type: 'uint256' } + ], + name: 'swap', + outputs: [ + { internalType: 'uint256', name: 'actualToAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'lpFeeAmount', type: 'uint256' } + ], + stateMutability: 'nonpayable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/models/vooi-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/models/vooi-trade-struct.ts new file mode 100644 index 0000000..75b3059 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/models/vooi-trade-struct.ts @@ -0,0 +1,7 @@ +import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct'; + +export interface VooiTradeStruct extends EvmOnChainTradeStruct { + fromPoolId: number; + toPoolId: number; + deadlineMinutes: number; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/vooi-abstract-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/vooi-abstract-provider.ts new file mode 100644 index 0000000..34db37b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/vooi-abstract-provider.ts @@ -0,0 +1,125 @@ +import BigNumber from 'bignumber.js'; +import { RubicSdkError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { combineOptions, deadlineMinutesTimestamp } from 'src/common/utils/options'; +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Injector } from 'src/core/injector/injector'; +import { OnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { OnChainProxyFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options'; +import { EvmOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/evm-on-chain-provider'; +import { getFromToTokensAmountsByExact } from 'src/features/on-chain/calculation-manager/providers/dexes/common/utils/get-from-to-tokens-amounts-by-exact'; + +import { omniPoolAbi } from './constants/omni-pool-abi'; +import { VooiTradeStruct } from './models/vooi-trade-struct'; +import { VooiAbstractTrade } from './vooi-abstract-trade'; + +export abstract class VooiAbstractProvider< + T extends VooiAbstractTrade = VooiAbstractTrade +> extends EvmOnChainProvider { + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.VOOI; + } + + private readonly defaultOptions = { + ...evmProviderDefaultOptions, + deadlineMinutes: deadlineMinutesTimestamp(10) + }; + + protected abstract readonly omniPoolAddress: string; + + protected abstract readonly vooiPoolIdMapping: Record; + + protected abstract createTradeInstance( + tradeStruct: VooiTradeStruct, + providerAddress: string + ): T; + + public async calculate( + fromToken: PriceTokenAmount, + toToken: PriceToken, + options?: OnChainCalculationOptions + ): Promise { + const fromPoolId = this.vooiPoolIdMapping[fromToken.address.toLowerCase()]; + const toPoolId = this.vooiPoolIdMapping[toToken.address.toLowerCase()]; + + if (fromPoolId === undefined || toPoolId === undefined) { + throw new RubicSdkError('Vooi DEX supports only USDC.e, USDT, DAI token'); + } + + const fullOptions = combineOptions(options, this.defaultOptions); + + let weiAmountWithoutFee = fromToken.weiAmount; + let proxyFeeInfo: OnChainProxyFeeInfo | undefined; + if (fullOptions.useProxy) { + const proxyContractInfo = await this.handleProxyContract( + new PriceTokenAmount({ + ...fromToken.asStruct, + weiAmount: fromToken.weiAmount + }), + fullOptions + ); + weiAmountWithoutFee = proxyContractInfo.fromWithoutFee.weiAmount; + proxyFeeInfo = proxyContractInfo.proxyFeeInfo; + } + + // let gasPriceInfo: GasPriceInfo | undefined; + // if (fullOptions.gasCalculation !== 'disabled') { + // try { + // gasPriceInfo = await this.getGasPriceInfo(); + // } catch {} + // } + + const output = await this.getRoute(fromPoolId, toPoolId, weiAmountWithoutFee.toFixed()); + if (!output) { + throw new RubicSdkError('Can not estimate the route'); + } + + const { from, to, fromWithoutFee } = getFromToTokensAmountsByExact( + fromToken, + toToken, + 'input', + fromToken.weiAmount, + weiAmountWithoutFee, + output + ); + + const tradeStruct: VooiTradeStruct = { + from, + to, + fromPoolId, + toPoolId, + gasFeeInfo: null, + slippageTolerance: fullOptions.slippageTolerance, + deadlineMinutes: fullOptions.deadlineMinutes, + useProxy: fullOptions.useProxy, + proxyFeeInfo, + fromWithoutFee, + withDeflation: fullOptions.withDeflation, + usedForCrossChain: fullOptions.usedForCrossChain, + path: [from, to] + }; + + return this.createTradeInstance(tradeStruct, fullOptions.providerAddress); + + // if (fullOptions.gasCalculation === 'disabled') { + // return new VooiTrade(tradeStruct, fullOptions.providerAddress); + // } + + // const gasFeeInfo = getGasFeeInfo(estimatedGas, gasPriceInfo!); + // return new VooiTrade({ ...tradeStruct, gasFeeInfo }, fullOptions.providerAddress); + } + + private async getRoute(fromId: number, toId: number, fromAmount: string): Promise { + const web3 = Injector.web3PublicService.getWeb3Public(this.blockchain); + const result = await web3.callContractMethod<{ + actualToAmount: string; + lpFeeAmount: string; + }>(this.omniPoolAddress, omniPoolAbi, 'quoteFrom', [fromId, toId, fromAmount]); + return new BigNumber(result.actualToAmount); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/vooi-abstract-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/vooi-abstract-trade.ts new file mode 100644 index 0000000..9c9c392 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/common/vooi-abstract/vooi-abstract-trade.ts @@ -0,0 +1,63 @@ +import { deadlineMinutesTimestamp } from 'src/common/utils/options'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { EvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/evm-on-chain-trade'; + +import { vooiAbi } from './constants/vooi-abi'; +import { VooiTradeStruct } from './models/vooi-trade-struct'; + +export abstract class VooiAbstractTrade extends EvmOnChainTrade { + private fromPoolId: number; + + private toPoolId: number; + + private deadlineInMinutes: number; + + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.VOOI; + } + + public readonly type = ON_CHAIN_TRADE_TYPE.VOOI; + + public abstract readonly dexContractAddress: string; + + constructor(tradeStruct: VooiTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + this.fromPoolId = tradeStruct.fromPoolId; + this.toPoolId = tradeStruct.toPoolId; + this.deadlineInMinutes = tradeStruct.deadlineMinutes; + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + await this.checkFromAddress(options.fromAddress, true); + await this.checkReceiverAddress(options.receiverAddress); + const receiver = options?.receiverAddress || this.walletAddress; + + const gasParams = this.getGasParams(options); + + const config = EvmWeb3Pure.encodeMethodCall( + this.dexContractAddress, + vooiAbi, + 'swap', + [ + this.fromPoolId, + this.toPoolId, + this.from.stringWeiAmount, + this.toTokenAmountMin.stringWeiAmount, + receiver, + deadlineMinutesTimestamp(this.deadlineInMinutes) + ], + this.fromWithoutFee.isNative ? this.fromWithoutFee.stringWeiAmount : '0', + gasParams + ); + + return { tx: config, toAmount: this.to.stringWeiAmount }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/cro-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/cro-swap/constants.ts new file mode 100644 index 0000000..f6ededa --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/cro-swap/constants.ts @@ -0,0 +1,5 @@ +import { cronosProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/default-constants'; + +export const CRO_SWAP_CONTRACT_ADDRESS = '0x6c3A0E2E78848274B7E3346b8Ef8a4cBB2fEE2a9'; + +export const CRO_SWAP_PROVIDER_CONFIGURATION = cronosProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/cro-swap/cro-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/cro-swap/cro-swap-provider.ts new file mode 100644 index 0000000..0204d0c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/cro-swap/cro-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { CRO_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/cro-swap/constants'; +import { CroSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/cro-swap/cro-swap-trade'; + +export class CroSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.CRONOS; + + public readonly UniswapV2TradeClass = CroSwapTrade; + + public readonly providerSettings = CRO_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/cro-swap/cro-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/cro-swap/cro-swap-trade.ts new file mode 100644 index 0000000..a8287cd --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/cro-swap/cro-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { CRO_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/cro-swap/constants'; + +export class CroSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.CRO_SWAP; + } + + public readonly dexContractAddress = CRO_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crodex/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crodex/constants.ts new file mode 100644 index 0000000..b62bab1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crodex/constants.ts @@ -0,0 +1,5 @@ +import { cronosProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/default-constants'; + +export const CRODEX_CONTRACT_ADDRESS = '0xeC0A7a0C2439E8Cb67b992b12ecd020Ea943c7Be'; + +export const CRODEX_PROVIDER_CONFIGURATION = cronosProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crodex/crodex-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crodex/crodex-provider.ts new file mode 100644 index 0000000..018facd --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crodex/crodex-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { CRODEX_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/crodex/constants'; +import { CrodexTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/crodex/crodex-trade'; + +export class CrodexProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.CRONOS; + + public readonly UniswapV2TradeClass = CrodexTrade; + + public readonly providerSettings = CRODEX_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crodex/crodex-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crodex/crodex-trade.ts new file mode 100644 index 0000000..941648f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crodex/crodex-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { CRODEX_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/crodex/constants'; + +export class CrodexTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.CRO_DEX; + } + + public readonly dexContractAddress = CRODEX_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crona-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crona-swap/constants.ts new file mode 100644 index 0000000..f833f3a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crona-swap/constants.ts @@ -0,0 +1,5 @@ +import { cronosProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/default-constants'; + +export const CRONA_SWAP_CONTRACT_ADDRESS = '0xcd7d16fb918511bf7269ec4f48d61d79fb26f918'; + +export const CRONA_SWAP_PROVIDER_CONFIGURATION = cronosProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crona-swap/crona-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crona-swap/crona-swap-provider.ts new file mode 100644 index 0000000..c6ed450 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crona-swap/crona-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { CRONA_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/crona-swap/constants'; +import { CronaSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/crona-swap/crona-swap-trade'; + +export class CronaSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.CRONOS; + + public readonly UniswapV2TradeClass = CronaSwapTrade; + + public readonly providerSettings = CRONA_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crona-swap/crona-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crona-swap/crona-swap-trade.ts new file mode 100644 index 0000000..0cd5ca1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/crona-swap/crona-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { CRONA_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/cronos/crona-swap/constants'; + +export class CronaSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.CRONA_SWAP; + } + + public readonly dexContractAddress = CRONA_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/default-constants.ts new file mode 100644 index 0000000..1a62528 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/cronos/default-constants.ts @@ -0,0 +1,26 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultCronosRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.CRONOS]!.address, // WCRO + '0x66e428c3f67a68878562e79a0234c1f83c208770', // USDT + '0xc21223249CA28397B4B6541dfFaEcC539BfF0c59', // USDC + '0xfa9343c3897324496a05fc75abed6bac29f8a40f', // BNB, + '0x0e517979c2c1c1522ddb0c73905e0d39b3f990c0', // ADA + '0x1a8e39ae59e5556b56b76fcba98d22c9ae557396', // DOGE + '0xf78a326ACd53651F8dF5D8b137295e434B7c8ba5', // MATIC, + '0x062e66477faf219f25d27dced647bf57c3107d52', // WBTC + '0xf2001b145b43032aaf5ee2884e456ccd805f677d' // DAI +]; + +export const defaultCronosProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultCronosRoutingProvidersAddresses, + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.CRONOS]!.address +}; + +export const cronosProviderConfiguration: UniswapV2ProviderConfiguration = { + ...defaultCronosProviderConfiguration, + routingProvidersAddresses: defaultCronosRoutingProvidersAddresses +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/default-constants.ts new file mode 100644 index 0000000..6f62d10 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/default-constants.ts @@ -0,0 +1,15 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultDFKRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.DFK]!.address // WJEWEL +]; + +const defaultDFKWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.DFK]!.address; + +export const defaultDFKProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultDFKRoutingProvidersAddresses, + wethAddress: defaultDFKWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/trader-dfk/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/trader-dfk/constants.ts new file mode 100644 index 0000000..1f59544 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/trader-dfk/constants.ts @@ -0,0 +1,5 @@ +import { defaultDFKProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/default-constants'; + +export const TRADER_DFK_CONTRACT_ADDRESS = '0x3C351E1afdd1b1BC44e931E12D4E05D6125eaeCa'; + +export const TRADER_DFK_PROVIDER_CONFIGURATION = defaultDFKProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/trader-dfk/trader-dfk-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/trader-dfk/trader-dfk-provider.ts new file mode 100644 index 0000000..bc30b10 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/trader-dfk/trader-dfk-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { TRADER_DFK_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/trader-dfk/constants'; +import { TradeDFKSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/trader-dfk/trader-dfk-trade'; + +export class TradeDFKSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.DFK; + + public readonly UniswapV2TradeClass = TradeDFKSwapTrade; + + public readonly providerSettings = TRADER_DFK_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/trader-dfk/trader-dfk-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/trader-dfk/trader-dfk-trade.ts new file mode 100644 index 0000000..efadc77 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/trader-dfk/trader-dfk-trade.ts @@ -0,0 +1,20 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { DEX_TRADER_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/dex-trader/dex-trader-abi'; +import { ETH_SWAP_METHOD } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/dex-trader/dex-trader-swap-method'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { TRADER_DFK_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/defi-kingdoms/trader-dfk/constants'; + +export class TradeDFKSwapTrade extends UniswapV2AbstractTrade { + public static readonly contractAbi = DEX_TRADER_ABI; + + public static readonly swapMethods = ETH_SWAP_METHOD; + + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.TRADER; + } + + public readonly dexContractAddress = TRADER_DFK_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/default-constants.ts new file mode 100644 index 0000000..52a9968 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/default-constants.ts @@ -0,0 +1,19 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultEthereumPowRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.ETHEREUM_POW]!.address, // WETH + '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT + '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' // USDC +]; + +const defaultEthereumPowWethAddress = + wrappedNativeTokensList[BLOCKCHAIN_NAME.ETHEREUM_POW]!.address; + +export const defaultEthereumPowProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 1, + routingProvidersAddresses: defaultEthereumPowRoutingProvidersAddresses, + wethAddress: defaultEthereumPowWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/sushi-swap-ethereum-pow/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/sushi-swap-ethereum-pow/constants.ts new file mode 100644 index 0000000..194e74d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/sushi-swap-ethereum-pow/constants.ts @@ -0,0 +1,7 @@ +import { defaultEthereumPowProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/default-constants'; + +export const SUSHI_SWAP_ETHEREUM_POW_CONTRACT_ADDRESS = + '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'; + +export const SUSHI_SWAP_ETHEREUM_POW_PROVIDER_CONFIGURATION = + defaultEthereumPowProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/sushi-swap-ethereum-pow/sushi-swap-ethereum-pow-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/sushi-swap-ethereum-pow/sushi-swap-ethereum-pow-provider.ts new file mode 100644 index 0000000..fea8d5b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/sushi-swap-ethereum-pow/sushi-swap-ethereum-pow-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SUSHI_SWAP_ETHEREUM_POW_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/sushi-swap-ethereum-pow/constants'; +import { SushiSwapEthereumPowTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/sushi-swap-ethereum-pow/sushi-swap-ethereum-pow-trade'; + +export class SushiSwapEthereumPowProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ETHEREUM_POW; + + public readonly UniswapV2TradeClass = SushiSwapEthereumPowTrade; + + public readonly providerSettings = SUSHI_SWAP_ETHEREUM_POW_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/sushi-swap-ethereum-pow/sushi-swap-ethereum-pow-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/sushi-swap-ethereum-pow/sushi-swap-ethereum-pow-trade.ts new file mode 100644 index 0000000..ad602c6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/sushi-swap-ethereum-pow/sushi-swap-ethereum-pow-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { SUSHI_SWAP_ETHEREUM_POW_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/sushi-swap-ethereum-pow/constants'; + +export class SushiSwapEthereumPowTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SUSHI_SWAP; + } + + public readonly dexContractAddress = SUSHI_SWAP_ETHEREUM_POW_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v2-ethereum-pow/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v2-ethereum-pow/constants.ts new file mode 100644 index 0000000..bc077ee --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v2-ethereum-pow/constants.ts @@ -0,0 +1,10 @@ +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; +import { defaultEthereumPowProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/default-constants'; + +export const UNISWAP_V2_ETHEREUM_POW_CONTRACT_ADDRESS = + '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'; + +export const UNISWAP_V2_ETHEREUM_POW_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultEthereumPowProviderConfiguration, + maxTransitTokens: 2 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v2-ethereum-pow/uni-swap-v2-ethereum-pow-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v2-ethereum-pow/uni-swap-v2-ethereum-pow-provider.ts new file mode 100644 index 0000000..1a55421 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v2-ethereum-pow/uni-swap-v2-ethereum-pow-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { UNISWAP_V2_ETHEREUM_POW_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v2-ethereum-pow/constants'; +import { UniSwapV2EthereumPowTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v2-ethereum-pow/uni-swap-v2-ethereum-pow-trade'; + +export class UniSwapV2EthereumPowProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ETHEREUM_POW; + + public readonly UniswapV2TradeClass = UniSwapV2EthereumPowTrade; + + public readonly providerSettings = UNISWAP_V2_ETHEREUM_POW_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v2-ethereum-pow/uni-swap-v2-ethereum-pow-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v2-ethereum-pow/uni-swap-v2-ethereum-pow-trade.ts new file mode 100644 index 0000000..fca1d67 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v2-ethereum-pow/uni-swap-v2-ethereum-pow-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { UNISWAP_V2_ETHEREUM_POW_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v2-ethereum-pow/constants'; + +export class UniSwapV2EthereumPowTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.UNISWAP_V2; + } + + public readonly dexContractAddress = UNISWAP_V2_ETHEREUM_POW_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/constants/provider-configuration.ts new file mode 100644 index 0000000..e3c1e2f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/constants/provider-configuration.ts @@ -0,0 +1,9 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const UNI_SWAP_V3_ETHEREUM_POW_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = + { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.ETHEREUM_POW]!.address, + maxTransitTokens: 1 + }; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/constants/router-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/constants/router-configuration.ts new file mode 100644 index 0000000..62d19c9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/constants/router-configuration.ts @@ -0,0 +1,66 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { + UniswapV3RouterConfiguration, + UniswapV3RouterLiquidityPool +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration'; + +/** + * Most popular tokens in uni v3 to use in a route. + */ +const tokensSymbols = ['WETH', 'USDT', 'USDC', 'WBTC', 'DAI'] as const; + +type TokenSymbol = (typeof tokensSymbols)[number]; + +const routerTokens: Record = { + WETH: wrappedNativeTokensList[BLOCKCHAIN_NAME.ETHEREUM_POW]!.address, + USDT: '0xdac17f958d2ee523a2206206994597c13d831ec7', + USDC: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + WBTC: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + DAI: '0x6b175474e89094c44da98b954eedeac495271d0f' +}; + +const routerLiquidityPools: UniswapV3RouterLiquidityPool[] = [ + { + poolAddress: '0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8', + tokenSymbolA: 'USDC', + tokenSymbolB: 'WETH', + fee: 3000 + }, + { + poolAddress: '0x7858E59e0C01EA06Df3aF3D20aC7B0003275D4Bf', + tokenSymbolA: 'USDC', + tokenSymbolB: 'USDT', + fee: 500 + }, + { + poolAddress: '0xCBCdF9626bC03E24f779434178A73a0B4bad62eD', + tokenSymbolA: 'WBTC', + tokenSymbolB: 'WETH', + fee: 3000 + }, + { + poolAddress: '0x4e68Ccd3E89f51C3074ca5072bbAC773960dFa36', + tokenSymbolA: 'WETH', + tokenSymbolB: 'USDT', + fee: 3000 + }, + { + poolAddress: '0x6c6Bc977E13Df9b0de53b251522280BB72383700', + tokenSymbolA: 'DAI', + tokenSymbolB: 'USDC', + fee: 500 + }, + { + poolAddress: '0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640', + tokenSymbolA: 'USDC', + tokenSymbolB: 'WETH', + fee: 500 + } +]; + +export const UNI_SWAP_V3_ETHEREUM_POW_ROUTER_CONFIGURATION: UniswapV3RouterConfiguration = + { + tokens: routerTokens, + liquidityPools: routerLiquidityPools + }; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-provider.ts new file mode 100644 index 0000000..130e2b8 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-provider.ts @@ -0,0 +1,21 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-provider'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; +import { UNI_SWAP_V3_ETHEREUM_POW_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/constants/provider-configuration'; +import { UNI_SWAP_V3_ETHEREUM_POW_ROUTER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/constants/router-configuration'; +import { UniSwapV3EthereumPowTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-trade'; + +export class UniSwapV3EthereumPowProvider extends UniswapV3AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ETHEREUM_POW; + + public readonly OnChainTradeClass = UniSwapV3EthereumPowTrade; + + public readonly providerConfiguration = UNI_SWAP_V3_ETHEREUM_POW_PROVIDER_CONFIGURATION; + + public readonly routerConfiguration = UNI_SWAP_V3_ETHEREUM_POW_ROUTER_CONFIGURATION; + + protected readonly quoterController = new UniswapV3QuoterController( + this.blockchain, + this.routerConfiguration + ); +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-trade.ts new file mode 100644 index 0000000..daf69b8 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-trade.ts @@ -0,0 +1,3 @@ +import { UniswapV3AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-trade'; + +export class UniSwapV3EthereumPowTrade extends UniswapV3AbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/curve-ethereum/curve-ethereum-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/curve-ethereum/curve-ethereum-provider.ts new file mode 100644 index 0000000..10ca4e4 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/curve-ethereum/curve-ethereum-provider.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { CurveAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider'; +import { CurveEthereumTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/curve-ethereum/curve-ethereum-trade'; + +export class CurveEthereumProvider extends CurveAbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ETHEREUM; + + public readonly Trade = CurveEthereumTrade; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/curve-ethereum/curve-ethereum-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/curve-ethereum/curve-ethereum-trade.ts new file mode 100644 index 0000000..8f85f15 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/curve-ethereum/curve-ethereum-trade.ts @@ -0,0 +1,3 @@ +import { CurveAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade'; + +export class CurveEthereumTrade extends CurveAbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/default-constants.ts new file mode 100644 index 0000000..0c6bc22 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/default-constants.ts @@ -0,0 +1,18 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultEthereumRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.ETHEREUM]!.address, // WETH + '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT + '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' // USDC +]; + +const defaultEthereumWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.ETHEREUM]!.address; + +export const defaultEthereumProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 1, + routingProvidersAddresses: defaultEthereumRoutingProvidersAddresses, + wethAddress: defaultEthereumWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/pancake-router-ethereum/pancake-router-ethereum-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/pancake-router-ethereum/pancake-router-ethereum-provider.ts new file mode 100644 index 0000000..265006b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/pancake-router-ethereum/pancake-router-ethereum-provider.ts @@ -0,0 +1,20 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { PancakeRouterProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/pancake-router-provider'; +import { mainnet } from 'viem/chains'; + +export class PancakeRouterEthereumProvider extends PancakeRouterProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ETHEREUM; + + protected readonly chain = mainnet; + + protected readonly dexAddress = '0x13f4EA83D0bd40E75C8222255bc855a974568Dd4'; + + protected readonly v3subgraphAddress = + 'https://api.thegraph.com/subgraphs/name/pancakeswap/exchange-v3-bsc'; + + protected readonly v2subgraphAddress = 'https://proxy-worker-api.pancakeswap.com/bsc-exchange'; + + protected readonly maxHops = 2; + + protected readonly maxSplits = 3; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/constants.ts new file mode 100644 index 0000000..c8dbbd5 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/constants.ts @@ -0,0 +1,5 @@ +import { defaultEthereumProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/default-constants'; + +export const SUSHI_SWAP_ETHEREUM_CONTRACT_ADDRESS = '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'; + +export const SUSHI_SWAP_ETHEREUM_PROVIDER_CONFIGURATION = defaultEthereumProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/sushi-swap-ethereum-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/sushi-swap-ethereum-provider.ts new file mode 100644 index 0000000..b0e6f3a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/sushi-swap-ethereum-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SUSHI_SWAP_ETHEREUM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/constants'; +import { SushiSwapEthereumTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/sushi-swap-ethereum-trade'; + +export class SushiSwapEthereumProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ETHEREUM; + + public readonly UniswapV2TradeClass = SushiSwapEthereumTrade; + + public readonly providerSettings = SUSHI_SWAP_ETHEREUM_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/sushi-swap-ethereum-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/sushi-swap-ethereum-trade.ts new file mode 100644 index 0000000..0fef9aa --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/sushi-swap-ethereum-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { SUSHI_SWAP_ETHEREUM_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/sushi-swap-ethereum/constants'; + +export class SushiSwapEthereumTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SUSHI_SWAP; + } + + public readonly dexContractAddress = SUSHI_SWAP_ETHEREUM_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/constants.ts new file mode 100644 index 0000000..70f123f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/constants.ts @@ -0,0 +1,9 @@ +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; +import { defaultEthereumProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/default-constants'; + +export const UNISWAP_V2_ETHEREUM_CONTRACT_ADDRESS = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'; + +export const UNISWAP_V2_ETHEREUM_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultEthereumProviderConfiguration, + maxTransitTokens: 2 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/uni-swap-v2-ethereum-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/uni-swap-v2-ethereum-provider.ts new file mode 100644 index 0000000..c62d32e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/uni-swap-v2-ethereum-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { UNISWAP_V2_ETHEREUM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/constants'; +import { UniSwapV2EthereumTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/uni-swap-v2-ethereum-trade'; + +export class UniSwapV2EthereumProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ETHEREUM; + + public readonly UniswapV2TradeClass = UniSwapV2EthereumTrade; + + public readonly providerSettings = UNISWAP_V2_ETHEREUM_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/uni-swap-v2-ethereum-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/uni-swap-v2-ethereum-trade.ts new file mode 100644 index 0000000..893570f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/uni-swap-v2-ethereum-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { UNISWAP_V2_ETHEREUM_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v2-ethereum/constants'; + +export class UniSwapV2EthereumTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.UNISWAP_V2; + } + + public readonly dexContractAddress = UNISWAP_V2_ETHEREUM_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/constants/provider-configuration.ts new file mode 100644 index 0000000..fc7bfe3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/constants/provider-configuration.ts @@ -0,0 +1,8 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const UNI_SWAP_V3_ETHEREUM_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.ETHEREUM]!.address, + maxTransitTokens: 1 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/constants/router-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/constants/router-configuration.ts new file mode 100644 index 0000000..0b75ac4 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/constants/router-configuration.ts @@ -0,0 +1,66 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { + UniswapV3RouterConfiguration, + UniswapV3RouterLiquidityPool +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration'; + +/** + * Most popular tokens in uni v3 to use in a route. + */ +const tokensSymbols = ['WETH', 'USDT', 'USDC', 'WBTC', 'DAI'] as const; + +type TokenSymbol = (typeof tokensSymbols)[number]; + +const routerTokens: Record = { + WETH: wrappedNativeTokensList[BLOCKCHAIN_NAME.ETHEREUM]!.address, + USDT: '0xdac17f958d2ee523a2206206994597c13d831ec7', + USDC: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + WBTC: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + DAI: '0x6b175474e89094c44da98b954eedeac495271d0f' +}; + +const routerLiquidityPools: UniswapV3RouterLiquidityPool[] = [ + { + poolAddress: '0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8', + tokenSymbolA: 'USDC', + tokenSymbolB: 'WETH', + fee: 3000 + }, + { + poolAddress: '0x7858E59e0C01EA06Df3aF3D20aC7B0003275D4Bf', + tokenSymbolA: 'USDC', + tokenSymbolB: 'USDT', + fee: 500 + }, + { + poolAddress: '0xCBCdF9626bC03E24f779434178A73a0B4bad62eD', + tokenSymbolA: 'WBTC', + tokenSymbolB: 'WETH', + fee: 3000 + }, + { + poolAddress: '0x4e68Ccd3E89f51C3074ca5072bbAC773960dFa36', + tokenSymbolA: 'WETH', + tokenSymbolB: 'USDT', + fee: 3000 + }, + { + poolAddress: '0x6c6Bc977E13Df9b0de53b251522280BB72383700', + tokenSymbolA: 'DAI', + tokenSymbolB: 'USDC', + fee: 500 + }, + { + poolAddress: '0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640', + tokenSymbolA: 'USDC', + tokenSymbolB: 'WETH', + fee: 500 + } +]; + +export const UNI_SWAP_V3_ETHEREUM_ROUTER_CONFIGURATION: UniswapV3RouterConfiguration = + { + tokens: routerTokens, + liquidityPools: routerLiquidityPools + }; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/uni-swap-v3-ethereum-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/uni-swap-v3-ethereum-provider.ts new file mode 100644 index 0000000..3182ee0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/uni-swap-v3-ethereum-provider.ts @@ -0,0 +1,21 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-provider'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; +import { UNI_SWAP_V3_ETHEREUM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/constants/provider-configuration'; +import { UNI_SWAP_V3_ETHEREUM_ROUTER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/constants/router-configuration'; +import { UniSwapV3EthereumTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/uni-swap-v3-ethereum-trade'; + +export class UniSwapV3EthereumProvider extends UniswapV3AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ETHEREUM; + + public readonly OnChainTradeClass = UniSwapV3EthereumTrade; + + public readonly providerConfiguration = UNI_SWAP_V3_ETHEREUM_PROVIDER_CONFIGURATION; + + public readonly routerConfiguration = UNI_SWAP_V3_ETHEREUM_ROUTER_CONFIGURATION; + + protected readonly quoterController = new UniswapV3QuoterController( + this.blockchain, + this.routerConfiguration + ); +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/uni-swap-v3-ethereum-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/uni-swap-v3-ethereum-trade.ts new file mode 100644 index 0000000..3f13158 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/uni-swap-v3-ethereum-trade.ts @@ -0,0 +1,3 @@ +import { UniswapV3AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-trade'; + +export class UniSwapV3EthereumTrade extends UniswapV3AbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/verse/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/verse/constants.ts new file mode 100644 index 0000000..fd18a4f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/verse/constants.ts @@ -0,0 +1,5 @@ +import { defaultEthereumProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/default-constants'; + +export const VERSE_CONTRACT_ADDRESS = '0xB4B0ea46Fe0E9e8EAB4aFb765b527739F2718671'; + +export const VERSE_PROVIDER_CONFIGURATION = defaultEthereumProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/verse/verse-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/verse/verse-provider.ts new file mode 100644 index 0000000..30d2b38 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/verse/verse-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { VERSE_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/verse/constants'; +import { VerseTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/verse/verse-trade'; + +export class VerseProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ETHEREUM; + + public readonly UniswapV2TradeClass = VerseTrade; + + public readonly providerSettings = VERSE_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/verse/verse-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/verse/verse-trade.ts new file mode 100644 index 0000000..9fb8bef --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/ethereum/verse/verse-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { VERSE_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/verse/constants'; + +export class VerseTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.VERSE; + } + + public readonly dexContractAddress = VERSE_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/curve-fantom/curve-fantom-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/curve-fantom/curve-fantom-provider.ts new file mode 100644 index 0000000..27cfb51 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/curve-fantom/curve-fantom-provider.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { CurveAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider'; +import { CurveFantomTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/curve-fantom/curve-fantom-trade'; + +export class CurveFantomProvider extends CurveAbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.FANTOM; + + public readonly Trade = CurveFantomTrade; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/curve-fantom/curve-fantom-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/curve-fantom/curve-fantom-trade.ts new file mode 100644 index 0000000..21461de --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/curve-fantom/curve-fantom-trade.ts @@ -0,0 +1,3 @@ +import { CurveAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade'; + +export class CurveFantomTrade extends CurveAbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/default-constants.ts new file mode 100644 index 0000000..88e8867 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/default-constants.ts @@ -0,0 +1,21 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultFantomRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.FANTOM]!.address, // wFTM + '0x5cc61a78f164885776aa610fb0fe1257df78e59b', // SPIRIT + '0x04068da6c83afcfa0e13ba15a6696662335d5b75', // USDC + '0x049d68029688eAbF473097a2fC38ef61633A3C7A', // fUSDT + '0x82f0b8b456c1a451378467398982d4834b6829c1', // MIM + '0x321162Cd933E2Be498Cd2267a90534A804051b11', // wBTC + '0x74b23882a30290451a17c44f4f05243b6b58c76d' // wETH +]; + +const defaultFantomWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.FANTOM]!.address; + +export const defaultFantomProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultFantomRoutingProvidersAddresses, + wethAddress: defaultFantomWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/soul-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/soul-swap/constants.ts new file mode 100644 index 0000000..a720078 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/soul-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultFantomProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/default-constants'; + +export const SOUL_SWAP_CONTRACT_ADDRESS = '0x6b3d631B87FE27aF29efeC61d2ab8CE4d621cCBF'; + +export const SOUL_SWAP_PROVIDER_CONFIGURATION = defaultFantomProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/soul-swap/soul-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/soul-swap/soul-swap-provider.ts new file mode 100644 index 0000000..b39fc60 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/soul-swap/soul-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SOUL_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/soul-swap/constants'; +import { SoulSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/soul-swap/soul-swap-trade'; + +export class SoulSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.FANTOM; + + public readonly UniswapV2TradeClass = SoulSwapTrade; + + public readonly providerSettings = SOUL_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/soul-swap/soul-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/soul-swap/soul-swap-trade.ts new file mode 100644 index 0000000..66be450 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/soul-swap/soul-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { SOUL_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/soul-swap/constants'; + +export class SoulSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SOUL_SWAP; + } + + public readonly dexContractAddress = SOUL_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/constants.ts new file mode 100644 index 0000000..258e449 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultFantomProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/default-constants'; + +export const SPIRIT_SWAP_CONTRACT_ADDRESS = '0x16327E3FbDaCA3bcF7E38F5Af2599D2DDc33aE52'; + +export const SPIRIT_SWAP_PROVIDER_CONFIGURATION = defaultFantomProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/spirit-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/spirit-swap-provider.ts new file mode 100644 index 0000000..58a2f9a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/spirit-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SPIRIT_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/constants'; +import { SpiritSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/spirit-swap-trade'; + +export class SpiritSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.FANTOM; + + public readonly UniswapV2TradeClass = SpiritSwapTrade; + + public readonly providerSettings = SPIRIT_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/spirit-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/spirit-swap-trade.ts new file mode 100644 index 0000000..60a33a9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/spirit-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { SPIRIT_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/spirit-swap/constants'; + +export class SpiritSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SPIRIT_SWAP; + } + + public readonly dexContractAddress = SPIRIT_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/constants.ts new file mode 100644 index 0000000..5ed39df --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/constants.ts @@ -0,0 +1,21 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; +import { defaultFantomProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/default-constants'; + +export const SPOOKY_SWAP_CONTRACT_ADDRESS = '0xF491e7B69E4244ad4002BC14e878a34207E38c29'; + +const routingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.FANTOM]!.address, // wFTM + '0x8d11ec38a3eb5e956b052f67da8bdc9bef8abf3e', // DAI + '0x04068da6c83afcfa0e13ba15a6696662335d5b75', // USDC + '0x049d68029688eAbF473097a2fC38ef61633A3C7A', // fUSDT + '0x321162Cd933E2Be498Cd2267a90534A804051b11', // wBTC + '0x74b23882a30290451a17c44f4f05243b6b58c76d', // wETH + '0x841fad6eae12c286d1fd18d1d525dffa75c7effe' // BOO +]; + +export const SPOOKY_SWAP_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultFantomProviderConfiguration, + routingProvidersAddresses +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/spooky-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/spooky-swap-provider.ts new file mode 100644 index 0000000..4c82210 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/spooky-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SPOOKY_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/constants'; +import { SpookySwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/spooky-swap-trade'; + +export class SpookySwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.FANTOM; + + public readonly UniswapV2TradeClass = SpookySwapTrade; + + public readonly providerSettings = SPOOKY_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/spooky-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/spooky-swap-trade.ts new file mode 100644 index 0000000..48fe10b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/spooky-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { SPOOKY_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/spooky-swap/constants'; + +export class SpookySwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SPOOKY_SWAP; + } + + public readonly dexContractAddress = SPOOKY_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/constants.ts new file mode 100644 index 0000000..b61f2ac --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/constants.ts @@ -0,0 +1,5 @@ +import { defaultFantomProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/default-constants'; + +export const SUSHI_SWAP_FANTOM_CONTRACT_ADDRESS = '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'; + +export const SUSHI_SWAP_FANTOM_PROVIDER_CONFIGURATION = defaultFantomProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/sushi-swap-fantom-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/sushi-swap-fantom-provider.ts new file mode 100644 index 0000000..1661dde --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/sushi-swap-fantom-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SUSHI_SWAP_FANTOM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/constants'; +import { SushiSwapFantomTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/sushi-swap-fantom-trade'; + +export class SushiSwapFantomProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.FANTOM; + + public readonly UniswapV2TradeClass = SushiSwapFantomTrade; + + public readonly providerSettings = SUSHI_SWAP_FANTOM_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/sushi-swap-fantom-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/sushi-swap-fantom-trade.ts new file mode 100644 index 0000000..29bd307 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/sushi-swap-fantom-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { SUSHI_SWAP_FANTOM_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/fantom/sushi-swap-fantom/constants'; + +export class SushiSwapFantomTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SUSHI_SWAP; + } + + public readonly dexContractAddress = SUSHI_SWAP_FANTOM_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/default-constants.ts new file mode 100644 index 0000000..a197423 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/default-constants.ts @@ -0,0 +1,20 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const wethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.AVALANCHE]!.address; + +const routingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.AVALANCHE]!.address, // WAVAX + '0x231401dc8b53338d78c08f83cc4ebc74148196d0', // USDC + '0x5425890298aed601595a70ab815c96711a31bc65', // USDC2 + '0x0b9d5d9136855f6fec3c0993fee6e9ce8a297846', // LINK + '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB', // WETH + '0xd1c3f94DE7e5B45fa4eDBBA472491a9f4B166FC4' // XAVA +]; + +export const defaultFujiProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 3, + routingProvidersAddresses, + wethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/joe-fuji/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/joe-fuji/constants.ts new file mode 100644 index 0000000..d8f51ae --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/joe-fuji/constants.ts @@ -0,0 +1,4 @@ +import { defaultFujiProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/fuji/default-constants'; + +export const JOE_FUJI_CONTRACT_ADDRESS = '0x7b50046cEC8252ca835b148b1eDD997319120a12'; +export const JOE_FUJI_PROVIDER_CONFIGURATION = defaultFujiProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/joe-fuji/joe-fuji-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/joe-fuji/joe-fuji-provider.ts new file mode 100644 index 0000000..22bdb89 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/joe-fuji/joe-fuji-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { JOE_FUJI_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/fuji/joe-fuji/constants'; +import { JoeFujiTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/fuji/joe-fuji/joe-fuji-trade'; + +export class JoeFujiProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.FUJI; + + public readonly UniswapV2TradeClass = JoeFujiTrade; + + public readonly providerSettings = JOE_FUJI_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/joe-fuji/joe-fuji-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/joe-fuji/joe-fuji-trade.ts new file mode 100644 index 0000000..363dcad --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/joe-fuji/joe-fuji-trade.ts @@ -0,0 +1,20 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AVAX_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/avax-abi'; +import { AVALANCHE_SWAP_METHOD } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/swap-methods'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { JOE_FUJI_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/fuji/joe-fuji/constants'; + +export class JoeFujiTrade extends UniswapV2AbstractTrade { + public static readonly contractAbi = AVAX_ABI; + + public static readonly swapMethods = AVALANCHE_SWAP_METHOD; + + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.JOE; + } + + public readonly dexContractAddress = JOE_FUJI_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/pangolin-fuji/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/pangolin-fuji/constants.ts new file mode 100644 index 0000000..21d5bd1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/pangolin-fuji/constants.ts @@ -0,0 +1,5 @@ +import { defaultFujiProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/fuji/default-constants'; + +export const PANGOLIN_FUJI_CONTRACT_ADDRESS = '0x2D99ABD9008Dc933ff5c0CD271B88309593aB921'; + +export const PANGOLIN_FUJI_PROVIDER_CONFIGURATION = defaultFujiProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/pangolin-fuji/pangolin-fuji-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/pangolin-fuji/pangolin-fuji-provider.ts new file mode 100644 index 0000000..43adc0d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/pangolin-fuji/pangolin-fuji-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { PANGOLIN_FUJI_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/fuji/pangolin-fuji/constants'; +import { PangolinFujiTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/fuji/pangolin-fuji/pangolin-fuji-trade'; + +export class PangolinFujiProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.FUJI; + + public readonly UniswapV2TradeClass = PangolinFujiTrade; + + public readonly providerSettings = PANGOLIN_FUJI_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/pangolin-fuji/pangolin-fuji-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/pangolin-fuji/pangolin-fuji-trade.ts new file mode 100644 index 0000000..8e08111 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/fuji/pangolin-fuji/pangolin-fuji-trade.ts @@ -0,0 +1,20 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AVAX_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/avax-abi'; +import { AVALANCHE_SWAP_METHOD } from 'src/features/on-chain/calculation-manager/providers/dexes/avalanche/swap-methods'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { PANGOLIN_FUJI_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/fuji/pangolin-fuji/constants'; + +export class PangolinFujiTrade extends UniswapV2AbstractTrade { + public static readonly contractAbi = AVAX_ABI; + + public static readonly swapMethods = AVALANCHE_SWAP_METHOD; + + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.PANGOLIN; + } + + public readonly dexContractAddress = PANGOLIN_FUJI_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/gnosis/curve-gnosis/curve-gnosis-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/gnosis/curve-gnosis/curve-gnosis-provider.ts new file mode 100644 index 0000000..e0f9ed1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/gnosis/curve-gnosis/curve-gnosis-provider.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { CurveAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider'; +import { CurveGnosisTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/gnosis/curve-gnosis/curve-gnosis-trade'; + +export class CurveGnosisProvider extends CurveAbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.GNOSIS; + + public readonly Trade = CurveGnosisTrade; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/gnosis/curve-gnosis/curve-gnosis-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/gnosis/curve-gnosis/curve-gnosis-trade.ts new file mode 100644 index 0000000..11472d6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/gnosis/curve-gnosis/curve-gnosis-trade.ts @@ -0,0 +1,3 @@ +import { CurveAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade'; + +export class CurveGnosisTrade extends CurveAbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/goerli/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/goerli/default-constants.ts new file mode 100644 index 0000000..b4afa1e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/goerli/default-constants.ts @@ -0,0 +1,19 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const wethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.GOERLI]!.address; + +const defaultGoerliRoutingProvidersAddresses = [ + wethAddress, // WETH + '0xCbE56b00d173A26a5978cE90Db2E33622fD95A28', // USDC + '0xf4B2cbc3bA04c478F0dC824f4806aC39982Dce73', // USDT + '0xb93cba7013f4557cdfb590fd152d24ef4063485f', // DAI + '0xcc7bb2d219a0fc08033e130629c2b854b7ba9195' // ZETA +]; + +export const defaultGoerliProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 1, + routingProvidersAddresses: defaultGoerliRoutingProvidersAddresses, + wethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/goerli/uni-swap-v2-goerli/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/goerli/uni-swap-v2-goerli/constants.ts new file mode 100644 index 0000000..949797e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/goerli/uni-swap-v2-goerli/constants.ts @@ -0,0 +1,9 @@ +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; +import { defaultGoerliProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/goerli/default-constants'; + +export const UNISWAP_V2_GOERLI_CONTRACT_ADDRESS = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'; + +export const UNISWAP_V2_GOERLI_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultGoerliProviderConfiguration, + maxTransitTokens: 2 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/goerli/uni-swap-v2-goerli/uni-swap-v2-goerli-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/goerli/uni-swap-v2-goerli/uni-swap-v2-goerli-provider.ts new file mode 100644 index 0000000..25373aa --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/goerli/uni-swap-v2-goerli/uni-swap-v2-goerli-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { UNISWAP_V2_GOERLI_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/goerli/uni-swap-v2-goerli/constants'; +import { UniSwapV2GoerliTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/goerli/uni-swap-v2-goerli/uni-swap-v2-goerli-trade'; + +export class UniSwapV2GoerliProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.GOERLI; + + public readonly UniswapV2TradeClass = UniSwapV2GoerliTrade; + + public readonly providerSettings = UNISWAP_V2_GOERLI_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/goerli/uni-swap-v2-goerli/uni-swap-v2-goerli-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/goerli/uni-swap-v2-goerli/uni-swap-v2-goerli-trade.ts new file mode 100644 index 0000000..bdffa7b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/goerli/uni-swap-v2-goerli/uni-swap-v2-goerli-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { UNISWAP_V2_GOERLI_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/goerli/uni-swap-v2-goerli/constants'; + +export class UniSwapV2GoerliTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.UNISWAP_V2; + } + + public readonly dexContractAddress = UNISWAP_V2_GOERLI_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/default-constants.ts new file mode 100644 index 0000000..929731b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/default-constants.ts @@ -0,0 +1,21 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultHarmonyRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.HARMONY]!.address, // WONE + '0xef977d2f931c1978db5f6747666fa1eacb0d0339', // DAI + '0x985458e523db3d53125813ed68c274899e9dfab4', // USDC + '0x3c2b8be99c50593081eaa2a724f0b8285f5aba8f', // USDT + '0x3095c7557bcb296ccc6e363de01b760ba031f2d9', // WBTC + '0x0dc78c79b4eb080ead5c1d16559225a46b580694', // WAGMI + '0xea589e93ff18b1a1f1e9bac7ef3e86ab62addc79' // VIPER +]; + +const defaultHarmonyWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.HARMONY]!.address; + +export const defaultHarmonyProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultHarmonyRoutingProvidersAddresses, + wethAddress: defaultHarmonyWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/constants.ts new file mode 100644 index 0000000..0e0599c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/constants.ts @@ -0,0 +1,5 @@ +import { defaultHarmonyProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/default-constants'; + +export const SUSHI_SWAP_HARMONY_CONTRACT_ADDRESS = '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'; + +export const SUSHI_SWAP_HARMONY_PROVIDER_CONFIGURATION = defaultHarmonyProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/sushi-swap-harmony-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/sushi-swap-harmony-provider.ts new file mode 100644 index 0000000..5cf413f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/sushi-swap-harmony-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SUSHI_SWAP_HARMONY_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/constants'; +import { SushiSwapHarmonyTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/sushi-swap-harmony-trade'; + +export class SushiSwapHarmonyProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.HARMONY; + + public readonly UniswapV2TradeClass = SushiSwapHarmonyTrade; + + public readonly providerSettings = SUSHI_SWAP_HARMONY_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/sushi-swap-harmony-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/sushi-swap-harmony-trade.ts new file mode 100644 index 0000000..863fc30 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/sushi-swap-harmony-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { SUSHI_SWAP_HARMONY_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/sushi-swap-harmony/constants'; + +export class SushiSwapHarmonyTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SUSHI_SWAP; + } + + public readonly dexContractAddress = SUSHI_SWAP_HARMONY_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/trader-harmony/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/trader-harmony/constants.ts new file mode 100644 index 0000000..db601a2 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/trader-harmony/constants.ts @@ -0,0 +1,5 @@ +import { defaultHarmonyProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/default-constants'; + +export const TRADER_HARMONY_CONTRACT_ADDRESS = '0x24ad62502d1C652Cc7684081169D04896aC20f30'; + +export const TRADER_HARMONY_PROVIDER_CONFIGURATION = defaultHarmonyProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/trader-harmony/trader-harmony-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/trader-harmony/trader-harmony-provider.ts new file mode 100644 index 0000000..42b0656 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/trader-harmony/trader-harmony-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { TRADER_HARMONY_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/trader-harmony/constants'; +import { TradeHarmonySwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/trader-harmony/trader-harmony-trade'; + +export class TradeHarmonySwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.DFK; + + public readonly UniswapV2TradeClass = TradeHarmonySwapTrade; + + public readonly providerSettings = TRADER_HARMONY_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/trader-harmony/trader-harmony-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/trader-harmony/trader-harmony-trade.ts new file mode 100644 index 0000000..0cd8b80 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/trader-harmony/trader-harmony-trade.ts @@ -0,0 +1,20 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { DEX_TRADER_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/dex-trader/dex-trader-abi'; +import { ETH_SWAP_METHOD } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/dex-trader/dex-trader-swap-method'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { TRADER_HARMONY_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/trader-harmony/constants'; + +export class TradeHarmonySwapTrade extends UniswapV2AbstractTrade { + public static readonly contractAbi = DEX_TRADER_ABI; + + public static readonly swapMethods = ETH_SWAP_METHOD; + + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.TRADER; + } + + public readonly dexContractAddress = TRADER_HARMONY_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/viper-swap-harmony/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/viper-swap-harmony/constants.ts new file mode 100644 index 0000000..a2b79a7 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/viper-swap-harmony/constants.ts @@ -0,0 +1,5 @@ +import { defaultHarmonyProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/default-constants'; + +export const VIPER_SWAP_HARMONY_CONTRACT_ADDRESS = '0xf012702a5f0e54015362cBCA26a26fc90AA832a3'; + +export const VIPER_SWAP_HARMONY_PROVIDER_CONFIGURATION = defaultHarmonyProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/viper-swap-harmony/viper-swap-harmony-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/viper-swap-harmony/viper-swap-harmony-provider.ts new file mode 100644 index 0000000..457b2c3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/viper-swap-harmony/viper-swap-harmony-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { VIPER_SWAP_HARMONY_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/viper-swap-harmony/constants'; +import { ViperSwapHarmonyTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/viper-swap-harmony/viper-swap-harmony-trade'; + +export class ViperSwapHarmonyProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.HARMONY; + + public readonly UniswapV2TradeClass = ViperSwapHarmonyTrade; + + public readonly providerSettings = VIPER_SWAP_HARMONY_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/viper-swap-harmony/viper-swap-harmony-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/viper-swap-harmony/viper-swap-harmony-trade.ts new file mode 100644 index 0000000..9f5f663 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/harmony/viper-swap-harmony/viper-swap-harmony-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { VIPER_SWAP_HARMONY_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/harmony/viper-swap-harmony/constants'; + +export class ViperSwapHarmonyTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.VIPER_SWAP; + } + + public readonly dexContractAddress = VIPER_SWAP_HARMONY_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/ascent-horizon-eon/ascent-horizen-eon-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/ascent-horizon-eon/ascent-horizen-eon-provider.ts new file mode 100644 index 0000000..e359d5f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/ascent-horizon-eon/ascent-horizen-eon-provider.ts @@ -0,0 +1,11 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { AscentHorizenEonTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/ascent-horizon-eon/ascent-horizen-eon-trade'; +import { ASCENT_HORIZEN_EON_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/ascent-horizon-eon/constants'; +export class AscentHorizenEonProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.HORIZEN_EON; + + public readonly UniswapV2TradeClass = AscentHorizenEonTrade; + + public readonly providerSettings = ASCENT_HORIZEN_EON_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/ascent-horizon-eon/ascent-horizen-eon-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/ascent-horizon-eon/ascent-horizen-eon-trade.ts new file mode 100644 index 0000000..7c82d6c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/ascent-horizon-eon/ascent-horizen-eon-trade.ts @@ -0,0 +1,69 @@ +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { ContractMulticallResponse } from 'src/core/blockchain/web3-public-service/web3-public/models/contract-multicall-response'; +import { Injector } from 'src/core/injector/injector'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { DefaultRoutesMethodArgument } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/route-method-arguments'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { + ASCENT_CONTRACT_ABI, + ASCENT_HORIZEN_EON_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/ascent-horizon-eon/constants'; + +export class AscentHorizenEonTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ASCENT; + } + + public readonly dexContractAddress = ASCENT_HORIZEN_EON_CONTRACT_ADDRESS; + + public static readonly contractAbi = ASCENT_CONTRACT_ABI; + + public static async callForRoutes( + blockchain: EvmBlockchainName, + exact: Exact, + routesMethodArguments: DefaultRoutesMethodArgument[] + ): Promise[]> { + const web3Public = Injector.web3PublicService.getWeb3Public(blockchain); + const methodName = exact === 'input' ? 'getAmountsOut' : 'getAmountsIn'; + const args = routesMethodArguments.map(([amount, routes]) => { + const [firstRoute, ...otherRoutes] = routes; + const routesArgs = otherRoutes.map( + (route, index) => + (index === 0 + ? [firstRoute!, route, false] + : [otherRoutes[index - 1]!, route, false]) as [string, string, boolean] + ); + return [amount, routesArgs]; + }); + + return web3Public.multicallContractMethod( + this.getDexContractAddress(blockchain), + this.contractAbi, + methodName, + args + ); + } + + protected getCallParameters(receiverAddress?: string): unknown[] { + const { amountIn, amountOut } = this.getAmountInAndAmountOut(); + const amountParameters = this.from.isNative ? [amountOut] : [amountIn, amountOut]; + + const [firstToken, ...otherTokens] = [...this.wrappedPath]; + const path = otherTokens.map((token, index) => + index === 0 + ? [firstToken!.address, token.address, false] + : [otherTokens[index - 1]!.address, token.address, false] + ); + + return [ + ...amountParameters, + path, + receiverAddress || this.walletAddress, + this.deadlineMinutesTimestamp + ]; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/ascent-horizon-eon/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/ascent-horizon-eon/constants.ts new file mode 100644 index 0000000..1f7da53 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/ascent-horizon-eon/constants.ts @@ -0,0 +1,190 @@ +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; +import { defaultHorizenEonProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/default-constants'; +import { AbiItem } from 'web3-utils'; + +export const ASCENT_HORIZEN_EON_CONTRACT_ADDRESS = '0xcBE5798aeC48bed4bd90DD60882b4a9665bA33E2'; + +export const ASCENT_HORIZEN_EON_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultHorizenEonProviderConfiguration +}; + +export const ASCENT_CONTRACT_ABI: AbiItem[] = [ + { + type: 'function', + stateMutability: 'view', + outputs: [ + { type: 'uint256', name: 'amount', internalType: 'uint256' }, + { type: 'bool', name: 'stable', internalType: 'bool' } + ], + name: 'getAmountOut', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'address', name: 'tokenIn', internalType: 'address' }, + { type: 'address', name: 'tokenOut', internalType: 'address' } + ] + }, + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'getAmountsOut', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { + type: 'tuple[]', + name: 'routes', + internalType: 'struct RouterV2.route[]', + components: [ + { type: 'address', name: 'from', internalType: 'address' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'bool', name: 'stable', internalType: 'bool' } + ] + } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactETHForTokens', + inputs: [ + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { + type: 'tuple[]', + name: 'routes', + internalType: 'struct RouterV2.route[]', + components: [ + { type: 'address', name: 'from', internalType: 'address' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'bool', name: 'stable', internalType: 'bool' } + ] + }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'swapExactETHForTokensSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { + type: 'tuple[]', + name: 'routes', + internalType: 'struct RouterV2.route[]', + components: [ + { type: 'address', name: 'from', internalType: 'address' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'bool', name: 'stable', internalType: 'bool' } + ] + }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactTokensForETH', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { + type: 'tuple[]', + name: 'routes', + internalType: 'struct RouterV2.route[]', + components: [ + { type: 'address', name: 'from', internalType: 'address' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'bool', name: 'stable', internalType: 'bool' } + ] + }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [], + name: 'swapExactTokensForETHSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { + type: 'tuple[]', + name: 'routes', + internalType: 'struct RouterV2.route[]', + components: [ + { type: 'address', name: 'from', internalType: 'address' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'bool', name: 'stable', internalType: 'bool' } + ] + }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactTokensForTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { + type: 'tuple[]', + name: 'routes', + internalType: 'struct RouterV2.route[]', + components: [ + { type: 'address', name: 'from', internalType: 'address' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'bool', name: 'stable', internalType: 'bool' } + ] + }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactTokensForTokensSimple', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address', name: 'tokenFrom', internalType: 'address' }, + { type: 'address', name: 'tokenTo', internalType: 'address' }, + { type: 'bool', name: 'stable', internalType: 'bool' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [], + name: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { + type: 'tuple[]', + name: 'routes', + internalType: 'struct RouterV2.route[]', + components: [ + { type: 'address', name: 'from', internalType: 'address' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'bool', name: 'stable', internalType: 'bool' } + ] + }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/default-constants.ts new file mode 100644 index 0000000..12c3eb3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/default-constants.ts @@ -0,0 +1,22 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultHorizenEonRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.HORIZEN_EON]!.address, // wZEN + '0x38C2a6953F86a7453622B1E7103b738239728754', // izDAI + '0xCc44eB064CD32AAfEEb2ebb2a47bE0B882383b53', // izUSDC + '0xA167bcAb6791304EDa9B636C8beEC75b3D2829E6', // izUSDT + '0x1d7fb99AED3C365B4DEf061B7978CE5055Dfc1e7', // izBTC + '0x2c2E0B0c643aB9ad03adBe9140627A645E99E054', // izETH (wETH) + '0xCEad8ee30e03aE87E5E709617f7FdF180Eef9973', // ZUSD + '0x6318374DFb468113E06d3463ec5Ed0B6Ae0F0982' // izAVAX (wAVAX) +]; + +const defaultHorizenEonWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.HORIZEN_EON]!.address; + +export const defaultHorizenEonProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultHorizenEonRoutingProvidersAddresses, + wethAddress: defaultHorizenEonWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/spooky-swap-horizen-eon/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/spooky-swap-horizen-eon/constants.ts new file mode 100644 index 0000000..6c5d5df --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/spooky-swap-horizen-eon/constants.ts @@ -0,0 +1,23 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; +import { defaultHorizenEonProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/default-constants'; + +export const SPOOKY_SWAP_HORIZEN_EON_CONTRACT_ADDRESS = + '0xccEC4EF0228488636B6E8dF77D7d4211c87Ef255'; + +const routingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.HORIZEN_EON]!.address, // wZEN + '0x38C2a6953F86a7453622B1E7103b738239728754', // izDAI + '0xCc44eB064CD32AAfEEb2ebb2a47bE0B882383b53', // izUSDC + '0xA167bcAb6791304EDa9B636C8beEC75b3D2829E6', // izUSDT + '0x1d7fb99AED3C365B4DEf061B7978CE5055Dfc1e7', // izBTC + '0x2c2E0B0c643aB9ad03adBe9140627A645E99E054', // izETH (wETH) + '0xCEad8ee30e03aE87E5E709617f7FdF180Eef9973', // ZUSD + '0x6318374DFb468113E06d3463ec5Ed0B6Ae0F0982' // izAVAX (wAVAX) +]; + +export const SPOOKY_SWAP_HORIZEN_EON_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultHorizenEonProviderConfiguration, + routingProvidersAddresses +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/spooky-swap-horizen-eon/spooky-swap-horizen-eon-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/spooky-swap-horizen-eon/spooky-swap-horizen-eon-provider.ts new file mode 100644 index 0000000..603a19e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/spooky-swap-horizen-eon/spooky-swap-horizen-eon-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SPOOKY_SWAP_HORIZEN_EON_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/spooky-swap-horizen-eon/constants'; +import { SpookySwapHorizenEonTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/spooky-swap-horizen-eon/spooky-swap-horizen-eon-trade'; + +export class SpookySwapHorizenEonProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.HORIZEN_EON; + + public readonly UniswapV2TradeClass = SpookySwapHorizenEonTrade; + + public readonly providerSettings = SPOOKY_SWAP_HORIZEN_EON_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/spooky-swap-horizen-eon/spooky-swap-horizen-eon-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/spooky-swap-horizen-eon/spooky-swap-horizen-eon-trade.ts new file mode 100644 index 0000000..f3e6df6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/spooky-swap-horizen-eon/spooky-swap-horizen-eon-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { SPOOKY_SWAP_HORIZEN_EON_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/horizen-eon/spooky-swap-horizen-eon/constants'; + +export class SpookySwapHorizenEonTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SPOOKY_SWAP; + } + + public readonly dexContractAddress = SPOOKY_SWAP_HORIZEN_EON_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/curve-kava/curve-kava-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/curve-kava/curve-kava-provider.ts new file mode 100644 index 0000000..49fcd44 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/curve-kava/curve-kava-provider.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { CurveAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider'; +import { CurveKavaTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/curve-kava/curve-kava-trade'; + +export class CurveKavaProvider extends CurveAbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.KAVA; + + public readonly Trade = CurveKavaTrade; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/curve-kava/curve-kava-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/curve-kava/curve-kava-trade.ts new file mode 100644 index 0000000..53a52a7 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/curve-kava/curve-kava-trade.ts @@ -0,0 +1,3 @@ +import { CurveAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade'; + +export class CurveKavaTrade extends CurveAbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/default-constants.ts new file mode 100644 index 0000000..3e9bcc9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/default-constants.ts @@ -0,0 +1,30 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultKavaRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.KAVA]!.address, // WKAVA + '0x765277eebeca2e31912c9946eae1021199b39c61', // DAI + '0xE3F5a90F9cb311505cd691a46596599aA1A0AD7D', // ETH + '0xab4e4bbb6d207b341cb5edbfa497d17ff5afa4d4', // JPT + '0xfa9343c3897324496a05fc75abed6bac29f8a40f', // USDC + '0xb44a9b6905af7c801311e8f4e76932ee959c663c', // USDT + '0x818ec0a7fe18ff94269904fced6ae3dae6d6dc0b' // WBTC +]; + +const defaultKavaWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.KAVA]!.address; + +export const defaultKavaProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 3, + routingProvidersAddresses: defaultKavaRoutingProvidersAddresses, + wethAddress: defaultKavaWethAddress +}; + +export const kavaProviderConfiguration: UniswapV2ProviderConfiguration = { + ...defaultKavaProviderConfiguration, + routingProvidersAddresses: [ + ...defaultKavaRoutingProvidersAddresses, + '0xeEeEEb57642040bE42185f49C52F7E9B38f8eeeE', // ELK + '0x0f428d528b4f00c82a8ad032580d605cf5f122ee' // TIDE + ] +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/elk/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/elk/constants.ts new file mode 100644 index 0000000..14b7019 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/elk/constants.ts @@ -0,0 +1,5 @@ +import { kavaProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/default-constants'; + +export const ELK_CONTRACT_ADDRESS = '0x7a2c1D96C76B6EB62241df4d2fAEb9F0D3D59E10'; + +export const ELK_PROVIDER_CONFIGURATION = kavaProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/elk/elk-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/elk/elk-provider.ts new file mode 100644 index 0000000..ad86b0b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/elk/elk-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { ELK_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/elk/constants'; +import { ElkTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/elk/elk-trade'; + +export class ElkProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.KAVA; + + public readonly UniswapV2TradeClass = ElkTrade; + + public readonly providerSettings = ELK_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/elk/elk-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/elk/elk-trade.ts new file mode 100644 index 0000000..8d5d1b8 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/elk/elk-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { ELK_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/elk/constants'; + +export class ElkTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ELK; + } + + public readonly dexContractAddress = ELK_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/jupiter-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/jupiter-swap/constants.ts new file mode 100644 index 0000000..acb12d8 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/jupiter-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultKavaProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/default-constants'; + +export const JUPITER_SWAP_CONTRACT_ADDRESS = '0xEa3CB4Ba9d1fD6fb19Df1380958d5649bD3e7C50'; + +export const JUPITER_SWAP_PROVIDER_CONFIGURATION = defaultKavaProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/jupiter-swap/jupiter-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/jupiter-swap/jupiter-swap-provider.ts new file mode 100644 index 0000000..12689e9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/jupiter-swap/jupiter-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { JUPITER_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/jupiter-swap/constants'; +import { JupiterSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/jupiter-swap/jupiter-swap-trade'; + +export class JupiterSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.KAVA; + + public readonly UniswapV2TradeClass = JupiterSwapTrade; + + public readonly providerSettings = JUPITER_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/jupiter-swap/jupiter-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/jupiter-swap/jupiter-swap-trade.ts new file mode 100644 index 0000000..217e96d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/jupiter-swap/jupiter-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { JUPITER_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/jupiter-swap/constants'; + +export class JupiterSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.JUPITER_SWAP; + } + + public readonly dexContractAddress = JUPITER_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/photon-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/photon-swap/constants.ts new file mode 100644 index 0000000..39f6b42 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/photon-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultKavaProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/default-constants'; + +export const PHOTON_SWAP_CONTRACT_ADDRESS = '0x8a340F39A468C2FcBFFf2122446a9A0745A313Ad'; + +export const PHOTON_SWAP_PROVIDER_CONFIGURATION = defaultKavaProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/photon-swap/photon-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/photon-swap/photon-swap-provider.ts new file mode 100644 index 0000000..9d2a6f1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/photon-swap/photon-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { PHOTON_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/photon-swap/constants'; +import { PhotonSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/photon-swap/photon-swap-trade'; + +export class PhotonSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.KAVA; + + public readonly UniswapV2TradeClass = PhotonSwapTrade; + + public readonly providerSettings = PHOTON_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/photon-swap/photon-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/photon-swap/photon-swap-trade.ts new file mode 100644 index 0000000..2de7a2d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/photon-swap/photon-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { PHOTON_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/photon-swap/constants'; + +export class PhotonSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.PHOTON_SWAP; + } + + public readonly dexContractAddress = PHOTON_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/surfdex/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/surfdex/constants.ts new file mode 100644 index 0000000..c881488 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/surfdex/constants.ts @@ -0,0 +1,5 @@ +import { kavaProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/default-constants'; + +export const SURFDEX_CONTRACT_ADDRESS = '0x4310ed61E7E4fd50C2b44C92725C087abeB632a2'; + +export const SURFDEX_PROVIDER_CONFIGURATION = kavaProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/surfdex/surfdex-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/surfdex/surfdex-provider.ts new file mode 100644 index 0000000..031859f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/surfdex/surfdex-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SURFDEX_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/surfdex/constants'; +import { SurfdexTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/surfdex/surfdex-trade'; + +export class SurfdexProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.KAVA; + + public readonly UniswapV2TradeClass = SurfdexTrade; + + public readonly providerSettings = SURFDEX_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/surfdex/surfdex-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/surfdex/surfdex-trade.ts new file mode 100644 index 0000000..1b72ce5 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kava/surfdex/surfdex-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { SURFDEX_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/kava/surfdex/constants'; + +export class SurfdexTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SURFDEX; + } + + public readonly dexContractAddress = SURFDEX_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/claim-swap/claim-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/claim-swap/claim-swap-provider.ts new file mode 100644 index 0000000..b5828ec --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/claim-swap/claim-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { ClaimSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/klaytn/claim-swap/claim-swap-trade'; +import { CLAIM_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/klaytn/claim-swap/constants'; + +export class ClaimSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.KLAYTN; + + public readonly UniswapV2TradeClass = ClaimSwapTrade; + + public readonly providerSettings = CLAIM_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/claim-swap/claim-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/claim-swap/claim-swap-trade.ts new file mode 100644 index 0000000..18075b3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/claim-swap/claim-swap-trade.ts @@ -0,0 +1,20 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { CLAIM_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/klaytn/claim-swap/constants'; +import { KLAY_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/klaytn/klaytn-abi'; +import { KLAY_SWAP_METHOD } from 'src/features/on-chain/calculation-manager/providers/dexes/klaytn/klaytn-swap-method'; + +export class ClaimSwapTrade extends UniswapV2AbstractTrade { + public static readonly contractAbi = KLAY_ABI; + + public static readonly swapMethods = KLAY_SWAP_METHOD; + + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.CLAIM_SWAP; + } + + public readonly dexContractAddress = CLAIM_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/claim-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/claim-swap/constants.ts new file mode 100644 index 0000000..e4f0381 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/claim-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultKlaytnProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/klaytn/default-constants'; + +export const CLAIM_SWAP_CONTRACT_ADDRESS = '0xEf71750C100f7918d6Ded239Ff1CF09E81dEA92D'; + +export const CLAIM_SWAP_PROVIDER_CONFIGURATION = defaultKlaytnProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/default-constants.ts new file mode 100644 index 0000000..f0627ea --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/default-constants.ts @@ -0,0 +1,15 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultKlaytnRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.KLAYTN]!.address // WKLAYTN +]; + +const defaultKlaytnWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.KLAYTN]!.address; + +export const defaultKlaytnProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultKlaytnRoutingProvidersAddresses, + wethAddress: defaultKlaytnWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/klaytn-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/klaytn-abi.ts new file mode 100644 index 0000000..56c0c04 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/klaytn-abi.ts @@ -0,0 +1,138 @@ +import { AbiItem } from 'web3-utils'; + +export const KLAY_ABI = [ + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'getAmountsIn', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' } + ] + }, + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'getAmountsOut', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactKLAYForTokens', + inputs: [ + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'swapExactKLAYForTokensSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactTokensForKLAY', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [], + name: 'swapExactTokensForKLAYSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactTokensForTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [], + name: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapKLAYForExactTokens', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapTokensForExactKLAY', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'uint256', name: 'amountInMax', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapTokensForExactTokens', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'uint256', name: 'amountInMax', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/klaytn-swap-method.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/klaytn-swap-method.ts new file mode 100644 index 0000000..856f874 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/klaytn/klaytn-swap-method.ts @@ -0,0 +1,45 @@ +import { + ExactInputOutputSwapMethodsList, + RegularSwapMethodsList, + SupportingFeeSwapMethodsList, + SwapMethodsList +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/SWAP_METHOD'; + +export const KLAY_EXACT_INPUT_REGULAR_SWAP_METHOD: RegularSwapMethodsList = { + TOKENS_TO_TOKENS: 'swapExactTokensForTokens', + ETH_TO_TOKENS: 'swapExactKLAYForTokens', + TOKENS_TO_ETH: 'swapExactTokensForKLAY' +}; + +export const KLAY_EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD: SupportingFeeSwapMethodsList = { + TOKENS_TO_TOKENS_SUPPORTING_FEE: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + ETH_TO_TOKENS_SUPPORTING_FEE: 'swapExactKLAYForTokensSupportingFeeOnTransferTokens', + TOKENS_TO_ETH_SUPPORTING_FEE: 'swapExactTokensForKLAYSupportingFeeOnTransferTokens' +}; + +export const KLAY_EXACT_INPUT_SWAP_METHOD: SwapMethodsList = { + ...KLAY_EXACT_INPUT_REGULAR_SWAP_METHOD, + ...KLAY_EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD +}; + +export const KLAY_EXACT_OUTPUT_REGULAR_SWAP_METHOD: RegularSwapMethodsList = { + TOKENS_TO_TOKENS: 'swapTokensForExactTokens', + ETH_TO_TOKENS: 'swapKLAYForExactTokens', + TOKENS_TO_ETH: 'swapTokensForExactKLAY' +}; + +export const KLAY_EXACT_OUTPUT_SUPPORTING_FEE_SWAP_METHOD: SupportingFeeSwapMethodsList = { + TOKENS_TO_TOKENS_SUPPORTING_FEE: 'swapTokensForExactTokensSupportingFeeOnTransferTokens', + ETH_TO_TOKENS_SUPPORTING_FEE: 'swapKLAYForExactTokensSupportingFeeOnTransferTokens', + TOKENS_TO_ETH_SUPPORTING_FEE: 'swapTokensForExactKLAYSupportingFeeOnTransferTokens' +}; + +export const KLAY_EXACT_OUTPUT_SWAP_METHOD: SwapMethodsList = { + ...KLAY_EXACT_OUTPUT_REGULAR_SWAP_METHOD, + ...KLAY_EXACT_OUTPUT_SUPPORTING_FEE_SWAP_METHOD +}; + +export const KLAY_SWAP_METHOD: ExactInputOutputSwapMethodsList = { + input: KLAY_EXACT_INPUT_SWAP_METHOD, + output: KLAY_EXACT_OUTPUT_SWAP_METHOD +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kroma/izumi-kroma/izumi-kroma-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kroma/izumi-kroma/izumi-kroma-provider.ts new file mode 100644 index 0000000..58fa512 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/kroma/izumi-kroma/izumi-kroma-provider.ts @@ -0,0 +1,21 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiKromaProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.KROMA; + + protected readonly dexAddress = '0x02F55D53DcE23B4AA962CC68b0f685f26143Bdb2'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0x3EF68D3f7664b2805D4E88381b64868a56f88bC4', + liquidityManagerAddress: '0x110dE362cc436D7f54210f96b8C7652C2617887D', + routingTokenAddresses: [ + '0x0257e4d92C00C9EfcCa1d641b224d7d09cfa4522', // USDC + '0x0Cf7c2A584988871b654Bd79f96899e4cd6C41C0', // USDT + '0x4200000000000000000000000000000000000001' // WETH + ], + multicallAddress: '0x7a524c7e82874226F0b51aade60A1BE4D430Cf0F', + supportedFees: [500, 3000, 10_000] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/default-constants.ts new file mode 100644 index 0000000..c5f5c70 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/default-constants.ts @@ -0,0 +1,20 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultLineaRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.LINEA]!.address, // WETH + '0xf5C6825015280CdfD0b56903F9F8B5A2233476F5', // BNB + '0x7d43AABC515C356145049227CeE54B608342c0ad', // BUSD + '0x265b25e22bcd7f10a5bd6e6410f10537cc7567e8', // MATIC + '0x0B1A02A7309dFbfAD1Cd4adC096582C87e8A3Ac1', // HZN + '0x2140Ea50bc3B6Ac3971F9e9Ea93A1442665670e4' // NFTE +]; + +const defaultLineaWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.LINEA]!.address; + +export const defaultLineaProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultLineaRoutingProvidersAddresses, + wethAddress: defaultLineaWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/constants/provider-configuration.ts new file mode 100644 index 0000000..7347bd9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/constants/provider-configuration.ts @@ -0,0 +1,8 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const HORIZONDEX_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.LINEA]!.address, + maxTransitTokens: 1 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/constants/router-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/constants/router-configuration.ts new file mode 100644 index 0000000..b636b07 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/constants/router-configuration.ts @@ -0,0 +1,253 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { + UniswapV3RouterConfiguration, + UniswapV3RouterLiquidityPool +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration'; +import { AbiItem } from 'web3-utils'; + +/** + * Most popular tokens in uni v3 to use in a route. + */ +const tokensSymbols = ['WETH', 'BNB', 'BUSD', 'MATIC', 'HZN', 'NFTE'] as const; + +type TokenSymbol = (typeof tokensSymbols)[number]; + +const routerTokens: Record = { + WETH: wrappedNativeTokensList[BLOCKCHAIN_NAME.LINEA]!.address, + BNB: '0xf5C6825015280CdfD0b56903F9F8B5A2233476F5', + BUSD: '0x7d43AABC515C356145049227CeE54B608342c0ad', + MATIC: '0x265b25e22bcd7f10a5bd6e6410f10537cc7567e8', + HZN: '0x0B1A02A7309dFbfAD1Cd4adC096582C87e8A3Ac1', + NFTE: '0x2140Ea50bc3B6Ac3971F9e9Ea93A1442665670e4' +}; + +const routerLiquidityPools: UniswapV3RouterLiquidityPool[] = [ + { + poolAddress: '0xe2df725e44ab983e8513ecfc9c3e13bc21ea867e', + tokenSymbolA: 'BUSD', + tokenSymbolB: 'WETH', + fee: 300 + }, + { + poolAddress: '0x0330fddd733ea64f92b348ff19a2bb4d29d379d5', + tokenSymbolA: 'MATIC', + tokenSymbolB: 'WETH', + fee: 300 + }, + { + poolAddress: '0xfe7a3ab43d8db17643ba5dc2f132a74049dcf42f', + tokenSymbolA: 'BUSD', + tokenSymbolB: 'BNB', + fee: 300 + }, + { + poolAddress: '0xa6a69fddec12e7ee44474a92e9c549a612519411', + tokenSymbolA: 'WETH', + tokenSymbolB: 'BNB', + fee: 300 + }, + { + poolAddress: '0x0003ecc56c19953ea2e2de626e44111ade02ad6e', + tokenSymbolA: 'HZN', + tokenSymbolB: 'WETH', + fee: 300 + }, + { + poolAddress: '0xa24b6a8698ee173ccf5a97f73f1f2d8bb7032feb', + tokenSymbolA: 'HZN', + tokenSymbolB: 'WETH', + fee: 1000 + }, + { + poolAddress: '0xc9dc3eda8f6b664e2e25d632bb4d4f28ab58ee3c', + tokenSymbolA: 'NFTE', + tokenSymbolB: 'WETH', + fee: 1000 + } +]; + +export const HORIZONDEX_ROUTER_CONFIGURATION: UniswapV3RouterConfiguration = { + tokens: routerTokens, + liquidityPools: routerLiquidityPools +}; + +export const HORIZONDEX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS = + '0x272e156df8da513c69cb41cc7a99185d53f926bb'; + +export const HORIZONDEX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'address', name: '_factory', type: 'address' }, + { internalType: 'address', name: '_WETH', type: 'address' } + ], + stateMutability: 'nonpayable', + type: 'constructor' + }, + { + inputs: [], + name: 'WETH', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'factory', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' }], + name: 'multicall', + outputs: [{ internalType: 'bytes[]', name: 'results', type: 'bytes[]' }], + stateMutability: 'payable', + type: 'function' + }, + { inputs: [], name: 'refundEth', outputs: [], stateMutability: 'payable', type: 'function' }, + { + inputs: [ + { internalType: 'int256', name: 'deltaQty0', type: 'int256' }, + { internalType: 'int256', name: 'deltaQty1', type: 'int256' }, + { internalType: 'bytes', name: 'data', type: 'bytes' } + ], + name: 'swapCallback', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'minAmountOut', type: 'uint256' } + ], + internalType: 'struct IRouter.ExactInputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'swapExactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'minAmountOut', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtP', type: 'uint160' } + ], + internalType: 'struct IRouter.ExactInputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'swapExactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'maxAmountIn', type: 'uint256' } + ], + internalType: 'struct IRouter.ExactOutputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'swapExactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'maxAmountIn', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtP', type: 'uint160' } + ], + internalType: 'struct IRouter.ExactOutputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'swapExactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'transferAllTokens', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'feeUnits', type: 'uint256' }, + { internalType: 'address', name: 'feeRecipient', type: 'address' } + ], + name: 'transferAllTokensWithFee', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'unwrapWeth', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'feeUnits', type: 'uint256' }, + { internalType: 'address', name: 'feeRecipient', type: 'address' } + ], + name: 'unwrapWethWithFee', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { stateMutability: 'payable', type: 'receive' } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-provider.ts new file mode 100644 index 0000000..945ba20 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-provider.ts @@ -0,0 +1,21 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-provider'; +import { HorizondexUniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/horizondex-uniswap-v3-quoter-controller'; +import { HORIZONDEX_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/constants/provider-configuration'; +import { HORIZONDEX_ROUTER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/constants/router-configuration'; +import { HorizondexTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-trade'; + +export class HorizondexProvider extends UniswapV3AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.LINEA; + + public readonly OnChainTradeClass = HorizondexTrade; + + public readonly providerConfiguration = HORIZONDEX_PROVIDER_CONFIGURATION; + + public readonly routerConfiguration = HORIZONDEX_ROUTER_CONFIGURATION; + + protected readonly quoterController = new HorizondexUniswapV3QuoterController( + this.blockchain, + this.routerConfiguration + ); +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-trade.ts new file mode 100644 index 0000000..4c87d28 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-trade.ts @@ -0,0 +1,84 @@ +import { RubicSdkError } from 'src/common/errors'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV3AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-trade'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; +import { + HORIZONDEX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI, + HORIZONDEX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/constants/router-configuration'; + +export class HorizondexTrade extends UniswapV3AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.HORIZONDEX; + } + + public readonly dexContractAddress = HORIZONDEX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = HORIZONDEX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI; + + protected readonly unwrapWethMethodName = 'unwrapWeth'; + + /** + * Returns swap `exactInput` method's name and arguments to use in Swap contract. + */ + protected getSwapRouterExactInputMethodData(walletAddress: string): MethodData { + const amountParams = this.getAmountParams(); + + if (this.route.poolsPath.length === 1) { + const methodName = + this.exact === 'input' ? 'swapExactInputSingle' : 'swapExactOutputSingle'; + + const pool = this.route.poolsPath[0]; + if (!pool) { + throw new RubicSdkError('Initial pool has to be defined'); + } + const toTokenAddress = compareAddresses( + pool.token0.address, + this.route.initialTokenAddress + ) + ? pool.token1.address + : pool.token0.address; + + if (!this.route?.poolsPath?.[0]) { + throw new RubicSdkError('PoolsPath[0] has to be defined'); + } + + return { + methodName, + methodArguments: [ + [ + this.route.initialTokenAddress, + toTokenAddress, + this.route.poolsPath[0].fee, + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams, + 0 + ] + ] + }; + } + + const methodName = this.exact === 'input' ? 'swapExactInput' : 'swapExactOutput'; + + return { + methodName, + methodArguments: [ + [ + UniswapV3QuoterController.getEncodedPoolsPath( + this.route.poolsPath, + this.route.initialTokenAddress + ), + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams + ] + ] + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/utils/quoter-controller/constants/quoter-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/utils/quoter-controller/constants/quoter-contract-data.ts new file mode 100644 index 0000000..dcde401 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/utils/quoter-controller/constants/quoter-contract-data.ts @@ -0,0 +1,102 @@ +import { AbiItem } from 'web3-utils'; + +export const HORIZONDEX_QUOTER_CONTRACT_ADDRESS = '0x07AceD5690e09935b1c0e6E88B772d9440F64718'; + +export const HORIZONDEX_QUOTER_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' } + ], + name: 'quoteExactInput', + outputs: [ + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint160[]', name: 'afterSqrtPList', type: 'uint160[]' }, + { internalType: 'uint32[]', name: 'initializedTicksCrossedList', type: 'uint32[]' }, + { internalType: 'uint256', name: 'gasEstimate', type: 'uint256' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint24', name: 'feeUnits', type: 'uint24' }, + { internalType: 'uint160', name: 'limitSqrtP', type: 'uint160' } + ], + internalType: 'struct IQuoterV2.QuoteExactInputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'quoteExactInputSingle', + outputs: [ + { + components: [ + { internalType: 'uint256', name: 'usedAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'returnedAmount', type: 'uint256' }, + { internalType: 'uint160', name: 'afterSqrtP', type: 'uint160' }, + { internalType: 'uint32', name: 'initializedTicksCrossed', type: 'uint32' }, + { internalType: 'uint256', name: 'gasEstimate', type: 'uint256' } + ], + internalType: 'struct IQuoterV2.QuoteOutput', + name: 'output', + type: 'tuple' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' } + ], + name: 'quoteExactOutput', + outputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint160[]', name: 'afterSqrtPList', type: 'uint160[]' }, + { internalType: 'uint32[]', name: 'initializedTicksCrossedList', type: 'uint32[]' }, + { internalType: 'uint256', name: 'gasEstimate', type: 'uint256' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint24', name: 'feeUnits', type: 'uint24' }, + { internalType: 'uint160', name: 'limitSqrtP', type: 'uint160' } + ], + internalType: 'struct IQuoterV2.QuoteExactOutputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'quoteExactOutputSingle', + outputs: [ + { + components: [ + { internalType: 'uint256', name: 'usedAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'returnedAmount', type: 'uint256' }, + { internalType: 'uint160', name: 'afterSqrtP', type: 'uint160' }, + { internalType: 'uint32', name: 'initializedTicksCrossed', type: 'uint32' }, + { internalType: 'uint256', name: 'gasEstimate', type: 'uint256' } + ], + internalType: 'struct IQuoterV2.QuoteOutput', + name: 'output', + type: 'tuple' + } + ], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/izumi-linea/izumi-linea-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/izumi-linea/izumi-linea-provider.ts new file mode 100644 index 0000000..ba81f74 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/izumi-linea/izumi-linea-provider.ts @@ -0,0 +1,23 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiLineaProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.LINEA; + + protected readonly dexAddress = '0x032b241De86a8660f1Ae0691a4760B426EA246d7'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0xe4A0b241D8345d86FB140D40c87C5fbDd685B9dd', + liquidityManagerAddress: '0x1CB60033F61e4fc171c963f0d2d3F63Ece24319c', + routingTokenAddresses: [ + '0x176211869ca2b568f2a7d4ee941e073a821ee1ff', // USDC + wrappedNativeTokensList[BLOCKCHAIN_NAME.LINEA]!.address, // WETH + '0x7d43aabc515c356145049227cee54b608342c0ad', // BUSD + '0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d' // iUSD + ], + multicallAddress: '0x7a524c7e82874226F0b51aade60A1BE4D430Cf0F', + supportedFees: [10000, 3000, 500] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/sync-swap/linea-sync-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/sync-swap/linea-sync-swap-provider.ts new file mode 100644 index 0000000..ee6cdd3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/sync-swap/linea-sync-swap-provider.ts @@ -0,0 +1,31 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { SyncSwapAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abstract-provider'; + +export class LineaSyncSwapProvider extends SyncSwapAbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.LINEA; + + public readonly dexContractAddress = '0x80e38291e06339d10AAB483C65695D004dBD5C69'; + + public readonly routerHelperContract = '0x91e3D3E51dC93B80a2FFBfdCa29EbF33e132D4E6'; + + public readonly vault = '0x7160570BB153Edd0Ea1775EC2b2Ac9b65F1aB61B'; + + public readonly factories = [ + '0x37BAc764494c8db4e54BDE72f6965beA9fa0AC2d', + '0xE4CF807E351b56720B17A59094179e7Ed9dD3727' + ]; + + public readonly routeTokens = [ + '0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f', // WETH + '0x176211869cA2b568f2A7D4EE941E073a821EE1ff', // USDC + '0x7d43aabc515c356145049227cee54b608342c0ad', // ceBUSD + '0xf5c6825015280cdfd0b56903f9f8b5a2233476f5', // ceBNB + '0x3aab2285ddcddad8edf438c1bab47e1a9d05a9b4', // WBTC + '0x5471ea8f739dd37e9b81be9c5c77754d8aa953e4', // ceAVAX + '0x265b25e22bcd7f10a5bd6e6410f10537cc7567e8' // ceMATIC + ]; + + public readonly masterAddress = '0x608Cb7C3168427091F5994A45Baf12083964B4A3'; + + public readonly maxTransitTokens = 1; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/vooi/constants/pool-id-mapping.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/vooi/constants/pool-id-mapping.ts new file mode 100644 index 0000000..a3991fa --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/vooi/constants/pool-id-mapping.ts @@ -0,0 +1,5 @@ +export const vooiLineaPoolIdMapping: Record = { + '0x4af15ec2a0bd43db75dd04e62faa3b8ef36b00d5': 2, // DAI + '0xa219439258ca9da29e9cc4ce5596924745e12b93': 1, // USDT + '0x176211869ca2b568f2a7d4ee941e073a821ee1ff': 0 // USDC +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/vooi/vooi-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/vooi/vooi-provider.ts new file mode 100644 index 0000000..35a90a5 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/vooi/vooi-provider.ts @@ -0,0 +1,21 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { vooiLineaPoolIdMapping } from 'src/features/on-chain/calculation-manager/providers/dexes/linea/vooi/constants/pool-id-mapping'; + +import { VooiTradeStruct } from '../../common/vooi-abstract/models/vooi-trade-struct'; +import { VooiAbstractProvider } from '../../common/vooi-abstract/vooi-abstract-provider'; +import { VooiLineaTrade } from './vooi-trade'; + +export class VooiLineaProvider extends VooiAbstractProvider { + protected readonly omniPoolAddress = '0x87E4c4517B28844f575Cfbbc4CABBD867863EA6E'; + + protected readonly vooiPoolIdMapping = vooiLineaPoolIdMapping; + + public readonly blockchain = BLOCKCHAIN_NAME.LINEA; + + protected createTradeInstance( + tradeStruct: VooiTradeStruct, + providerAddress: string + ): VooiLineaTrade { + return new VooiLineaTrade(tradeStruct, providerAddress); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/vooi/vooi-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/vooi/vooi-trade.ts new file mode 100644 index 0000000..c68f916 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/linea/vooi/vooi-trade.ts @@ -0,0 +1,5 @@ +import { VooiAbstractTrade } from '../../common/vooi-abstract/vooi-abstract-trade'; + +export class VooiLineaTrade extends VooiAbstractTrade { + public readonly dexContractAddress = '0xBc7f67fA9C72f9fcCf917cBCEe2a50dEb031462A'; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/manta-pacific/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/manta-pacific/default-constants.ts new file mode 100644 index 0000000..655c65d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/manta-pacific/default-constants.ts @@ -0,0 +1,20 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultMantaPacificRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.MANTA_PACIFIC]!.address, // WETH + '0xf417F5A458eC102B90352F697D6e2Ac3A3d2851f', // USDT + '0xb73603C5d87fA094B7314C74ACE2e64D165016fb', // USDC + '0x1c466b9371f8aBA0D7c458bE10a62192Fcb8Aa71', // DAI + '0x305E88d809c9DC03179554BFbf85Ac05Ce8F18d6' // WBTC +]; + +const defaultMantaPacificWethAddress = + wrappedNativeTokensList[BLOCKCHAIN_NAME.MANTA_PACIFIC]!.address; + +export const defaultMantaPacificProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultMantaPacificRoutingProvidersAddresses, + wethAddress: defaultMantaPacificWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/manta-pacific/izumi-manta-pacific/izumi-manta-pacific-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/manta-pacific/izumi-manta-pacific/izumi-manta-pacific-provider.ts new file mode 100644 index 0000000..39a1424 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/manta-pacific/izumi-manta-pacific/izumi-manta-pacific-provider.ts @@ -0,0 +1,22 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiMantaPacificProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.MANTA_PACIFIC; + + protected readonly dexAddress = '0x3EF68D3f7664b2805D4E88381b64868a56f88bC4'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0x34bc1b87f60e0a30c0e24FD7Abada70436c71406', + liquidityManagerAddress: '0x19b683A2F45012318d9B2aE1280d68d3eC54D663', + routingTokenAddresses: [ + '0xb73603C5d87fA094B7314C74ACE2e64D165016fb', // USDC + wrappedNativeTokensList[BLOCKCHAIN_NAME.MANTA_PACIFIC]!.address, // WETH + '0xf417F5A458eC102B90352F697D6e2Ac3A3d2851f' // USDT + ], + multicallAddress: '0x7a524c7e82874226F0b51aade60A1BE4D430Cf0F', + supportedFees: [10000, 3000, 500] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/constants/provider-configuration.ts new file mode 100644 index 0000000..5eb209d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/constants/provider-configuration.ts @@ -0,0 +1,8 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const FUSIONX_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.MANTLE]!.address, + maxTransitTokens: 1 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/constants/router-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/constants/router-configuration.ts new file mode 100644 index 0000000..9c2976d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/constants/router-configuration.ts @@ -0,0 +1,312 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { + UniswapV3RouterConfiguration, + UniswapV3RouterLiquidityPool +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration'; +import { AbiItem } from 'web3-utils'; + +/** + * Most popular tokens in uni v3 to use in a route. + */ +const tokensSymbols = ['WETH', 'WMNT', 'USDT', 'USDC', 'WBTC', 'MINU'] as const; + +type TokenSymbol = (typeof tokensSymbols)[number]; + +const routerTokens: Record = { + WMNT: wrappedNativeTokensList[BLOCKCHAIN_NAME.MANTLE]!.address, + WETH: '0xdEAddEaDdeadDEadDEADDEAddEADDEAddead1111', + USDT: '0x201EBa5CC46D216Ce6DC03F6a759e8E766e956aE', + USDC: '0x09bc4e0d864854c6afb6eb9a9cdf58ac190d0df9', + WBTC: '0xcabae6f6ea1ecab08ad02fe02ce9a44f09aebfa2', + MINU: '0x51cfe5b1e764dc253f4c8c1f19a081ff4c3517ed' +}; + +const routerLiquidityPools: UniswapV3RouterLiquidityPool[] = [ + { + poolAddress: '0xa125af1a4704044501fe12ca9567ef1550e430e8', + tokenSymbolA: 'USDT', + tokenSymbolB: 'WETH', + fee: 500 + }, + { + poolAddress: '0x262255f4770aebe2d0c8b97a46287dcecc2a0aff', + tokenSymbolA: 'USDT', + tokenSymbolB: 'WMNT', + fee: 500 + }, + { + poolAddress: '0x16867d00d45347a2ded25b8cdb7022b3171d4ae0', + tokenSymbolA: 'USDC', + tokenSymbolB: 'USDT', + fee: 100 + }, + { + poolAddress: '0xb11d56e78076df5b4fea0f3f9f1febdb043fabf3', + tokenSymbolA: 'USDT', + tokenSymbolB: 'WBTC', + fee: 500 + }, + { + poolAddress: '0x3bfb98c0af1aca0e66d96624c7e545eb131f285e', + tokenSymbolA: 'MINU', + tokenSymbolB: 'WMNT', + fee: 10000 + }, + { + poolAddress: '0xb1d9358e08391e93593c6a1899dc77931912bb16', + tokenSymbolA: 'USDT', + tokenSymbolB: 'MINU', + fee: 10000 + } +]; + +export const FUSIONX_ROUTER_CONFIGURATION: UniswapV3RouterConfiguration = { + tokens: routerTokens, + liquidityPools: routerLiquidityPools +}; + +export const FUSIONX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS = + '0x5989FB161568b9F133eDf5Cf6787f5597762797F'; +export const FUSIONX_UNISWAP_V3_FACTORY_CONTRACT_ADDRESS = + '0x530d2766D1988CC1c000C8b7d00334c14B69AD71'; + +export const FUSIONX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI = [ + { + type: 'constructor', + inputs: [ + { type: 'address', name: '_deployer', internalType: 'address' }, + { type: 'address', name: '_factory', internalType: 'address' }, + { type: 'address', name: '_WETH9', internalType: 'address' } + ] + }, + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'address', name: '', internalType: 'address' }], + name: 'WETH9', + inputs: [] + }, + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'address', name: '', internalType: 'address' }], + name: 'deployer', + inputs: [] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'uint256', name: 'amountOut', internalType: 'uint256' }], + name: 'exactInput', + inputs: [ + { + type: 'tuple', + name: 'params', + internalType: 'struct ISwapRouter.ExactInputParams', + components: [ + { type: 'bytes' }, + { type: 'address' }, + { type: 'uint256' }, + { type: 'uint256' }, + { type: 'uint256' } + ] + } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'uint256', name: 'amountOut', internalType: 'uint256' }], + name: 'exactInputSingle', + inputs: [ + { + type: 'tuple', + name: 'params', + internalType: 'struct ISwapRouter.ExactInputSingleParams', + components: [ + { type: 'address' }, + { type: 'address' }, + { type: 'uint24' }, + { type: 'address' }, + { type: 'uint256' }, + { type: 'uint256' }, + { type: 'uint256' }, + { type: 'uint160' } + ] + } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'uint256', name: 'amountIn', internalType: 'uint256' }], + name: 'exactOutput', + inputs: [ + { + type: 'tuple', + name: 'params', + internalType: 'struct ISwapRouter.ExactOutputParams', + components: [ + { type: 'bytes' }, + { type: 'address' }, + { type: 'uint256' }, + { type: 'uint256' }, + { type: 'uint256' } + ] + } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'uint256', name: 'amountIn', internalType: 'uint256' }], + name: 'exactOutputSingle', + inputs: [ + { + type: 'tuple', + name: 'params', + internalType: 'struct ISwapRouter.ExactOutputSingleParams', + components: [ + { type: 'address' }, + { type: 'address' }, + { type: 'uint24' }, + { type: 'address' }, + { type: 'uint256' }, + { type: 'uint256' }, + { type: 'uint256' }, + { type: 'uint160' } + ] + } + ] + }, + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'address', name: '', internalType: 'address' }], + name: 'factory', + inputs: [] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [], + name: 'fusionXV3SwapCallback', + inputs: [ + { type: 'int256', name: 'amount0Delta', internalType: 'int256' }, + { type: 'int256', name: 'amount1Delta', internalType: 'int256' }, + { type: 'bytes', name: '_data', internalType: 'bytes' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'bytes[]', name: 'results', internalType: 'bytes[]' }], + name: 'multicall', + inputs: [{ type: 'bytes[]', name: 'data', internalType: 'bytes[]' }] + }, + { type: 'function', stateMutability: 'payable', outputs: [], name: 'refundETH', inputs: [] }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'selfPermit', + inputs: [ + { type: 'address', name: 'token', internalType: 'address' }, + { type: 'uint256', name: 'value', internalType: 'uint256' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' }, + { type: 'uint8', name: 'v', internalType: 'uint8' }, + { type: 'bytes32', name: 'r', internalType: 'bytes32' }, + { type: 'bytes32', name: 's', internalType: 'bytes32' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'selfPermitAllowed', + inputs: [ + { type: 'address', name: 'token', internalType: 'address' }, + { type: 'uint256', name: 'nonce', internalType: 'uint256' }, + { type: 'uint256', name: 'expiry', internalType: 'uint256' }, + { type: 'uint8', name: 'v', internalType: 'uint8' }, + { type: 'bytes32', name: 'r', internalType: 'bytes32' }, + { type: 'bytes32', name: 's', internalType: 'bytes32' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'selfPermitAllowedIfNecessary', + inputs: [ + { type: 'address', name: 'token', internalType: 'address' }, + { type: 'uint256', name: 'nonce', internalType: 'uint256' }, + { type: 'uint256', name: 'expiry', internalType: 'uint256' }, + { type: 'uint8', name: 'v', internalType: 'uint8' }, + { type: 'bytes32', name: 'r', internalType: 'bytes32' }, + { type: 'bytes32', name: 's', internalType: 'bytes32' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'selfPermitIfNecessary', + inputs: [ + { type: 'address', name: 'token', internalType: 'address' }, + { type: 'uint256', name: 'value', internalType: 'uint256' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' }, + { type: 'uint8', name: 'v', internalType: 'uint8' }, + { type: 'bytes32', name: 'r', internalType: 'bytes32' }, + { type: 'bytes32', name: 's', internalType: 'bytes32' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'sweepToken', + inputs: [ + { type: 'address', name: 'token', internalType: 'address' }, + { type: 'uint256', name: 'amountMinimum', internalType: 'uint256' }, + { type: 'address', name: 'recipient', internalType: 'address' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'sweepTokenWithFee', + inputs: [ + { type: 'address', name: 'token', internalType: 'address' }, + { type: 'uint256', name: 'amountMinimum', internalType: 'uint256' }, + { type: 'address', name: 'recipient', internalType: 'address' }, + { type: 'uint256', name: 'feeBips', internalType: 'uint256' }, + { type: 'address', name: 'feeRecipient', internalType: 'address' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'unwrapWETH9', + inputs: [ + { type: 'uint256', name: 'amountMinimum', internalType: 'uint256' }, + { type: 'address', name: 'recipient', internalType: 'address' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'unwrapWETH9WithFee', + inputs: [ + { type: 'uint256', name: 'amountMinimum', internalType: 'uint256' }, + { type: 'address', name: 'recipient', internalType: 'address' }, + { type: 'uint256', name: 'feeBips', internalType: 'uint256' }, + { type: 'address', name: 'feeRecipient', internalType: 'address' } + ] + }, + { type: 'receive' } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/fusionx-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/fusionx-provider.ts new file mode 100644 index 0000000..cf94250 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/fusionx-provider.ts @@ -0,0 +1,21 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-provider'; +import { FusionXUniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/fusionx-uniswap-v3-quoter-controller'; +import { FUSIONX_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/constants/provider-configuration'; +import { FUSIONX_ROUTER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/constants/router-configuration'; +import { FusionXTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/fusionx-trade'; + +export class FusionXProvider extends UniswapV3AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.MANTLE; + + public readonly OnChainTradeClass = FusionXTrade; + + public readonly providerConfiguration = FUSIONX_PROVIDER_CONFIGURATION; + + public readonly routerConfiguration = FUSIONX_ROUTER_CONFIGURATION; + + protected readonly quoterController = new FusionXUniswapV3QuoterController( + this.blockchain, + this.routerConfiguration + ); +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/fusionx-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/fusionx-trade.ts new file mode 100644 index 0000000..22cf022 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/fusionx-trade.ts @@ -0,0 +1,19 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV3AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-trade'; +import { + FUSIONX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI, + FUSIONX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/constants/router-configuration'; + +export class FusionXTrade extends UniswapV3AbstractTrade { + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.FUSIONX; + } + + public readonly dexContractAddress = FUSIONX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = FUSIONX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/utils/quoter-controller/constants/quoter-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/utils/quoter-controller/constants/quoter-contract-data.ts new file mode 100644 index 0000000..ade7f9d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/fusionx/utils/quoter-controller/constants/quoter-contract-data.ts @@ -0,0 +1,86 @@ +import { AbiItem } from 'web3-utils'; + +export const FUSIONX_QUOTER_CONTRACT_ADDRESS = '0x90f72244294E7c5028aFd6a96E18CC2c1E913995'; + +export const FUSIONX_QUOTER_CONTRACT_ABI = [ + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'uint160[]', name: 'sqrtPriceX96AfterList', internalType: 'uint160[]' }, + { type: 'uint32[]', name: 'initializedTicksCrossedList', internalType: 'uint32[]' }, + { type: 'uint256', name: 'gasEstimate', internalType: 'uint256' } + ], + name: 'quoteExactInput', + inputs: [ + { type: 'bytes', name: 'path', internalType: 'bytes' }, + { type: 'uint256', name: 'amountIn', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'uint160', name: 'sqrtPriceX96After', internalType: 'uint160' }, + { type: 'uint32', name: 'initializedTicksCrossed', internalType: 'uint32' }, + { type: 'uint256', name: 'gasEstimate', internalType: 'uint256' } + ], + name: 'quoteExactInputSingle', + inputs: [ + { + type: 'tuple', + name: 'params', + internalType: 'struct IQuoterV2.QuoteExactInputSingleParams', + components: [ + { type: 'address' }, + { type: 'address' }, + { type: 'uint256' }, + { type: 'uint24' }, + { type: 'uint160' } + ] + } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint160[]', name: 'sqrtPriceX96AfterList', internalType: 'uint160[]' }, + { type: 'uint32[]', name: 'initializedTicksCrossedList', internalType: 'uint32[]' }, + { type: 'uint256', name: 'gasEstimate', internalType: 'uint256' } + ], + name: 'quoteExactOutput', + inputs: [ + { type: 'bytes', name: 'path', internalType: 'bytes' }, + { type: 'uint256', name: 'amountOut', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint160', name: 'sqrtPriceX96After', internalType: 'uint160' }, + { type: 'uint32', name: 'initializedTicksCrossed', internalType: 'uint32' }, + { type: 'uint256', name: 'gasEstimate', internalType: 'uint256' } + ], + name: 'quoteExactOutputSingle', + inputs: [ + { + type: 'tuple', + name: 'params', + internalType: 'struct IQuoterV2.QuoteExactOutputSingleParams', + components: [ + { type: 'address' }, + { type: 'address' }, + { type: 'uint256' }, + { type: 'uint24' }, + { type: 'uint160' } + ] + } + ] + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/izumi-mantle/izumi-mantle-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/izumi-mantle/izumi-mantle-provider.ts new file mode 100644 index 0000000..d8c1ae0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mantle/izumi-mantle/izumi-mantle-provider.ts @@ -0,0 +1,25 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiMantleProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.MANTLE; + + protected readonly dexAddress = '0x25C030116Feb2E7BbA054b9de0915E5F51b03e31'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0xe6805638db944eA605e774e72c6F0D15Fb6a1347', + liquidityManagerAddress: '0x611575eE1fbd4F7915D0eABCC518eD396fF78F0c', + routingTokenAddresses: [ + '0x201eba5cc46d216ce6dc03f6a759e8e766e956ae', // USDT + wrappedNativeTokensList[BLOCKCHAIN_NAME.MANTLE]!.address, // WBNB + '0xdeaddeaddeaddeaddeaddeaddeaddeaddead1111', // WETH + '0x09bc4e0d864854c6afb6eb9a9cdf58ac190d0df9', // USDC + '0x60d01ec2d5e98ac51c8b4cf84dfcce98d527c747', // IZI + '0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d' // iUSD + ], + multicallAddress: '0x1DADF066518E2b7064D85cED45625BFeC52ca07d', + supportedFees: [10000, 3000, 500] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/merlin/izumi-merlin/izumi-merlin-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/merlin/izumi-merlin/izumi-merlin-provider.ts new file mode 100644 index 0000000..6461a07 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/merlin/izumi-merlin/izumi-merlin-provider.ts @@ -0,0 +1,21 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiMerlinProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.MERLIN; + + protected readonly dexAddress = '0x1aFa5D7f89743219576Ef48a9826261bE6378a68'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0xD5f164e44057e8004266cc2EABc670cDFa3E0Fd2', + liquidityManagerAddress: '0x261507940678Bf22d8ee96c31dF4a642294c0467', + routingTokenAddresses: [ + '0xb880fd278198bd590252621d4cd071b1842e9bcd', // MBTC + wrappedNativeTokensList[BLOCKCHAIN_NAME.MERLIN]!.address + ], + multicallAddress: '0x7a524c7e82874226F0b51aade60A1BE4D430Cf0F', + supportedFees: [10000, 3000] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/merlin/merlin-swap/merlin-swap-merlin-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/merlin/merlin-swap/merlin-swap-merlin-provider.ts new file mode 100644 index 0000000..09b3bbf --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/merlin/merlin-swap/merlin-swap-merlin-provider.ts @@ -0,0 +1,40 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; +import { IzumiTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-trade'; +import { IzumiTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/models/izumi-trade-struct'; +import { MerlinSwapMerlinTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/merlin/merlin-swap/merlin-swap-merlin-trade'; + +export class MerlinSwapMerlinProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.MERLIN; + + protected readonly dexAddress = '0x1aFa5D7f89743219576Ef48a9826261bE6378a68'; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.MERLIN_SWAP; + } + + public getProviderTrade(tradeStruct: IzumiTradeStruct, providerAddress: string): IzumiTrade { + return new MerlinSwapMerlinTrade(tradeStruct, providerAddress); + } + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0xD5f164e44057e8004266cc2EABc670cDFa3E0Fd2', + liquidityManagerAddress: '0x261507940678Bf22d8ee96c31dF4a642294c0467', + routingTokenAddresses: [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.MERLIN]!.address, // WBTC + '0x480e158395cc5b41e5584347c495584ca2caf78d', // VOYA + '0x7a677e59dc2c8a42d6af3a62748c5595034a008b', // HUHU + '0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d', // iUSD + '0xb880fd278198bd590252621d4cd071b1842e9bcd', // M-BTC + '0x62e99191071fc1c5947cf1e21aa95708dcc51adb' // Owl + ], + multicallAddress: '0x7a524c7e82874226F0b51aade60A1BE4D430Cf0F', + supportedFees: [10000, 3000, 500] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/merlin/merlin-swap/merlin-swap-merlin-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/merlin/merlin-swap/merlin-swap-merlin-trade.ts new file mode 100644 index 0000000..b110ada --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/merlin/merlin-swap/merlin-swap-merlin-trade.ts @@ -0,0 +1,11 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { IzumiTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-trade'; + +export class MerlinSwapMerlinTrade extends IzumiTrade { + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.MERLIN_SWAP; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/default-constants.ts new file mode 100644 index 0000000..11cc2d0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/default-constants.ts @@ -0,0 +1,21 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultMetisRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.METIS]!.address, // WMETIS + '0xea32a96608495e54156ae48931a7c20f0dcc1a21', // m.USDC + '0xbb06dca3ae6887fabf931640f67cab3e3a16f4dc', // m.USDT + '0x721532bc0da5ffaeb0a6a45fb24271e8098629a7', // BYTE + '0x420000000000000000000000000000000000000a', // WETH + '0x90fe084f877c65e1b577c7b2ea64b8d8dd1ab278', // NETT + '0x2bf9b864cdc97b08b6d79ad4663e71b8ab65c45c' // BUSD +]; + +const defaultMetisWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.METIS]!.address; + +export const defaultMetisProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultMetisRoutingProvidersAddresses, + wethAddress: defaultMetisWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/metis-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/metis-abi.ts new file mode 100644 index 0000000..8c6be61 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/metis-abi.ts @@ -0,0 +1,138 @@ +import { AbiItem } from 'web3-utils'; + +export const METIS_ABI = [ + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'getAmountsIn', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' } + ] + }, + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'getAmountsOut', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactMetisForTokens', + inputs: [ + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'swapExactMetisForTokensSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactTokensForMetis', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [], + name: 'swapExactTokensForMetisSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactTokensForTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [], + name: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapMetisForExactTokens', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapTokensForExactMetis', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'uint256', name: 'amountInMax', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapTokensForExactTokens', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'uint256', name: 'amountInMax', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/metis-swap-methods.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/metis-swap-methods.ts new file mode 100644 index 0000000..0b53973 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/metis-swap-methods.ts @@ -0,0 +1,45 @@ +import { + ExactInputOutputSwapMethodsList, + RegularSwapMethodsList, + SupportingFeeSwapMethodsList, + SwapMethodsList +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/SWAP_METHOD'; + +export const METIS_EXACT_INPUT_REGULAR_SWAP_METHOD: RegularSwapMethodsList = { + TOKENS_TO_TOKENS: 'swapExactTokensForTokens', + ETH_TO_TOKENS: 'swapExactMetisForTokens', + TOKENS_TO_ETH: 'swapExactTokensForMetis' +}; + +export const METIS_EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD: SupportingFeeSwapMethodsList = { + TOKENS_TO_TOKENS_SUPPORTING_FEE: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + ETH_TO_TOKENS_SUPPORTING_FEE: 'swapExactMetisForTokensSupportingFeeOnTransferTokens', + TOKENS_TO_ETH_SUPPORTING_FEE: 'swapExactTokensForMetisSupportingFeeOnTransferTokens' +}; + +export const METIS_EXACT_INPUT_SWAP_METHOD: SwapMethodsList = { + ...METIS_EXACT_INPUT_REGULAR_SWAP_METHOD, + ...METIS_EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD +}; + +export const METIS_EXACT_OUTPUT_REGULAR_SWAP_METHOD: RegularSwapMethodsList = { + TOKENS_TO_TOKENS: 'swapTokensForExactTokens', + ETH_TO_TOKENS: 'swapMetisForExactTokens', + TOKENS_TO_ETH: 'swapTokensForExactMetis' +}; + +export const METIS_EXACT_OUTPUT_SUPPORTING_FEE_SWAP_METHOD: SupportingFeeSwapMethodsList = { + TOKENS_TO_TOKENS_SUPPORTING_FEE: 'swapTokensForExactTokensSupportingFeeOnTransferTokens', + ETH_TO_TOKENS_SUPPORTING_FEE: 'swapMetisForExactTokensSupportingFeeOnTransferTokens', + TOKENS_TO_ETH_SUPPORTING_FEE: 'swapTokensForExactMetisSupportingFeeOnTransferTokens' +}; + +export const METIS_EXACT_OUTPUT_SWAP_METHOD: SwapMethodsList = { + ...METIS_EXACT_OUTPUT_REGULAR_SWAP_METHOD, + ...METIS_EXACT_OUTPUT_SUPPORTING_FEE_SWAP_METHOD +}; + +export const METIS_SWAP_METHOD: ExactInputOutputSwapMethodsList = { + input: METIS_EXACT_INPUT_SWAP_METHOD, + output: METIS_EXACT_OUTPUT_SWAP_METHOD +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/net-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/net-swap/constants.ts new file mode 100644 index 0000000..875e74c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/net-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultMetisProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/metis/default-constants'; + +export const NET_SWAP_CONTRACT_ADDRESS = '0x1E876cCe41B7b844FDe09E38Fa1cf00f213bFf56'; + +export const NET_SWAP_PROVIDER_CONFIGURATION = defaultMetisProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/net-swap/net-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/net-swap/net-swap-provider.ts new file mode 100644 index 0000000..2ad4c53 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/net-swap/net-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { NET_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/metis/net-swap/constants'; +import { NetSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/metis/net-swap/net-swap-trade'; + +export class NetSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.METIS; + + public readonly UniswapV2TradeClass = NetSwapTrade; + + public readonly providerSettings = NET_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/net-swap/net-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/net-swap/net-swap-trade.ts new file mode 100644 index 0000000..fa69de3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/metis/net-swap/net-swap-trade.ts @@ -0,0 +1,20 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { METIS_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/metis/metis-abi'; +import { METIS_SWAP_METHOD } from 'src/features/on-chain/calculation-manager/providers/dexes/metis/metis-swap-methods'; +import { NET_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/metis/net-swap/constants'; + +export class NetSwapTrade extends UniswapV2AbstractTrade { + public static readonly contractAbi = METIS_ABI; + + public static readonly swapMethods = METIS_SWAP_METHOD; + + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.NET_SWAP; + } + + public readonly dexContractAddress = NET_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/constants/provider-configuration.ts new file mode 100644 index 0000000..6f9fbc3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/constants/provider-configuration.ts @@ -0,0 +1,8 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const MODE_ALGEBRA_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.MODE]!.address, + maxTransitTokens: 1 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/constants/swap-router-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/constants/swap-router-contract-data.ts new file mode 100644 index 0000000..fb70a14 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/constants/swap-router-contract-data.ts @@ -0,0 +1,108 @@ +import { AbiItem } from 'web3-utils'; + +export const MODE_ALGEBRA_ROUTER_CONTRACT_ADDRESS = '0xAc48FcF1049668B285f3dC72483DF5Ae2162f7e8'; + +export const MODE_ALGEBRA_ROUTER_CONTRACT_ABI = [ + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactInputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactInputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactOutputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactOutputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' }], + name: 'multicall', + outputs: [{ internalType: 'bytes[]', name: 'results', type: 'bytes[]' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountMinimum', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'unwrapWNativeToken', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { stateMutability: 'payable', type: 'receive' } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/mode-algebra-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/mode-algebra-provider.ts new file mode 100644 index 0000000..e91463d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/mode-algebra-provider.ts @@ -0,0 +1,65 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { createTokenNativeAddressProxyInPathStartAndEnd } from 'src/features/common/utils/token-native-address-proxy'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraTradeStructOmitPath } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UniswapV3AlgebraAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider'; +import { ModeAlgebraTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/mode-algebra-trade'; +import { ModeAlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/models/mode-algebra-route'; + +import { defaultModeProviderConfiguration } from '../default-constants'; +import { MODE_ALGEBRA_PROVIDER_CONFIGURATION } from './constants/provider-configuration'; +import { + MODE_ALGEBRA_ROUTER_CONTRACT_ABI, + MODE_ALGEBRA_ROUTER_CONTRACT_ADDRESS +} from './constants/swap-router-contract-data'; +import { + MODE_ALGEBRA_QUOTER_CONTRACT_ABI, + MODE_ALGEBRA_QUOTER_CONTRACT_ADDRESS +} from './utils/quoter-controller/constants/quoter-contract-data'; + +export class ModeAlgebraProvider extends UniswapV3AlgebraAbstractProvider { + protected readonly contractAddress = MODE_ALGEBRA_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = MODE_ALGEBRA_ROUTER_CONTRACT_ABI; + + public readonly blockchain = BLOCKCHAIN_NAME.MODE; + + protected readonly OnChainTradeClass = ModeAlgebraTrade; + + protected readonly providerConfiguration = MODE_ALGEBRA_PROVIDER_CONFIGURATION; + + protected readonly quoterController = new AlgebraQuoterController( + this.blockchain, + defaultModeProviderConfiguration.routingProvidersAddresses, + MODE_ALGEBRA_QUOTER_CONTRACT_ADDRESS, + MODE_ALGEBRA_QUOTER_CONTRACT_ABI + ); + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.KIM; + } + + protected createTradeInstance( + tradeStruct: UniswapV3AlgebraTradeStructOmitPath, + route: ModeAlgebraRoute, + providerAddress: string + ): ModeAlgebraTrade { + const path = createTokenNativeAddressProxyInPathStartAndEnd( + route.path, + EvmWeb3Pure.nativeTokenAddress + ); + return new ModeAlgebraTrade( + { + ...tradeStruct, + path, + route + }, + providerAddress + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/mode-algebra-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/mode-algebra-trade.ts new file mode 100644 index 0000000..6e42fa8 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/mode-algebra-trade.ts @@ -0,0 +1,78 @@ +import { Token } from 'src/common/tokens'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade'; +import { ModeAlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/models/mode-algebra-route'; +import { ModeAlgebraTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/models/mode-algebra-trade-struct'; + +import { + MODE_ALGEBRA_ROUTER_CONTRACT_ABI, + MODE_ALGEBRA_ROUTER_CONTRACT_ADDRESS +} from './constants/swap-router-contract-data'; + +export class ModeAlgebraTrade extends UniswapV3AlgebraAbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.KIM; + } + + public readonly dexContractAddress = MODE_ALGEBRA_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = MODE_ALGEBRA_ROUTER_CONTRACT_ABI; + + protected readonly unwrapWethMethodName = 'unwrapWNativeToken'; + + private readonly route: ModeAlgebraRoute; + + public readonly wrappedPath: ReadonlyArray; + + constructor(tradeStruct: ModeAlgebraTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this.route = tradeStruct.route; + + this.wrappedPath = this.route.path; + } + + /** + * Returns swap `exactInput` method's name and arguments to use in Swap contract. + */ + protected getSwapRouterExactInputMethodData(walletAddress: string): MethodData { + const amountParams = this.getAmountParams(); + + if (this.route.path.length === 2 && this.route?.path?.[0] && this.route?.path?.[1]) { + const methodName = this.exact === 'input' ? 'exactInputSingle' : 'exactOutputSingle'; + + return { + methodName, + methodArguments: [ + [ + this.route.path[0].address, + this.route.path[1].address, + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams, + 0 + ] + ] + }; + } + + const methodName = this.exact === 'input' ? 'exactInput' : 'exactOutput'; + + return { + methodName, + methodArguments: [ + [ + AlgebraQuoterController.getEncodedPath(this.route.path), + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams + ] + ] + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/models/mode-algebra-route.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/models/mode-algebra-route.ts new file mode 100644 index 0000000..949f95e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/models/mode-algebra-route.ts @@ -0,0 +1,9 @@ +import { Token } from 'src/common/tokens'; +import { UniswapV3AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route'; + +export interface ModeAlgebraRoute extends UniswapV3AlgebraRoute { + /** + * List of pools' contract addresses to use in a trade's route. + */ + path: Token[]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/models/mode-algebra-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/models/mode-algebra-trade-struct.ts new file mode 100644 index 0000000..4cb7708 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/models/mode-algebra-trade-struct.ts @@ -0,0 +1,6 @@ +import { UniswapV3AlgebraTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { ModeAlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/models/mode-algebra-route'; + +export interface ModeAlgebraTradeStruct extends UniswapV3AlgebraTradeStruct { + route: ModeAlgebraRoute; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/utils/quoter-controller/constants/quoter-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/utils/quoter-controller/constants/quoter-contract-data.ts new file mode 100644 index 0000000..81835c8 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/algebra-mode/utils/quoter-controller/constants/quoter-contract-data.ts @@ -0,0 +1,50 @@ +import { AbiItem } from 'web3-utils'; + +export const MODE_ALGEBRA_QUOTER_CONTRACT_ADDRESS = '0x8678f0aBe08D30dC555F27c488551569A29B31Eb'; + +export const MODE_ALGEBRA_QUOTER_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' } + ], + name: 'quoteExactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' } + ], + name: 'quoteExactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/default-constants.ts new file mode 100644 index 0000000..b24967a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/default-constants.ts @@ -0,0 +1,18 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultModeRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.MODE]!.address, // WETH + '0xf0F161fDA2712DB8b566946122a5af183995e2eD', // USDT + '0xd988097fb8612cc24eeC14542bC03424c656005f', // USDC + '0xcDd475325D6F564d27247D1DddBb0DAc6fA0a5CF' // WBTC +]; + +const defaultModeWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.MODE]!.address; + +export const defaultModeProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultModeRoutingProvidersAddresses, + wethAddress: defaultModeWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/eddy-finance-mode/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/eddy-finance-mode/constants.ts new file mode 100644 index 0000000..51a9a2c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/eddy-finance-mode/constants.ts @@ -0,0 +1,144 @@ +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; +import { defaultModeProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/mode/default-constants'; +import { AbiItem } from 'web3-utils'; + +export const EDDY_FINANCE_MODE_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultModeProviderConfiguration, + maxTransitTokens: 1 +}; + +export const EDDY_FINANCE_MODE_SWAP_CONTRACT_ADDRESS = '0xCb0ca072EFb267F17289574Bf563e8dF05c7Ffe3'; + +export const EDDY_FINANCE_MODE_CALCULATE_CONTRACT_ADDRESS = + '0xc1e624C810D297FD70eF53B0E08F44FABE468591'; + +export const EDDY_FINANCE_MODE_SWAP_CONTRACT_ABI = [ + { + inputs: [], + name: 'WETH', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + // ETH → any + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapEddyExactETHForTokens', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'payable', + type: 'function' + }, + // Any → ETH + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapEddyExactTokensForEth', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + // Any → Any + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + }, + { + internalType: 'address', + name: 'to', + type: 'address' + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256' + } + ], + name: 'swapEddyTokensForTokens', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/eddy-finance-mode/eddy-finance-mode-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/eddy-finance-mode/eddy-finance-mode-provider.ts new file mode 100644 index 0000000..0f0f98d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/eddy-finance-mode/eddy-finance-mode-provider.ts @@ -0,0 +1,13 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { EDDY_FINANCE_MODE_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/mode/eddy-finance-mode/constants'; + +import { EddyFinanceModeTrade } from './eddy-finance-mode-trade'; + +export class EddyFinanceModeProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.MODE; + + public readonly UniswapV2TradeClass = EddyFinanceModeTrade; + + public readonly providerSettings = EDDY_FINANCE_MODE_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/eddy-finance-mode/eddy-finance-mode-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/eddy-finance-mode/eddy-finance-mode-trade.ts new file mode 100644 index 0000000..507d6de --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/eddy-finance-mode/eddy-finance-mode-trade.ts @@ -0,0 +1,145 @@ +import { NotWhitelistedProviderError, RubicSdkError } from 'src/common/errors'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { checkUnsupportedReceiverAddress } from 'src/features/common/utils/check-unsupported-receiver-address'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { EDDY_SWAP_METHOD } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/EDDY_SWAP_METHOD'; +import { ExactInputOutputSwapMethodsList } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/SWAP_METHOD'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { + EDDY_FINANCE_MODE_CALCULATE_CONTRACT_ADDRESS, + EDDY_FINANCE_MODE_SWAP_CONTRACT_ABI, + EDDY_FINANCE_MODE_SWAP_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/mode/eddy-finance-mode/constants'; + +export class EddyFinanceModeTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.EDDY_FINANCE; + } + + public readonly dexContractAddress = EDDY_FINANCE_MODE_CALCULATE_CONTRACT_ADDRESS; + + public static readonly swapMethods: ExactInputOutputSwapMethodsList = EDDY_SWAP_METHOD; + + protected get spenderAddress(): string { + return this.useProxy + ? rubicProxyContractAddress[this.from.blockchain].gateway + : EDDY_FINANCE_MODE_SWAP_CONTRACT_ADDRESS; + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + await this.checkFromAddress(options.fromAddress, true); + checkUnsupportedReceiverAddress( + options?.receiverAddress, + options?.fromAddress || this.walletAddress + ); + + if (options.supportFee === undefined) { + const needApprove = await this.needApprove(options.fromAddress); + if (needApprove) { + throw new RubicSdkError( + 'To use `encode` function, token must be approved for wallet' + ); + } + + try { + await this.checkBalance(); + } catch (_err) { + throw new RubicSdkError( + 'To use `encode` function, wallet must have enough balance or you must provider `supportFee` parameter in options.' + ); + } + } + + const methodName = await this.getMethodName(options, options.fromAddress, false); + const gasParams = this.getGasParams(options); + + const config = EvmWeb3Pure.encodeMethodCall( + EDDY_FINANCE_MODE_SWAP_CONTRACT_ADDRESS, + EDDY_FINANCE_MODE_SWAP_CONTRACT_ABI, + methodName, + this.getCallParameters(options.receiverAddress), + this.nativeValueToSend, + gasParams + ); + + return { tx: config, toAmount: this.to.stringWeiAmount }; + } + + protected getCallParameters(receiverAddress?: string): unknown[] { + const { amountIn } = this.getAmountInAndAmountOut(); + // EddyFinance 13.05.2024:: + // amountOut is handled at our contracts for now so you can pass it as 0(Will be changed in future) + const amountOut = '0'; + const amountParameters = this.from.isNative ? [amountOut] : [amountIn, amountOut]; + + return [ + ...amountParameters, + this.wrappedPath.map(t => t.address), + receiverAddress || this.walletAddress, + this.deadlineMinutesTimestamp + ]; + } + + protected getSwapParametersByMethod( + method: string + ): Parameters['executeContractMethod']> { + const value = this.nativeValueToSend; + + return [ + EDDY_FINANCE_MODE_SWAP_CONTRACT_ADDRESS, + EDDY_FINANCE_MODE_SWAP_CONTRACT_ABI, + method, + this.getCallParameters(), + { value } + ]; + } + + protected async getSwapData(options: GetContractParamsOptions): Promise { + const directTransactionConfig = await this.encodeDirect({ + ...options, + fromAddress: rubicProxyContractAddress[this.from.blockchain].router, + supportFee: false, + receiverAddress: + options.receiverAddress || rubicProxyContractAddress[this.from.blockchain].router + }); + + const availableDexs = ( + await ProxyCrossChainEvmTrade.getWhitelistedDexes(this.from.blockchain) + ).map(address => address.toLowerCase()); + + const routerAddress = directTransactionConfig.to; + const method = directTransactionConfig.data.slice(0, 10); + + if (!availableDexs.includes(routerAddress.toLowerCase())) { + throw new NotWhitelistedProviderError(routerAddress, undefined, 'dex'); + } + await ProxyCrossChainEvmTrade.checkDexWhiteList( + this.from.blockchain, + routerAddress, + method + ); + + return [ + [ + routerAddress, + routerAddress, + this.from.address, + this.to.address, + this.from.stringWeiAmount, + directTransactionConfig.data, + true + ] + ]; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/izumi-mode/izumi-mode-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/izumi-mode/izumi-mode-provider.ts new file mode 100644 index 0000000..629b9bc --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mode/izumi-mode/izumi-mode-provider.ts @@ -0,0 +1,22 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiModeProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.MODE; + + protected readonly dexAddress = '0x3EF68D3f7664b2805D4E88381b64868a56f88bC4'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0x34bc1b87f60e0a30c0e24FD7Abada70436c71406', + liquidityManagerAddress: '0x19b683A2F45012318d9B2aE1280d68d3eC54D663', + routingTokenAddresses: [ + '0xd988097fb8612cc24eec14542bc03424c656005f', // USDC + wrappedNativeTokensList[BLOCKCHAIN_NAME.MODE]!.address, // WETH + '0xf0f161fda2712db8b566946122a5af183995e2ed' // USDT + ], + multicallAddress: '0x7a524c7e82874226F0b51aade60A1BE4D430Cf0F', + supportedFees: [10000, 3000, 500] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonbeam/curve-moonbeam/curve-moonbeam-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonbeam/curve-moonbeam/curve-moonbeam-provider.ts new file mode 100644 index 0000000..03cbcf2 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonbeam/curve-moonbeam/curve-moonbeam-provider.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { CurveAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider'; +import { CurveMoonbeamTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/moonbeam/curve-moonbeam/curve-moonbeam-trade'; + +export class CurveMoonbeamProvider extends CurveAbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.MOONBEAM; + + public readonly Trade = CurveMoonbeamTrade; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonbeam/curve-moonbeam/curve-moonbeam-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonbeam/curve-moonbeam/curve-moonbeam-trade.ts new file mode 100644 index 0000000..8a676f9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonbeam/curve-moonbeam/curve-moonbeam-trade.ts @@ -0,0 +1,3 @@ +import { CurveAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade'; + +export class CurveMoonbeamTrade extends CurveAbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/default-constants.ts new file mode 100644 index 0000000..7d00722 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/default-constants.ts @@ -0,0 +1,19 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultMoonriverRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.MOONRIVER]!.address, // WMOVR + '0xB44a9B6905aF7c801311e8F4E76932ee959c663C', // USDT + '0xE3F5a90F9cb311505cd691a46596599aA1A0AD7D', // USDC + '0x80A16016cC4A2E6a2CACA8a4a498b1699fF0f844', // DAI + '0x5D9ab5522c64E1F6ef5e3627ECCc093f56167818' // BUSD +]; + +const defaultMoonriverWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.MOONRIVER]!.address; + +export const defaultMoonriverProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultMoonriverRoutingProvidersAddresses, + wethAddress: defaultMoonriverWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/constants.ts new file mode 100644 index 0000000..fcb933a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/constants.ts @@ -0,0 +1,123 @@ +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; +import { defaultMoonriverProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/default-constants'; +import { AbiItem } from 'web3-utils'; + +export const SOLARBEAM_CONTRACT_ADDRESS = '0xAA30eF758139ae4a7f798112902Bf6d65612045f'; + +const routingProvidersAddresses = [ + '0x98878B06940aE243284CA214f92Bb71a2b032B8A', // WMOVR + '0xB44a9B6905aF7c801311e8F4E76932ee959c663C', // USDT + '0xE3F5a90F9cb311505cd691a46596599aA1A0AD7D', // USDC + '0x80A16016cC4A2E6a2CACA8a4a498b1699fF0f844', // DAI + '0x5D9ab5522c64E1F6ef5e3627ECCc093f56167818', // BUSD + '0x6bD193Ee6D2104F14F94E2cA6efefae561A4334B' // SOLAR +]; + +const wethAddress = '0x98878B06940aE243284CA214f92Bb71a2b032B8A'; + +export const SOLARBEAM_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultMoonriverProviderConfiguration, + routingProvidersAddresses, + wethAddress +}; + +export const SOLARBEAM_CONTRACT_ABI = [ + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'getAmountsIn', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'uint256', name: 'fee', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'getAmountsOut', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'uint256', name: 'fee', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactETHForTokens', + inputs: [ + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'swapExactETHForTokensSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactTokensForETH', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [], + name: 'swapExactTokensForETHSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactTokensForTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [], + name: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/solarbeam-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/solarbeam-provider.ts new file mode 100644 index 0000000..542b98f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/solarbeam-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SOLARBEAM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/constants'; +import { SolarbeamTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/solarbeam-trade'; + +export class SolarbeamProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.MOONRIVER; + + public readonly UniswapV2TradeClass = SolarbeamTrade; + + public readonly providerSettings = SOLARBEAM_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/solarbeam-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/solarbeam-trade.ts new file mode 100644 index 0000000..9fbae60 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/solarbeam-trade.ts @@ -0,0 +1,40 @@ +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { ContractMulticallResponse } from 'src/core/blockchain/web3-public-service/web3-public/models/contract-multicall-response'; +import { Injector } from 'src/core/injector/injector'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { DefaultRoutesMethodArgument } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/route-method-arguments'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { + SOLARBEAM_CONTRACT_ABI, + SOLARBEAM_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/solarbeam/constants'; + +export class SolarbeamTrade extends UniswapV2AbstractTrade { + public static readonly contractAbi = SOLARBEAM_CONTRACT_ABI; + + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SOLAR_BEAM; + } + + public static callForRoutes( + blockchain: EvmBlockchainName, + exact: Exact, + routesMethodArguments: DefaultRoutesMethodArgument[] + ): Promise[]> { + const web3Public = Injector.web3PublicService.getWeb3Public(blockchain); + return web3Public.multicallContractMethod( + this.getDexContractAddress(blockchain), + this.contractAbi, + exact === 'input' ? 'getAmountsOut' : 'getAmountsIn', + routesMethodArguments.map(args => args.concat(this.feeParameter)) + ); + } + + private static readonly feeParameter = '25'; + + public readonly dexContractAddress = SOLARBEAM_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/constants.ts new file mode 100644 index 0000000..cec9067 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/constants.ts @@ -0,0 +1,5 @@ +import { defaultMoonriverProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/default-constants'; + +export const SUSHI_SWAP_MOONRIVER_CONTRACT_ADDRESS = '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'; + +export const SUSHI_SWAP_MOONRIVER_PROVIDER_CONFIGURATION = defaultMoonriverProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/sushi-swap-moonriver-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/sushi-swap-moonriver-provider.ts new file mode 100644 index 0000000..a602d96 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/sushi-swap-moonriver-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SUSHI_SWAP_MOONRIVER_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/constants'; +import { SushiSwapMoonriverTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/sushi-swap-moonriver-trade'; + +export class SushiSwapMoonriverProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.MOONRIVER; + + public readonly UniswapV2TradeClass = SushiSwapMoonriverTrade; + + public readonly providerSettings = SUSHI_SWAP_MOONRIVER_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/sushi-swap-moonriver-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/sushi-swap-moonriver-trade.ts new file mode 100644 index 0000000..84bc857 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/sushi-swap-moonriver-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { SUSHI_SWAP_MOONRIVER_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/moonriver/sushi-swap-moonriver/constants'; + +export class SushiSwapMoonriverTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SUSHI_SWAP; + } + + public readonly dexContractAddress = SUSHI_SWAP_MOONRIVER_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mumbai/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mumbai/default-constants.ts new file mode 100644 index 0000000..e60bbe1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mumbai/default-constants.ts @@ -0,0 +1,20 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const wethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.MUMBAI]!.address; + +const routingProvidersAddresses = [ + wethAddress, // WMATIC + '0x6De33698e9e9b787e09d3Bd7771ef63557E148bb', // USDC + '0xD9d1034ef3d21221F008C7e96346CA999966752C', // WUSDC + '0x19d66abd20fb2a0fc046c139d5af1e97f09a695e', // USDC2 + '0x2a655231e814e71015ff991d90c5790b5de82b94', // WETH + '0x6d8873f56a56f0af376091bedddd149f3592e854' // DAI +]; + +export const defaultMumbaiProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 3, + routingProvidersAddresses, + wethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mumbai/quick-swap-mumbai/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mumbai/quick-swap-mumbai/constants.ts new file mode 100644 index 0000000..1d3128f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mumbai/quick-swap-mumbai/constants.ts @@ -0,0 +1,5 @@ +import { defaultMumbaiProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/mumbai/default-constants'; + +export const QUICK_SWAP_MUMBAI_CONTRACT_ADDRESS = '0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff'; + +export const QUICK_SWAP_MUMBAI_PROVIDER_CONFIGURATION = defaultMumbaiProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mumbai/quick-swap-mumbai/quick-swap-mumbai-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mumbai/quick-swap-mumbai/quick-swap-mumbai-provider.ts new file mode 100644 index 0000000..758bf6a --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mumbai/quick-swap-mumbai/quick-swap-mumbai-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { QUICK_SWAP_MUMBAI_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/mumbai/quick-swap-mumbai/constants'; +import { QuickSwapMumbaiTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/mumbai/quick-swap-mumbai/quick-swap-mumbai-trade'; + +export class QuickSwapMumbaiProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.MUMBAI; + + public readonly UniswapV2TradeClass = QuickSwapMumbaiTrade; + + public readonly providerSettings = QUICK_SWAP_MUMBAI_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mumbai/quick-swap-mumbai/quick-swap-mumbai-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mumbai/quick-swap-mumbai/quick-swap-mumbai-trade.ts new file mode 100644 index 0000000..32a20ae --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/mumbai/quick-swap-mumbai/quick-swap-mumbai-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { QUICK_SWAP_MUMBAI_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/mumbai/quick-swap-mumbai/constants'; + +export class QuickSwapMumbaiTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.QUICK_SWAP; + } + + public readonly dexContractAddress = QUICK_SWAP_MUMBAI_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/oasis/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/oasis/default-constants.ts new file mode 100644 index 0000000..198b148 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/oasis/default-constants.ts @@ -0,0 +1,20 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultOasisRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.OASIS]!.address, // WROSE + '0x94fbfFe5698DB6f54d6Ca524DbE673a7729014Be', // USDC + '0x6Cb9750a92643382e020eA9a170AbB83Df05F30B', // USDT + '0x2bF9b864cdc97b08B6D79ad4663e71B8aB65c45c', // DAI + '0xdC19A122e268128B5eE20366299fc7b5b199C8e3', // weUSDT + '0xf02b3e437304892105992512539F769423a515Cb' // YUZU +]; + +const defaultOasisWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.OASIS]!.address; + +export const defaultOasisProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultOasisRoutingProvidersAddresses, + wethAddress: defaultOasisWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/oasis/yuzu-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/oasis/yuzu-swap/constants.ts new file mode 100644 index 0000000..8ac7458 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/oasis/yuzu-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultOasisProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/oasis/default-constants'; + +export const YUZU_SWAP_CONTRACT_ADDRESS = '0x250d48C5E78f1E85F7AB07FEC61E93ba703aE668'; + +export const YUZU_SWAP_PROVIDER_CONFIGURATION = defaultOasisProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/oasis/yuzu-swap/yuzu-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/oasis/yuzu-swap/yuzu-swap-provider.ts new file mode 100644 index 0000000..f31f700 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/oasis/yuzu-swap/yuzu-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { YUZU_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/oasis/yuzu-swap/constants'; +import { YuzuSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/oasis/yuzu-swap/yuzu-swap-trade'; + +export class YuzuSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.OASIS; + + public readonly UniswapV2TradeClass = YuzuSwapTrade; + + public readonly providerSettings = YUZU_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/oasis/yuzu-swap/yuzu-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/oasis/yuzu-swap/yuzu-swap-trade.ts new file mode 100644 index 0000000..d2b37a6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/oasis/yuzu-swap/yuzu-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { YUZU_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/oasis/yuzu-swap/constants'; + +export class YuzuSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.YUZU_SWAP; + } + + public readonly dexContractAddress = YUZU_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/optimism/curve-optimism/curve-optimism-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/optimism/curve-optimism/curve-optimism-provider.ts new file mode 100644 index 0000000..b0188d1 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/optimism/curve-optimism/curve-optimism-provider.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { CurveAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider'; +import { CurveOptimismTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/optimism/curve-optimism/curve-optimism-trade'; + +export class CurveOptimismProvider extends CurveAbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.OPTIMISM; + + public readonly Trade = CurveOptimismTrade; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/optimism/curve-optimism/curve-optimism-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/optimism/curve-optimism/curve-optimism-trade.ts new file mode 100644 index 0000000..2a9cf10 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/optimism/curve-optimism/curve-optimism-trade.ts @@ -0,0 +1,3 @@ +import { CurveAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade'; + +export class CurveOptimismTrade extends CurveAbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/default-constants.ts new file mode 100644 index 0000000..912d41b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/default-constants.ts @@ -0,0 +1,21 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultPolygonZKEVMRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.POLYGON_ZKEVM]!.address, // WETH + '0xC5015b9d9161Dca7e18e32f6f25C4aD850731Fd4', // DAI + '0x1E4a5963aBFD975d8c9021ce480b42188849D41d', // USDT + '0xA8CE8aee21bC2A48a5EF670afCc9274C7bbbC035', // USDC + '0x68286607A1d43602d880D349187c3c48c0fD05E6', // QUICK + '0xEA034fb02eB1808C2cc3adbC15f447B93CbE08e1' // wBTC +]; + +const defaultPolygonZKEVMWethAddress = + wrappedNativeTokensList[BLOCKCHAIN_NAME.POLYGON_ZKEVM]!.address; + +export const defaultPolygonZKEVMProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 3, + routingProvidersAddresses: defaultPolygonZKEVMRoutingProvidersAddresses, + wethAddress: defaultPolygonZKEVMWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/pancake-router-polygon-zkevm/pancake-router-polygon-zkevm-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/pancake-router-polygon-zkevm/pancake-router-polygon-zkevm-provider.ts new file mode 100644 index 0000000..0466ff3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/pancake-router-polygon-zkevm/pancake-router-polygon-zkevm-provider.ts @@ -0,0 +1,21 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { PancakeRouterProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/pancake-router/pancake-router-provider'; +import { polygonZkEvm } from 'viem/chains'; + +export class PancakeRouterPolygonZkEvmProvider extends PancakeRouterProvider { + public readonly blockchain = BLOCKCHAIN_NAME.POLYGON_ZKEVM; + + protected readonly chain = polygonZkEvm; + + protected readonly dexAddress = '0x678Aa4bF4E210cf2166753e054d5b7c31cc7fa86'; + + protected readonly v3subgraphAddress = + 'https://api.studio.thegraph.com/query/45376/exchange-v3-polygon-zkevm/v0.0.0'; + + protected readonly v2subgraphAddress = + 'https://api.studio.thegraph.com/query/45376/exchange-v2-polygon-zkevm/version/latest'; + + protected readonly maxHops = 2; + + protected readonly maxSplits = 2; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/constants/provider-configuration.ts new file mode 100644 index 0000000..ef91dbe --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/constants/provider-configuration.ts @@ -0,0 +1,9 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const QUICK_SWAP_V3_POLYGON_ZKEVM_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = + { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.POLYGON_ZKEVM]!.address, + maxTransitTokens: 3 + }; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/constants/swap-router-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/constants/swap-router-contract-data.ts new file mode 100644 index 0000000..3c39835 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/constants/swap-router-contract-data.ts @@ -0,0 +1,2 @@ +export const QUICK_SWAP_V3_POLYGON_ZKEVM_ROUTER_CONTRACT_ADDRESS = + '0xf6ad3ccf71abb3e12becf6b3d2a74c963859adcd'; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/quick-swap-v3-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/quick-swap-v3-provider.ts new file mode 100644 index 0000000..80dcfb2 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/quick-swap-v3-provider.ts @@ -0,0 +1,62 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { createTokenNativeAddressProxyInPathStartAndEnd } from 'src/features/common/utils/token-native-address-proxy'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { QuickswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/quickswap-v3/quickswap-v3-quoter-controller'; +import { UniswapV3AlgebraTradeStructOmitPath } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UniswapV3AlgebraAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider'; +import { QUICK_SWAP_V3_ROUTER_CONTRACT_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/constants/swap-router-contract-data'; +import { QuickSwapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-route'; +import { defaultPolygonZKEVMProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/default-constants'; +import { QUICK_SWAP_V3_POLYGON_ZKEVM_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/constants/provider-configuration'; +import { QUICK_SWAP_V3_POLYGON_ZKEVM_ROUTER_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/constants/swap-router-contract-data'; +import { QuickSwapV3PolygonZKEVMTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/quick-swap-v3-trade'; +import { + QUICK_SWAP_V3_POLYGON_ZKEVM_QUOTER_CONTRACT_ABI, + QUICK_SWAP_V3_POLYGON_ZKEVM_QUOTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/utils/quoter-controller/constants/quoter-contract-data'; + +export class QuickSwapV3PolygonZKEVMProvider extends UniswapV3AlgebraAbstractProvider { + protected readonly contractAddress = QUICK_SWAP_V3_POLYGON_ZKEVM_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = QUICK_SWAP_V3_ROUTER_CONTRACT_ABI; + + public readonly blockchain = BLOCKCHAIN_NAME.POLYGON_ZKEVM; + + protected readonly OnChainTradeClass = QuickSwapV3PolygonZKEVMTrade; + + protected readonly quoterController = new QuickswapV3QuoterController( + this.blockchain, + defaultPolygonZKEVMProviderConfiguration.routingProvidersAddresses, + QUICK_SWAP_V3_POLYGON_ZKEVM_QUOTER_CONTRACT_ADDRESS, + QUICK_SWAP_V3_POLYGON_ZKEVM_QUOTER_CONTRACT_ABI + ); + + public readonly providerConfiguration = QUICK_SWAP_V3_POLYGON_ZKEVM_PROVIDER_CONFIGURATION; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.QUICK_SWAP_V3; + } + + protected createTradeInstance( + tradeStruct: UniswapV3AlgebraTradeStructOmitPath, + route: QuickSwapV3Route, + providerAddress: string + ): QuickSwapV3PolygonZKEVMTrade { + const path = createTokenNativeAddressProxyInPathStartAndEnd( + route.path, + EvmWeb3Pure.nativeTokenAddress + ); + return new QuickSwapV3PolygonZKEVMTrade( + { + ...tradeStruct, + path, + route + }, + providerAddress + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/quick-swap-v3-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/quick-swap-v3-trade.ts new file mode 100644 index 0000000..3e20bba --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/quick-swap-v3-trade.ts @@ -0,0 +1,75 @@ +import { Token } from 'src/common/tokens'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade'; +import { QUICK_SWAP_V3_ROUTER_CONTRACT_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/constants/swap-router-contract-data'; +import { QuickSwapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-route'; +import { QuickSwapV3TradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-trade-struct'; +import { QUICK_SWAP_V3_POLYGON_ZKEVM_ROUTER_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/constants/swap-router-contract-data'; + +export class QuickSwapV3PolygonZKEVMTrade extends UniswapV3AlgebraAbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.QUICK_SWAP_V3; + } + + public readonly dexContractAddress = QUICK_SWAP_V3_POLYGON_ZKEVM_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = QUICK_SWAP_V3_ROUTER_CONTRACT_ABI; + + protected readonly unwrapWethMethodName = 'unwrapWNativeToken'; + + private readonly route: QuickSwapV3Route; + + public readonly wrappedPath: ReadonlyArray; + + constructor(tradeStruct: QuickSwapV3TradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this.route = tradeStruct.route; + + this.wrappedPath = this.route.path; + } + + /** + * Returns swap `exactInput` method's name and arguments to use in Swap contract. + */ + protected getSwapRouterExactInputMethodData(walletAddress: string): MethodData { + const amountParams = this.getAmountParams(); + + if (this.route.path.length === 2 && this.route?.path?.[0] && this.route?.path?.[1]) { + const methodName = this.exact === 'input' ? 'exactInputSingle' : 'exactOutputSingle'; + + return { + methodName, + methodArguments: [ + [ + this.route.path[0].address, + this.route.path[1].address, + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams, + 0 + ] + ] + }; + } + + const methodName = this.exact === 'input' ? 'exactInput' : 'exactOutput'; + + return { + methodName, + methodArguments: [ + [ + AlgebraQuoterController.getEncodedPath(this.route.path), + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams + ] + ] + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/utils/quoter-controller/constants/quoter-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/utils/quoter-controller/constants/quoter-contract-data.ts new file mode 100644 index 0000000..f8bafbc --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon-zkevm/quick-swap-v3/utils/quoter-controller/constants/quoter-contract-data.ts @@ -0,0 +1,104 @@ +import { AbiItem } from 'web3-utils'; + +export const QUICK_SWAP_V3_POLYGON_ZKEVM_QUOTER_CONTRACT_ADDRESS = + '0x55BeE1bD3Eb9986f6d2d963278de09eE92a3eF1D'; + +export const QUICK_SWAP_V3_POLYGON_ZKEVM_QUOTER_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'address', name: '_factory', type: 'address' }, + { internalType: 'address', name: '_WNativeToken', type: 'address' }, + { internalType: 'address', name: '_poolDeployer', type: 'address' } + ], + stateMutability: 'nonpayable', + type: 'constructor' + }, + { + inputs: [], + name: 'WNativeToken', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'int256', name: 'amount0Delta', type: 'int256' }, + { internalType: 'int256', name: 'amount1Delta', type: 'int256' }, + { internalType: 'bytes', name: 'path', type: 'bytes' } + ], + name: 'algebraSwapCallback', + outputs: [], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'factory', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'poolDeployer', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' } + ], + name: 'quoteExactInput', + outputs: [ + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint16[]', name: 'fees', type: 'uint16[]' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactInputSingle', + outputs: [ + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint16', name: 'fee', type: 'uint16' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' } + ], + name: 'quoteExactOutput', + outputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint16[]', name: 'fees', type: 'uint16[]' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactOutputSingle', + outputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint16', name: 'fee', type: 'uint16' } + ], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/algebra-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/algebra-provider.ts new file mode 100644 index 0000000..22f6c05 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/algebra-provider.ts @@ -0,0 +1,64 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { createTokenNativeAddressProxyInPathStartAndEnd } from 'src/features/common/utils/token-native-address-proxy'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraTradeStructOmitPath } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UniswapV3AlgebraAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider'; +import { AlgebraTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/algebra-trade'; +import { ALGEBRA_V3_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/constants/provider-configuration'; +import { + ALGEBRA_SWAP_ROUTER_CONTRACT_ABI, + ALGEBRA_SWAP_ROUTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/constants/swap-router-contract-data'; +import { AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/models/algebra-route'; +import { + ALGEBRA_QUOTER_CONTRACT_ABI, + ALGEBRA_QUOTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/utils/quoter-controller/constants/quoter-contract-data'; +import { defaultPolygonProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/default-constants'; + +export class AlgebraProvider extends UniswapV3AlgebraAbstractProvider { + public readonly contractAddress = ALGEBRA_SWAP_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = ALGEBRA_SWAP_ROUTER_CONTRACT_ABI; + + public readonly blockchain = BLOCKCHAIN_NAME.POLYGON; + + protected readonly OnChainTradeClass = AlgebraTrade; + + protected readonly quoterController = new AlgebraQuoterController( + this.blockchain, + defaultPolygonProviderConfiguration.routingProvidersAddresses, + ALGEBRA_QUOTER_CONTRACT_ADDRESS, + ALGEBRA_QUOTER_CONTRACT_ABI + ); + + public readonly providerConfiguration = ALGEBRA_V3_PROVIDER_CONFIGURATION; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ALGEBRA; + } + + protected createTradeInstance( + tradeStruct: UniswapV3AlgebraTradeStructOmitPath, + route: AlgebraRoute, + providerAddress: string + ): AlgebraTrade { + const path = createTokenNativeAddressProxyInPathStartAndEnd( + route.path, + EvmWeb3Pure.nativeTokenAddress + ); + return new AlgebraTrade( + { + ...tradeStruct, + path, + route + }, + providerAddress + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/algebra-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/algebra-trade.ts new file mode 100644 index 0000000..1aa4ede --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/algebra-trade.ts @@ -0,0 +1,77 @@ +import { Token } from 'src/common/tokens'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade'; +import { + ALGEBRA_SWAP_ROUTER_CONTRACT_ABI, + ALGEBRA_SWAP_ROUTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/constants/swap-router-contract-data'; +import { AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/models/algebra-route'; +import { AlgebraTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/models/algebra-trade-struct'; + +export class AlgebraTrade extends UniswapV3AlgebraAbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ALGEBRA; + } + + public readonly dexContractAddress = ALGEBRA_SWAP_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = ALGEBRA_SWAP_ROUTER_CONTRACT_ABI; + + protected readonly unwrapWethMethodName = 'unwrapWNativeToken'; + + private readonly route: AlgebraRoute; + + public readonly wrappedPath: ReadonlyArray; + + constructor(tradeStruct: AlgebraTradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this.route = tradeStruct.route; + + this.wrappedPath = this.route.path; + } + + /** + * Returns swap `exactInput` method's name and arguments to use in Swap contract. + */ + protected getSwapRouterExactInputMethodData(walletAddress: string): MethodData { + const amountParams = this.getAmountParams(); + + if (this.route.path.length === 2 && this.route?.path?.[0] && this.route?.path?.[1]) { + const methodName = this.exact === 'input' ? 'exactInputSingle' : 'exactOutputSingle'; + + return { + methodName, + methodArguments: [ + [ + this.route.path[0].address, + this.route.path[1].address, + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams, + 0 + ] + ] + }; + } + + const methodName = this.exact === 'input' ? 'exactInput' : 'exactOutput'; + + return { + methodName, + methodArguments: [ + [ + AlgebraQuoterController.getEncodedPath(this.route.path), + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams + ] + ] + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/constants/provider-configuration.ts new file mode 100644 index 0000000..80ad908 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/constants/provider-configuration.ts @@ -0,0 +1,8 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const ALGEBRA_V3_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.POLYGON]!.address, + maxTransitTokens: 1 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/constants/swap-router-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/constants/swap-router-contract-data.ts new file mode 100644 index 0000000..213d395 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/constants/swap-router-contract-data.ts @@ -0,0 +1,109 @@ +import { AbiItem } from 'web3-utils'; + +export const ALGEBRA_SWAP_ROUTER_CONTRACT_ADDRESS = '0x89D6B81A1Ef25894620D05ba843d83B0A296239e'; + +export const ALGEBRA_SWAP_ROUTER_CONTRACT_ABI = [ + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactInputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactInputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactOutputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactOutputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' }], + name: 'multicall', + outputs: [{ internalType: 'bytes[]', name: 'results', type: 'bytes[]' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountMinimum', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'unwrapWNativeToken', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { stateMutability: 'payable', type: 'receive' } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/models/algebra-route.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/models/algebra-route.ts new file mode 100644 index 0000000..8d8aa09 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/models/algebra-route.ts @@ -0,0 +1,9 @@ +import { Token } from 'src/common/tokens'; +import { UniswapV3AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route'; + +export interface AlgebraRoute extends UniswapV3AlgebraRoute { + /** + * List of pools' contract addresses to use in a trade's route. + */ + path: Token[]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/models/algebra-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/models/algebra-trade-struct.ts new file mode 100644 index 0000000..7fb6a65 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/models/algebra-trade-struct.ts @@ -0,0 +1,6 @@ +import { UniswapV3AlgebraTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/models/algebra-route'; + +export interface AlgebraTradeStruct extends UniswapV3AlgebraTradeStruct { + route: AlgebraRoute; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/utils/quoter-controller/constants/quoter-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/utils/quoter-controller/constants/quoter-contract-data.ts new file mode 100644 index 0000000..467f424 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/utils/quoter-controller/constants/quoter-contract-data.ts @@ -0,0 +1,142 @@ +import { AbiItem } from 'web3-utils'; + +export const ALGEBRA_QUOTER_CONTRACT_ADDRESS = '0xAaaCfe8F51B8baA4286ea97ddF145e946d5e7f46'; + +export const ALGEBRA_QUOTER_CONTRACT_ABI = [ + { + inputs: [ + { + internalType: 'bytes', + name: 'path', + type: 'bytes' + }, + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + } + ], + name: 'quoteExactInput', + outputs: [ + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256' + }, + { + internalType: 'uint16[]', + name: 'fees', + type: 'uint16[]' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'tokenIn', + type: 'address' + }, + { + internalType: 'address', + name: 'tokenOut', + type: 'address' + }, + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint160', + name: 'limitSqrtPrice', + type: 'uint160' + } + ], + name: 'quoteExactInputSingle', + outputs: [ + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256' + }, + { + internalType: 'uint16', + name: 'fee', + type: 'uint16' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'bytes', + name: 'path', + type: 'bytes' + }, + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256' + } + ], + name: 'quoteExactOutput', + outputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint16[]', + name: 'fees', + type: 'uint16[]' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'tokenIn', + type: 'address' + }, + { + internalType: 'address', + name: 'tokenOut', + type: 'address' + }, + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256' + }, + { + internalType: 'uint160', + name: 'limitSqrtPrice', + type: 'uint160' + } + ], + name: 'quoteExactOutputSingle', + outputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint16', + name: 'fee', + type: 'uint16' + } + ], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/utils/quoter-controller/constants/router-tokens.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/utils/quoter-controller/constants/router-tokens.ts new file mode 100644 index 0000000..58b2782 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/algebra/utils/quoter-controller/constants/router-tokens.ts @@ -0,0 +1,14 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +/** + * Most popular tokens in uni v3 to use in a route. + */ +export const ROUTER_TOKENS: string[] = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.POLYGON]!.address, // WMATIC + '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619', // WETH' + '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', // DAI' + '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', // USDT' + '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC' + '0x831753DD7087CaC61aB5644b308642cc1c33Dc13' // QUICK' +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/curve-polygon/curve-polygon-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/curve-polygon/curve-polygon-provider.ts new file mode 100644 index 0000000..e8a0e70 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/curve-polygon/curve-polygon-provider.ts @@ -0,0 +1,9 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { CurveAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-provider'; +import { CurvePolygonTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/curve-polygon/curve-polygon-trade'; + +export class CurvePolygonProvider extends CurveAbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.POLYGON; + + public readonly Trade = CurvePolygonTrade; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/curve-polygon/curve-polygon-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/curve-polygon/curve-polygon-trade.ts new file mode 100644 index 0000000..852871d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/curve-polygon/curve-polygon-trade.ts @@ -0,0 +1,3 @@ +import { CurveAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/curve-provider/curve-abstract-trade'; + +export class CurvePolygonTrade extends CurveAbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/default-constants.ts new file mode 100644 index 0000000..6b6557f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/default-constants.ts @@ -0,0 +1,20 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultPolygonRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.POLYGON]!.address, // WMATIC + '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619', // WETH + '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', // DAI + '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', // USDT + '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC + '0x831753DD7087CaC61aB5644b308642cc1c33Dc13' // QUICK +]; + +const defaultPolygonWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.POLYGON]!.address; + +export const defaultPolygonProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 3, + routingProvidersAddresses: defaultPolygonRoutingProvidersAddresses, + wethAddress: defaultPolygonWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/constants/provider-configuration.ts new file mode 100644 index 0000000..5d13fa0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/constants/provider-configuration.ts @@ -0,0 +1,8 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const QUICK_SWAP_V3_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.POLYGON]!.address, + maxTransitTokens: 1 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/constants/swap-router-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/constants/swap-router-contract-data.ts new file mode 100644 index 0000000..ca8f195 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/constants/swap-router-contract-data.ts @@ -0,0 +1,271 @@ +import { AbiItem } from 'web3-utils'; + +export const QUICK_SWAP_V3_ROUTER_CONTRACT_ADDRESS = '0xf5b509bB0909a69B1c207E495f687a596C168E12'; + +export const QUICK_SWAP_V3_ROUTER_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'address', name: '_factory', type: 'address' }, + { internalType: 'address', name: '_WNativeToken', type: 'address' }, + { internalType: 'address', name: '_poolDeployer', type: 'address' } + ], + stateMutability: 'nonpayable', + type: 'constructor' + }, + { + inputs: [], + name: 'WNativeToken', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'int256', name: 'amount0Delta', type: 'int256' }, + { internalType: 'int256', name: 'amount1Delta', type: 'int256' }, + { internalType: 'bytes', name: '_data', type: 'bytes' } + ], + name: 'algebraSwapCallback', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactInputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactInputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactInputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactInputSingleSupportingFeeOnTransferTokens', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' } + ], + internalType: 'struct ISwapRouter.ExactOutputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + internalType: 'struct ISwapRouter.ExactOutputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'exactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [], + name: 'factory', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' }], + name: 'multicall', + outputs: [{ internalType: 'bytes[]', name: 'results', type: 'bytes[]' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [], + name: 'poolDeployer', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'refundNativeToken', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256', name: 'value', type: 'uint256' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint8', name: 'v', type: 'uint8' }, + { internalType: 'bytes32', name: 'r', type: 'bytes32' }, + { internalType: 'bytes32', name: 's', type: 'bytes32' } + ], + name: 'selfPermit', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256', name: 'nonce', type: 'uint256' }, + { internalType: 'uint256', name: 'expiry', type: 'uint256' }, + { internalType: 'uint8', name: 'v', type: 'uint8' }, + { internalType: 'bytes32', name: 'r', type: 'bytes32' }, + { internalType: 'bytes32', name: 's', type: 'bytes32' } + ], + name: 'selfPermitAllowed', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256', name: 'nonce', type: 'uint256' }, + { internalType: 'uint256', name: 'expiry', type: 'uint256' }, + { internalType: 'uint8', name: 'v', type: 'uint8' }, + { internalType: 'bytes32', name: 'r', type: 'bytes32' }, + { internalType: 'bytes32', name: 's', type: 'bytes32' } + ], + name: 'selfPermitAllowedIfNecessary', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256', name: 'value', type: 'uint256' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint8', name: 'v', type: 'uint8' }, + { internalType: 'bytes32', name: 'r', type: 'bytes32' }, + { internalType: 'bytes32', name: 's', type: 'bytes32' } + ], + name: 'selfPermitIfNecessary', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256', name: 'amountMinimum', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'sweepToken', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256', name: 'amountMinimum', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'feeBips', type: 'uint256' }, + { internalType: 'address', name: 'feeRecipient', type: 'address' } + ], + name: 'sweepTokenWithFee', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountMinimum', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'unwrapWNativeToken', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountMinimum', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'feeBips', type: 'uint256' }, + { internalType: 'address', name: 'feeRecipient', type: 'address' } + ], + name: 'unwrapWNativeTokenWithFee', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { stateMutability: 'payable', type: 'receive' } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-route.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-route.ts new file mode 100644 index 0000000..32b964b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-route.ts @@ -0,0 +1,9 @@ +import { Token } from 'src/common/tokens'; +import { UniswapV3AlgebraRoute } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-route'; + +export interface QuickSwapV3Route extends UniswapV3AlgebraRoute { + /** + * List of pools' contract addresses to use in a trade's route. + */ + path: Token[]; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-trade-struct.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-trade-struct.ts new file mode 100644 index 0000000..7e23fc5 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-trade-struct.ts @@ -0,0 +1,6 @@ +import { UniswapV3AlgebraTradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { QuickSwapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-route'; + +export interface QuickSwapV3TradeStruct extends UniswapV3AlgebraTradeStruct { + route: QuickSwapV3Route; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/quick-swap-v3-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/quick-swap-v3-provider.ts new file mode 100644 index 0000000..d6d6a12 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/quick-swap-v3-provider.ts @@ -0,0 +1,64 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { createTokenNativeAddressProxyInPathStartAndEnd } from 'src/features/common/utils/token-native-address-proxy'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraTradeStructOmitPath } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UniswapV3AlgebraAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider'; +import { defaultPolygonProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/default-constants'; +import { QUICK_SWAP_V3_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/constants/provider-configuration'; +import { + QUICK_SWAP_V3_ROUTER_CONTRACT_ABI, + QUICK_SWAP_V3_ROUTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/constants/swap-router-contract-data'; +import { QuickSwapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-route'; +import { QuickSwapV3Trade } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/quick-swap-v3-trade'; +import { + QUICK_SWAP_V3_QUOTER_CONTRACT_ABI, + QUICK_SWAP_V3_QUOTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/utils/quoter-controller/constants/quoter-contract-data'; + +export class QuickSwapV3Provider extends UniswapV3AlgebraAbstractProvider { + protected readonly contractAddress = QUICK_SWAP_V3_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = QUICK_SWAP_V3_ROUTER_CONTRACT_ABI; + + public readonly blockchain = BLOCKCHAIN_NAME.POLYGON; + + protected readonly OnChainTradeClass = QuickSwapV3Trade; + + protected readonly providerConfiguration = QUICK_SWAP_V3_PROVIDER_CONFIGURATION; + + protected readonly quoterController = new AlgebraQuoterController( + this.blockchain, + defaultPolygonProviderConfiguration.routingProvidersAddresses, + QUICK_SWAP_V3_QUOTER_CONTRACT_ADDRESS, + QUICK_SWAP_V3_QUOTER_CONTRACT_ABI + ); + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.QUICK_SWAP_V3; + } + + protected createTradeInstance( + tradeStruct: UniswapV3AlgebraTradeStructOmitPath, + route: QuickSwapV3Route, + providerAddress: string + ): QuickSwapV3Trade { + const path = createTokenNativeAddressProxyInPathStartAndEnd( + route.path, + EvmWeb3Pure.nativeTokenAddress + ); + return new QuickSwapV3Trade( + { + ...tradeStruct, + path, + route + }, + providerAddress + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/quick-swap-v3-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/quick-swap-v3-trade.ts new file mode 100644 index 0000000..2e60069 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/quick-swap-v3-trade.ts @@ -0,0 +1,77 @@ +import { Token } from 'src/common/tokens'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/algebra/algebra-quoter-controller'; +import { UniswapV3AlgebraAbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade'; +import { + QUICK_SWAP_V3_ROUTER_CONTRACT_ABI, + QUICK_SWAP_V3_ROUTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/constants/swap-router-contract-data'; +import { QuickSwapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-route'; +import { QuickSwapV3TradeStruct } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/models/quick-swap-v3-trade-struct'; + +export class QuickSwapV3Trade extends UniswapV3AlgebraAbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.QUICK_SWAP_V3; + } + + public readonly dexContractAddress = QUICK_SWAP_V3_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = QUICK_SWAP_V3_ROUTER_CONTRACT_ABI; + + protected readonly unwrapWethMethodName = 'unwrapWNativeToken'; + + private readonly route: QuickSwapV3Route; + + public readonly wrappedPath: ReadonlyArray; + + constructor(tradeStruct: QuickSwapV3TradeStruct, providerAddress: string) { + super(tradeStruct, providerAddress); + + this.route = tradeStruct.route; + + this.wrappedPath = this.route.path; + } + + /** + * Returns swap `exactInput` method's name and arguments to use in Swap contract. + */ + protected getSwapRouterExactInputMethodData(walletAddress: string): MethodData { + const amountParams = this.getAmountParams(); + + if (this.route.path.length === 2 && this.route?.path?.[0] && this.route?.path?.[1]) { + const methodName = this.exact === 'input' ? 'exactInputSingle' : 'exactOutputSingle'; + + return { + methodName, + methodArguments: [ + [ + this.route.path[0].address, + this.route.path[1].address, + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams, + 0 + ] + ] + }; + } + + const methodName = this.exact === 'input' ? 'exactInput' : 'exactOutput'; + + return { + methodName, + methodArguments: [ + [ + AlgebraQuoterController.getEncodedPath(this.route.path), + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams + ] + ] + }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/utils/quoter-controller/constants/quoter-contract-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/utils/quoter-controller/constants/quoter-contract-data.ts new file mode 100644 index 0000000..f3d22ea --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap-v3/utils/quoter-controller/constants/quoter-contract-data.ts @@ -0,0 +1,103 @@ +import { AbiItem } from 'web3-utils'; + +export const QUICK_SWAP_V3_QUOTER_CONTRACT_ADDRESS = '0xa15F0D7377B2A0C0c10db057f641beD21028FC89'; + +export const QUICK_SWAP_V3_QUOTER_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'address', name: '_factory', type: 'address' }, + { internalType: 'address', name: '_WNativeToken', type: 'address' }, + { internalType: 'address', name: '_poolDeployer', type: 'address' } + ], + stateMutability: 'nonpayable', + type: 'constructor' + }, + { + inputs: [], + name: 'WNativeToken', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'int256', name: 'amount0Delta', type: 'int256' }, + { internalType: 'int256', name: 'amount1Delta', type: 'int256' }, + { internalType: 'bytes', name: 'path', type: 'bytes' } + ], + name: 'algebraSwapCallback', + outputs: [], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'factory', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'poolDeployer', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' } + ], + name: 'quoteExactInput', + outputs: [ + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint16[]', name: 'fees', type: 'uint16[]' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactInputSingle', + outputs: [ + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint16', name: 'fee', type: 'uint16' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' } + ], + name: 'quoteExactOutput', + outputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint16[]', name: 'fees', type: 'uint16[]' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' } + ], + name: 'quoteExactOutputSingle', + outputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint16', name: 'fee', type: 'uint16' } + ], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/constants.ts new file mode 100644 index 0000000..086912f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultPolygonProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/default-constants'; + +export const QUICK_SWAP_CONTRACT_ADDRESS = '0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff'; + +export const QUICK_SWAP_PROVIDER_CONFIGURATION = defaultPolygonProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/quick-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/quick-swap-provider.ts new file mode 100644 index 0000000..d28a976 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/quick-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { QUICK_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/constants'; +import { QuickSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/quick-swap-trade'; + +export class QuickSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.POLYGON; + + public readonly UniswapV2TradeClass = QuickSwapTrade; + + public readonly providerSettings = QUICK_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/quick-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/quick-swap-trade.ts new file mode 100644 index 0000000..da0e962 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/quick-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { QUICK_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/quick-swap/constants'; + +export class QuickSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.QUICK_SWAP; + } + + public readonly dexContractAddress = QUICK_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/constants.ts new file mode 100644 index 0000000..9620f0e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/constants.ts @@ -0,0 +1,5 @@ +import { defaultPolygonProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/default-constants'; + +export const SUSHI_SWAP_POLYGON_CONTRACT_ADDRESS = '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'; + +export const SUSHI_SWAP_POLYGON_PROVIDER_CONFIGURATION = defaultPolygonProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/sushi-swap-polygon-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/sushi-swap-polygon-provider.ts new file mode 100644 index 0000000..7ea580e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/sushi-swap-polygon-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SUSHI_SWAP_POLYGON_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/constants'; +import { SushiSwapPolygonTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/sushi-swap-polygon-trade'; + +export class SushiSwapPolygonProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.POLYGON; + + public readonly UniswapV2TradeClass = SushiSwapPolygonTrade; + + public readonly providerSettings = SUSHI_SWAP_POLYGON_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/sushi-swap-polygon-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/sushi-swap-polygon-trade.ts new file mode 100644 index 0000000..985dbbf --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/sushi-swap-polygon-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { SUSHI_SWAP_POLYGON_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/sushi-swap-polygon/constants'; + +export class SushiSwapPolygonTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SUSHI_SWAP; + } + + public readonly dexContractAddress = SUSHI_SWAP_POLYGON_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/constants/provider-configuration.ts new file mode 100644 index 0000000..1db80a5 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/constants/provider-configuration.ts @@ -0,0 +1,8 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const UNI_SWAP_V3_POLYGON_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.POLYGON]!.address, + maxTransitTokens: 1 +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/constants/router-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/constants/router-configuration.ts new file mode 100644 index 0000000..d08b09e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/constants/router-configuration.ts @@ -0,0 +1,65 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { + UniswapV3RouterConfiguration, + UniswapV3RouterLiquidityPool +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration'; + +/** + * Most popular tokens in uni v3 to use in a route. + */ +const tokensSymbols = ['WMATIC', 'WETH', 'DAI', 'USDT', 'USDC'] as const; + +type TokenSymbol = (typeof tokensSymbols)[number]; + +const routerTokens: Record = { + WMATIC: wrappedNativeTokensList[BLOCKCHAIN_NAME.POLYGON]!.address, + WETH: '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619', + DAI: '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', + USDT: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', + USDC: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174' +}; + +const routerLiquidityPools: UniswapV3RouterLiquidityPool[] = [ + { + poolAddress: '0x167384319B41F7094e62f7506409Eb38079AbfF8', + tokenSymbolA: 'WMATIC', + tokenSymbolB: 'WETH', + fee: 3000 + }, + { + poolAddress: '0x45dDa9cb7c25131DF268515131f647d726f50608', + tokenSymbolA: 'USDC', + tokenSymbolB: 'WETH', + fee: 500 + }, + { + poolAddress: '0x0e44cEb592AcFC5D3F09D996302eB4C499ff8c10', + tokenSymbolA: 'USDC', + tokenSymbolB: 'WETH', + fee: 3000 + }, + { + poolAddress: '0x3F5228d0e7D75467366be7De2c31D0d098bA2C23', + tokenSymbolA: 'USDC', + tokenSymbolB: 'USDT', + fee: 500 + }, + { + poolAddress: '0x88f3C15523544835fF6c738DDb30995339AD57d6', + tokenSymbolA: 'WMATIC', + tokenSymbolB: 'USDC', + fee: 3000 + }, + { + poolAddress: '0x86f1d8390222A3691C28938eC7404A1661E618e0', + tokenSymbolA: 'WMATIC', + tokenSymbolB: 'WETH', + fee: 500 + } +]; + +export const UNI_SWAP_V3_POLYGON_ROUTER_CONFIGURATION: UniswapV3RouterConfiguration = { + tokens: routerTokens, + liquidityPools: routerLiquidityPools +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-provider.ts new file mode 100644 index 0000000..78907a4 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-provider.ts @@ -0,0 +1,21 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-provider'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; +import { UNI_SWAP_V3_POLYGON_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/constants/provider-configuration'; +import { UNI_SWAP_V3_POLYGON_ROUTER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/constants/router-configuration'; +import { UniSwapV3PolygonTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-trade'; + +export class UniSwapV3PolygonProvider extends UniswapV3AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.POLYGON; + + protected readonly OnChainTradeClass = UniSwapV3PolygonTrade; + + protected readonly providerConfiguration = UNI_SWAP_V3_POLYGON_PROVIDER_CONFIGURATION; + + protected readonly routerConfiguration = UNI_SWAP_V3_POLYGON_ROUTER_CONFIGURATION; + + protected readonly quoterController = new UniswapV3QuoterController( + this.blockchain, + this.routerConfiguration + ); +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-trade.ts new file mode 100644 index 0000000..25e1dda --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-trade.ts @@ -0,0 +1,3 @@ +import { UniswapV3AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-trade'; + +export class UniSwapV3PolygonTrade extends UniswapV3AbstractTrade {} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/default-constants.ts new file mode 100644 index 0000000..0de2e98 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/default-constants.ts @@ -0,0 +1,21 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultPulsechainRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.PULSECHAIN]!.address, // WPLS + '0xefD766cCb38EaF1dfd701853BFCe31359239F305', // DAI + '0x15D38573d2feeb82e7ad5187aB8c1D52810B1f07', // USDC + '0x0Cb6F5a34ad42ec934882A05265A7d5F59b51A2f', // USDT + '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH + '0x02DcdD04e3F455D838cd1249292C58f3B79e3C3C', // WETH + '0x95b303987a60c71504d99aa1b13b4da07b0790ab' // PLSX +]; + +const defaultPulsechainWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.PULSECHAIN]!.address; + +export const defaultPulsechainProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultPulsechainRoutingProvidersAddresses, + wethAddress: defaultPulsechainWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v1/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v1/constants.ts new file mode 100644 index 0000000..2385586 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v1/constants.ts @@ -0,0 +1,5 @@ +import { defaultPulsechainProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/pulsechain/default-constants'; + +export const PULSEX_V1_CONTRACT_ADDRESS = '0x98bf93ebf5c380C0e6Ae8e192A7e2AE08edAcc02'; + +export const PULSEX_V1_PROVIDER_CONFIGURATION = defaultPulsechainProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v1/pulsex-v1-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v1/pulsex-v1-provider.ts new file mode 100644 index 0000000..76cc32f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v1/pulsex-v1-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { PULSEX_V1_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v1/constants'; +import { PulseXV1Trade } from 'src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v1/pulsex-v1-trade'; + +export class PulseXV1Provider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.PULSECHAIN; + + public readonly UniswapV2TradeClass = PulseXV1Trade; + + public readonly providerSettings = PULSEX_V1_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v1/pulsex-v1-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v1/pulsex-v1-trade.ts new file mode 100644 index 0000000..abaeae5 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v1/pulsex-v1-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { PULSEX_V1_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v1/constants'; + +export class PulseXV1Trade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.PULSEX_V1; + } + + public readonly dexContractAddress = PULSEX_V1_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v2/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v2/constants.ts new file mode 100644 index 0000000..f1efb82 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v2/constants.ts @@ -0,0 +1,5 @@ +import { defaultPulsechainProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/pulsechain/default-constants'; + +export const PULSEX_V2_CONTRACT_ADDRESS = '0x165C3410fC91EF562C50559f7d2289fEbed552d9'; + +export const PULSEX_V2_PROVIDER_CONFIGURATION = defaultPulsechainProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v2/pulsex-v2-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v2/pulsex-v2-provider.ts new file mode 100644 index 0000000..11f490c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v2/pulsex-v2-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { PULSEX_V2_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v2/constants'; +import { PulseXV2Trade } from 'src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v2/pulsex-v2-trade'; + +export class PulseXV2Provider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.PULSECHAIN; + + public readonly UniswapV2TradeClass = PulseXV2Trade; + + public readonly providerSettings = PULSEX_V2_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v2/pulsex-v2-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v2/pulsex-v2-trade.ts new file mode 100644 index 0000000..0cfd73f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v2/pulsex-v2-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { PULSEX_V2_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/pulsechain/pulsex-v2/constants'; + +export class PulseXV2Trade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.PULSEX_V2; + } + + public readonly dexContractAddress = PULSEX_V2_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v2-scroll-sepolia/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v2-scroll-sepolia/constants.ts new file mode 100644 index 0000000..28f3a36 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v2-scroll-sepolia/constants.ts @@ -0,0 +1,17 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultScrollWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.SCROLL_SEPOLIA]!.address; + +const defaultScrollRoutingProvidersAddresses = [ + defaultScrollWethAddress, // WETH + '0x15Fe86961428E095B064bb52FcF5964bAb834E34' // USDC +]; +export const UNISWAP_V2_SCROLL_SEPOLIA_CONTRACT_ADDRESS = ''; + +export const UNISWAP_V2_SCROLL_SEPOLIA_CONFIGURATION: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultScrollRoutingProvidersAddresses, + wethAddress: defaultScrollWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v2-scroll-sepolia/uni-swap-v2-scroll-sepolia-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v2-scroll-sepolia/uni-swap-v2-scroll-sepolia-provider.ts new file mode 100644 index 0000000..775ec4b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v2-scroll-sepolia/uni-swap-v2-scroll-sepolia-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { UNISWAP_V2_SCROLL_SEPOLIA_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v2-scroll-sepolia/constants'; +import { UniSwapV2ScrollSepoliaTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v2-scroll-sepolia/uni-swap-v2-scroll-sepolia-trade'; + +export class UniSwapV2ScrollSepoliaProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.SCROLL_SEPOLIA; + + public readonly UniswapV2TradeClass = UniSwapV2ScrollSepoliaTrade; + + public readonly providerSettings = UNISWAP_V2_SCROLL_SEPOLIA_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v2-scroll-sepolia/uni-swap-v2-scroll-sepolia-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v2-scroll-sepolia/uni-swap-v2-scroll-sepolia-trade.ts new file mode 100644 index 0000000..56fa1a0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v2-scroll-sepolia/uni-swap-v2-scroll-sepolia-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { UNISWAP_V2_SCROLL_SEPOLIA_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v2-scroll-sepolia/constants'; + +export class UniSwapV2ScrollSepoliaTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.UNISWAP_V2; + } + + public readonly dexContractAddress = UNISWAP_V2_SCROLL_SEPOLIA_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/constants/provider-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/constants/provider-configuration.ts new file mode 100644 index 0000000..eee3a6e --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/constants/provider-configuration.ts @@ -0,0 +1,9 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV3AlgebraProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-provider-configuration'; + +export const UNI_SWAP_V3_SCROLL_SEPOLIA_PROVIDER_CONFIGURATION: UniswapV3AlgebraProviderConfiguration = + { + wethAddress: wrappedNativeTokensList[BLOCKCHAIN_NAME.SCROLL_SEPOLIA]!.address, + maxTransitTokens: 1 + }; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/constants/router-configuration.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/constants/router-configuration.ts new file mode 100644 index 0000000..be43e43 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/constants/router-configuration.ts @@ -0,0 +1,40 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { + UniswapV3RouterConfiguration, + UniswapV3RouterLiquidityPool +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-router-configuration'; + +/** + * Most popular tokens in uni v3 to use in a route. + */ +const tokensSymbols = ['WETH', 'USDC', 'GHO'] as const; + +type TokenSymbol = (typeof tokensSymbols)[number]; + +const routerTokens: Record = { + WETH: wrappedNativeTokensList[BLOCKCHAIN_NAME.SCROLL_SEPOLIA]!.address, + USDC: '0x15Fe86961428E095B064bb52FcF5964bAb834E34', + GHO: '0xD9692f1748aFEe00FACE2da35242417dd05a8615' +}; + +const routerLiquidityPools: UniswapV3RouterLiquidityPool[] = [ + { + poolAddress: '0x60ba72f15c2b133e8ef826602bab511f4c7bca78', + tokenSymbolA: 'USDC', + tokenSymbolB: 'WETH', + fee: 3000 + }, + { + poolAddress: '0xd8ac608580a56fdea4f1d9ef2ce5e4fa09591325', + tokenSymbolA: 'WETH', + tokenSymbolB: 'GHO', + fee: 3000 + } +]; + +export const UNI_SWAP_V3_SCROLL_SEPOLIA_ROUTER_CONFIGURATION: UniswapV3RouterConfiguration = + { + tokens: routerTokens, + liquidityPools: routerLiquidityPools + }; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/uni-swap-v3-scroll-sepolia-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/uni-swap-v3-scroll-sepolia-provider.ts new file mode 100644 index 0000000..f64ef93 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/uni-swap-v3-scroll-sepolia-provider.ts @@ -0,0 +1,89 @@ +import { RubicSdkError } from 'src/common/errors'; +import { Token } from 'src/common/tokens'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { createTokenNativeAddressProxyInPathStartAndEnd } from 'src/features/common/utils/token-native-address-proxy'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV3Route } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/models/uniswap-v3-route'; +import { UNISWAP_V3_QUOTER_CONTRACT_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/constants/quoter-contract-data'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; +import { UniswapV3AlgebraTradeStructOmitPath } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UniswapV3AlgebraAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider'; +import { UNI_SWAP_V3_SCROLL_SEPOLIA_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/constants/provider-configuration'; +import { UNI_SWAP_V3_SCROLL_SEPOLIA_ROUTER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/constants/router-configuration'; +import { UniSwapV3ScrollSepoliaTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/uni-swap-v3-scroll-sepolia-trade'; + +import { UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI } from '../../common/uniswap-v3-abstract/constants/swap-router-contract-abi'; + +export class UniSwapV3ScrollSepoliaProvider extends UniswapV3AlgebraAbstractProvider { + public readonly contractAddress = '0x59a662Ed724F19AD019307126CbEBdcF4b57d6B1'; + + protected readonly contractAbi = UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI; + + public readonly blockchain = BLOCKCHAIN_NAME.SCROLL_SEPOLIA; + + public readonly OnChainTradeClass = UniSwapV3ScrollSepoliaTrade; + + public readonly providerConfiguration = UNI_SWAP_V3_SCROLL_SEPOLIA_PROVIDER_CONFIGURATION; + + public readonly routerConfiguration = UNI_SWAP_V3_SCROLL_SEPOLIA_ROUTER_CONFIGURATION; + + protected readonly quoterController = new UniswapV3QuoterController( + this.blockchain, + this.routerConfiguration, + '0x805488DaA81c1b9e7C5cE3f1DCeA28F21448EC6A', + UNISWAP_V3_QUOTER_CONTRACT_ABI, + '0xB856587fe1cbA8600F75F1b1176E44250B11C788' + ); + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.UNI_SWAP_V3; + } + + protected createTradeInstance( + tradeStruct: UniswapV3AlgebraTradeStructOmitPath, + route: UniswapV3Route, + providerAddress: string + ): UniSwapV3ScrollSepoliaTrade { + const path = this.extractPath(route); + return new this.OnChainTradeClass( + { + ...tradeStruct, + path, + route + }, + providerAddress + ); + } + + private extractPath(route: UniswapV3Route): ReadonlyArray { + const initialPool = route.poolsPath[0]; + if (!initialPool) { + throw new RubicSdkError('Initial pool has to be defined'); + } + const path: Token[] = [ + compareAddresses(initialPool.token0.address, route.initialTokenAddress) + ? initialPool.token0 + : initialPool.token1 + ]; + + const lastToken = path[path.length - 1]; + if (!lastToken) { + throw new RubicSdkError('Last token has to be defined'); + } + + route.poolsPath.forEach(pool => { + path.push( + !compareAddresses(pool.token0.address, lastToken.address) + ? pool.token0 + : pool.token1 + ); + }); + + return createTokenNativeAddressProxyInPathStartAndEnd(path, EvmWeb3Pure.nativeTokenAddress); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/uni-swap-v3-scroll-sepolia-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/uni-swap-v3-scroll-sepolia-trade.ts new file mode 100644 index 0000000..73c302f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll-sepolia/uni-swap-v3-scroll-sepolia/uni-swap-v3-scroll-sepolia-trade.ts @@ -0,0 +1,5 @@ +import { UniswapV3AbstractTrade } from '../../common/uniswap-v3-abstract/uniswap-v3-abstract-trade'; + +export class UniSwapV3ScrollSepoliaTrade extends UniswapV3AbstractTrade { + public readonly dexContractAddress = '0x59a662Ed724F19AD019307126CbEBdcF4b57d6B1'; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll/izumi-scroll/izumi-scroll-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll/izumi-scroll/izumi-scroll-provider.ts new file mode 100644 index 0000000..1cbed35 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll/izumi-scroll/izumi-scroll-provider.ts @@ -0,0 +1,22 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiScrollProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.SCROLL; + + protected readonly dexAddress = '0x2db0AFD0045F3518c77eC6591a542e326Befd3D7'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0x33531bDBFE34fa6Fd5963D0423f7699775AacaaF', + liquidityManagerAddress: '0x1502d025BfA624469892289D45C0352997251728', + routingTokenAddresses: [ + '0xf55bec9cafdbe8730f096aa55dad6d22d44099df', // USDT + wrappedNativeTokensList[BLOCKCHAIN_NAME.SCROLL]!.address, + '0x06efdbff2a14a7c8e15944d1f4a48f9f95f663a4' // USDC + ], + multicallAddress: '0x93E94ef7D2d735fF21C302c765d8A77C1955A311', + supportedFees: [10000, 3000, 500] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll/sync-swap/scroll-sync-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll/sync-swap/scroll-sync-swap-provider.ts new file mode 100644 index 0000000..2745227 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/scroll/sync-swap/scroll-sync-swap-provider.ts @@ -0,0 +1,29 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { SyncSwapAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abstract-provider'; + +export class ScrollSyncSwapProvider extends SyncSwapAbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.SCROLL; + + public readonly dexContractAddress = '0x80e38291e06339d10AAB483C65695D004dBD5C69'; + + public readonly routerHelperContract = '0x39D2E9dBD697e135E3D111F7176dBc123D6807ca'; + + public readonly vault = '0x7160570BB153Edd0Ea1775EC2b2Ac9b65F1aB61B'; + + public readonly factories = [ + '0x37BAc764494c8db4e54BDE72f6965beA9fa0AC2d', + '0xE4CF807E351b56720B17A59094179e7Ed9dD3727' + ]; + + public readonly routeTokens = [ + '0x5300000000000000000000000000000000000004', // WETH + '0x06efdbff2a14a7c8e15944d1f4a48f9f95f663a4', // USDC + '0xf55bec9cafdbe8730f096aa55dad6d22d44099df', // USDT + '0xca77eb3fefe3725dc33bccb54edefc3d9f764f97', // DAI + '0x3c1bca5a656e69edcd0d4e36bebb3fcdaca60cf1' // WBTC + ]; + + public readonly masterAddress = '0x608Cb7C3168427091F5994A45Baf12083964B4A3'; + + public readonly maxTransitTokens = 1; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/default-constants.ts new file mode 100644 index 0000000..58484d4 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/default-constants.ts @@ -0,0 +1,17 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultSyscoinRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.SYSCOIN]!.address, // WSYS + '0x2bF9b864cdc97b08B6D79ad4663e71B8aB65c45c', // USDC + '0x922D641a426DcFFaeF11680e5358F34d97d112E1' // USDT +]; + +const defaultSyscoinWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.SYSCOIN]!.address; + +export const defaultSyscoinProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultSyscoinRoutingProvidersAddresses, + wethAddress: defaultSyscoinWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/pegasys/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/pegasys/constants.ts new file mode 100644 index 0000000..f9f1cd8 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/pegasys/constants.ts @@ -0,0 +1,5 @@ +import { defaultSyscoinProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/syscoin/default-constants'; + +export const PEGASYS_CONTRACT_ADDRESS = '0x017dAd2578372CAEE5c6CddfE35eEDB3728544C4'; + +export const PEGASYS_PROVIDER_CONFIGURATION = defaultSyscoinProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/pegasys/pegasys-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/pegasys/pegasys-provider.ts new file mode 100644 index 0000000..db1571c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/pegasys/pegasys-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { PEGASYS_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/syscoin/pegasys/constants'; +import { PegasysTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/syscoin/pegasys/pegasys-trade'; + +export class PegasysProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.SYSCOIN; + + public readonly UniswapV2TradeClass = PegasysTrade; + + public readonly providerSettings = PEGASYS_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/pegasys/pegasys-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/pegasys/pegasys-trade.ts new file mode 100644 index 0000000..0285e11 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/pegasys/pegasys-trade.ts @@ -0,0 +1,20 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { PEGASYS_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/syscoin/pegasys/constants'; +import { SYS_ABI } from 'src/features/on-chain/calculation-manager/providers/dexes/syscoin/syscoin-abi'; +import { SYS_SWAP_METHOD } from 'src/features/on-chain/calculation-manager/providers/dexes/syscoin/syscoin-swap-method'; + +export class PegasysTrade extends UniswapV2AbstractTrade { + public static readonly contractAbi = SYS_ABI; + + public static readonly swapMethods = SYS_SWAP_METHOD; + + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.PEGASYS; + } + + public readonly dexContractAddress = PEGASYS_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/syscoin-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/syscoin-abi.ts new file mode 100644 index 0000000..e2e3ee3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/syscoin-abi.ts @@ -0,0 +1,138 @@ +import { AbiItem } from 'web3-utils'; + +export const SYS_ABI = [ + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'getAmountsIn', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' } + ] + }, + { + type: 'function', + stateMutability: 'view', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'getAmountsOut', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactSYSForTokens', + inputs: [ + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [], + name: 'swapExactSYSForTokensSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactTokensForSYS', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [], + name: 'swapExactTokensForSYSSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapExactTokensForTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [], + name: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + inputs: [ + { type: 'uint256', name: 'amountIn', internalType: 'uint256' }, + { type: 'uint256', name: 'amountOutMin', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'payable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapSYSForExactTokens', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapTokensForExactSYS', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'uint256', name: 'amountInMax', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + }, + { + type: 'function', + stateMutability: 'nonpayable', + outputs: [{ type: 'uint256[]', name: 'amounts', internalType: 'uint256[]' }], + name: 'swapTokensForExactTokens', + inputs: [ + { type: 'uint256', name: 'amountOut', internalType: 'uint256' }, + { type: 'uint256', name: 'amountInMax', internalType: 'uint256' }, + { type: 'address[]', name: 'path', internalType: 'address[]' }, + { type: 'address', name: 'to', internalType: 'address' }, + { type: 'uint256', name: 'deadline', internalType: 'uint256' } + ] + } +] as AbiItem[]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/syscoin-swap-method.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/syscoin-swap-method.ts new file mode 100644 index 0000000..820db22 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/syscoin/syscoin-swap-method.ts @@ -0,0 +1,45 @@ +import { + ExactInputOutputSwapMethodsList, + RegularSwapMethodsList, + SupportingFeeSwapMethodsList, + SwapMethodsList +} from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/SWAP_METHOD'; + +export const SYS_EXACT_INPUT_REGULAR_SWAP_METHOD: RegularSwapMethodsList = { + TOKENS_TO_TOKENS: 'swapExactTokensForTokens', + ETH_TO_TOKENS: 'swapExactSYSForTokens', + TOKENS_TO_ETH: 'swapExactTokensForSYS' +}; + +export const SYS_EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD: SupportingFeeSwapMethodsList = { + TOKENS_TO_TOKENS_SUPPORTING_FEE: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + ETH_TO_TOKENS_SUPPORTING_FEE: 'swapExactSYSForTokensSupportingFeeOnTransferTokens', + TOKENS_TO_ETH_SUPPORTING_FEE: 'swapExactTokensForSYSSupportingFeeOnTransferTokens' +}; + +export const SYS_EXACT_INPUT_SWAP_METHOD: SwapMethodsList = { + ...SYS_EXACT_INPUT_REGULAR_SWAP_METHOD, + ...SYS_EXACT_INPUT_SUPPORTING_FEE_SWAP_METHOD +}; + +export const SYS_EXACT_OUTPUT_REGULAR_SWAP_METHOD: RegularSwapMethodsList = { + TOKENS_TO_TOKENS: 'swapTokensForExactTokens', + ETH_TO_TOKENS: 'swapSYSForExactTokens', + TOKENS_TO_ETH: 'swapTokensForExactSYS' +}; + +export const SYS_EXACT_OUTPUT_SUPPORTING_FEE_SWAP_METHOD: SupportingFeeSwapMethodsList = { + TOKENS_TO_TOKENS_SUPPORTING_FEE: 'swapTokensForExactTokensSupportingFeeOnTransferTokens', + ETH_TO_TOKENS_SUPPORTING_FEE: 'swapSYSForExactTokensSupportingFeeOnTransferTokens', + TOKENS_TO_ETH_SUPPORTING_FEE: 'swapTokensForExactSYSSupportingFeeOnTransferTokens' +}; + +export const SYS_EXACT_OUTPUT_SWAP_METHOD: SwapMethodsList = { + ...SYS_EXACT_OUTPUT_REGULAR_SWAP_METHOD, + ...SYS_EXACT_OUTPUT_SUPPORTING_FEE_SWAP_METHOD +}; + +export const SYS_SWAP_METHOD: ExactInputOutputSwapMethodsList = { + input: SYS_EXACT_INPUT_SWAP_METHOD, + output: SYS_EXACT_OUTPUT_SWAP_METHOD +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/taiko/izumi-taiko/izumi-taiko-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/taiko/izumi-taiko/izumi-taiko-provider.ts new file mode 100644 index 0000000..c66298c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/taiko/izumi-taiko/izumi-taiko-provider.ts @@ -0,0 +1,21 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiTaikoProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.TAIKO; + + protected readonly dexAddress = '0x04830cfCED9772b8ACbAF76Cfc7A630Ad82c9148'; + + protected readonly config = { + maxTransitTokens: 1, + quoterAddress: '0x14323AfbC2b82fE58F0D9c203830EE969B4d1bE2', + liquidityManagerAddress: '0x33531bDBFE34fa6Fd5963D0423f7699775AacaaF', + routingTokenAddresses: [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.TAIKO]!.address, // WETH + '0x07d83526730c7438048D55A4fc0b850e2aaB6f0b' // USDC + ], + multicallAddress: '0x7a524c7e82874226F0b51aade60A1BE4D430Cf0F', + supportedFees: [10000, 3000, 500] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/ape-swap/ape-swap-telos-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/ape-swap/ape-swap-telos-provider.ts new file mode 100644 index 0000000..e232b99 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/ape-swap/ape-swap-telos-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { ApeSwapTelosTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/ape-swap/ape-swap-telos-trade'; +import { APE_SWAP_TELOS_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/ape-swap/constants'; + +export class ApeSwapTelosProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.TELOS; + + public readonly UniswapV2TradeClass = ApeSwapTelosTrade; + + public readonly providerSettings = APE_SWAP_TELOS_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/ape-swap/ape-swap-telos-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/ape-swap/ape-swap-telos-trade.ts new file mode 100644 index 0000000..d69641f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/ape-swap/ape-swap-telos-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { APE_SWAP_TELOS_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/ape-swap/constants'; + +export class ApeSwapTelosTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.APE_SWAP; + } + + public readonly dexContractAddress = APE_SWAP_TELOS_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/ape-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/ape-swap/constants.ts new file mode 100644 index 0000000..1c5305d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/ape-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultTelosProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/default-constants'; + +export const APE_SWAP_TELOS_CONTRACT_ADDRESS = '0xb9667cf9a495a123b0c43b924f6c2244f42817be'; + +export const APE_SWAP_TELOS_PROVIDER_CONFIGURATION = defaultTelosProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/default-constants.ts new file mode 100644 index 0000000..6edfde0 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/default-constants.ts @@ -0,0 +1,19 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultTelosRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.TELOS]!.address, // WTLOS + '0xeFAeeE334F0Fd1712f9a8cc375f427D9Cdd40d73', // USDT + '0x818ec0A7Fe18Ff94269904fCED6AE3DaE6d6dC0b', // USDC + '0xf390830DF829cf22c53c8840554B98eafC5dCBc2', // BTC + '0xfA9343C3897324496A05fC75abeD6bAC29f8A40f' // ETH +]; + +const defaultTelosWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.TELOS]!.address; + +export const defaultTelosProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 1, + routingProvidersAddresses: defaultTelosRoutingProvidersAddresses, + wethAddress: defaultTelosWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/omnidex/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/omnidex/constants.ts new file mode 100644 index 0000000..b0706b6 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/omnidex/constants.ts @@ -0,0 +1,5 @@ +import { defaultTelosProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/default-constants'; + +export const OMNIDEX_CONTRACT_ADDRESS = '0xF9678db1CE83f6f51E5df348E2Cc842Ca51EfEc1'; + +export const OMNIDEX_PROVIDER_CONFIGURATION = defaultTelosProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/omnidex/omnidex-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/omnidex/omnidex-provider.ts new file mode 100644 index 0000000..3980271 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/omnidex/omnidex-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { OMNIDEX_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/omnidex/constants'; +import { OmnidexTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/omnidex/omnidex-trade'; + +export class OmnidexProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.TELOS; + + public readonly UniswapV2TradeClass = OmnidexTrade; + + public readonly providerSettings = OMNIDEX_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/omnidex/omnidex-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/omnidex/omnidex-trade.ts new file mode 100644 index 0000000..b2527ad --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/omnidex/omnidex-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { OMNIDEX_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/omnidex/constants'; + +export class OmnidexTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.OMNIDEX; + } + + public readonly dexContractAddress = OMNIDEX_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/sushi-swap-telos/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/sushi-swap-telos/constants.ts new file mode 100644 index 0000000..45f6f24 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/sushi-swap-telos/constants.ts @@ -0,0 +1,5 @@ +import { defaultTelosProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/default-constants'; + +export const SUSHI_SWAP_TELOS_CONTRACT_ADDRESS = '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'; + +export const SUSHI_SWAP_TELOS_PROVIDER_CONFIGURATION = defaultTelosProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/sushi-swap-telos/sushi-swap-telos-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/sushi-swap-telos/sushi-swap-telos-provider.ts new file mode 100644 index 0000000..a6cf1e7 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/sushi-swap-telos/sushi-swap-telos-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SUSHI_SWAP_TELOS_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/sushi-swap-telos/constants'; +import { SushiSwapTelosTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/sushi-swap-telos/sushi-swap-telos-trade'; + +export class SushiSwapTelosProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.TELOS; + + public readonly UniswapV2TradeClass = SushiSwapTelosTrade; + + public readonly providerSettings = SUSHI_SWAP_TELOS_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/sushi-swap-telos/sushi-swap-telos-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/sushi-swap-telos/sushi-swap-telos-trade.ts new file mode 100644 index 0000000..b8f208b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/sushi-swap-telos/sushi-swap-telos-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { SUSHI_SWAP_TELOS_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/sushi-swap-telos/constants'; + +export class SushiSwapTelosTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SUSHI_SWAP; + } + + public readonly dexContractAddress = SUSHI_SWAP_TELOS_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/zappy/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/zappy/constants.ts new file mode 100644 index 0000000..1f3c597 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/zappy/constants.ts @@ -0,0 +1,5 @@ +import { defaultTelosProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/default-constants'; + +export const ZAPPY_CONTRACT_ADDRESS = '0xb9239af0697c8efb42cba3568424b06753c6da71'; + +export const ZAPPY_PROVIDER_CONFIGURATION = defaultTelosProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/zappy/trisolaris-aurora-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/zappy/trisolaris-aurora-provider.ts new file mode 100644 index 0000000..5fd6212 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/zappy/trisolaris-aurora-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { ZAPPY_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/zappy/constants'; +import { ZappyTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/zappy/trisolaris-aurora-trade'; + +export class ZappyProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.TELOS; + + public readonly UniswapV2TradeClass = ZappyTrade; + + public readonly providerSettings = ZAPPY_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/zappy/trisolaris-aurora-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/zappy/trisolaris-aurora-trade.ts new file mode 100644 index 0000000..55e1ed9 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/telos/zappy/trisolaris-aurora-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { ZAPPY_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/telos/zappy/constants'; + +export class ZappyTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ZAPPY; + } + + public readonly dexContractAddress = ZAPPY_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/tron/bridgers/bridgers-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/tron/bridgers/bridgers-provider.ts new file mode 100644 index 0000000..5c37c9b --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/tron/bridgers/bridgers-provider.ts @@ -0,0 +1,105 @@ +import BigNumber from 'bignumber.js'; +import { BridgersPairIsUnavailableError, MaxAmountError, MinAmountError } from 'src/common/errors'; +import { PriceToken, PriceTokenAmount, TokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { combineOptions } from 'src/common/utils/options'; +import { TronBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { bridgersNativeAddress } from 'src/features/common/providers/bridgers/constants/bridgers-native-address'; +import { toBridgersBlockchain } from 'src/features/common/providers/bridgers/constants/to-bridgers-blockchain'; +import { + BridgersQuoteRequest, + BridgersQuoteResponse +} from 'src/features/common/providers/bridgers/models/bridgers-quote-api'; +import { createTokenNativeAddressProxy } from 'src/features/common/utils/token-native-address-proxy'; +import { + OnChainCalculationOptions, + RequiredOnChainCalculationOptions +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { OnChainPlatformFee } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { OnChainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/on-chain-provider'; +import { tronProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/tron-on-chain-provider/constants/tron-provider-default-options'; +import { TronOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/tron-on-chain-provider/tron-on-chain-provider'; +import { BridgersTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/tron/bridgers/bridgers-trade'; + +export class BridgersProvider extends TronOnChainProvider { + private readonly defaultOptions: RequiredOnChainCalculationOptions = tronProviderDefaultOptions; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.BRIDGERS; + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options?: OnChainCalculationOptions + ): Promise { + const fullOptions = combineOptions(options, this.defaultOptions); + + const fromTokenAddress = createTokenNativeAddressProxy( + from, + bridgersNativeAddress, + false + ).address; + const toTokenAddress = createTokenNativeAddressProxy( + toToken, + bridgersNativeAddress, + false + ).address; + const quoteRequest: BridgersQuoteRequest = { + fromTokenAddress, + toTokenAddress, + fromTokenAmount: from.stringWeiAmount, + fromTokenChain: toBridgersBlockchain[from.blockchain], + toTokenChain: toBridgersBlockchain[toToken.blockchain] + }; + const quoteResponse = await this.httpClient.post( + 'https://sswap.swft.pro/api/sswap/quote', + quoteRequest + ); + const transactionData = quoteResponse.data?.txData; + if (quoteResponse.resCode !== 100 || !transactionData) { + throw OnChainProvider.parseError(new BridgersPairIsUnavailableError()); + } + + if (from.tokenAmount.lt(transactionData.depositMin)) { + throw new MinAmountError(new BigNumber(transactionData.depositMin), from.symbol); + } + if (from.tokenAmount.gt(transactionData.depositMax)) { + throw new MaxAmountError(new BigNumber(transactionData.depositMax), from.symbol); + } + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: new BigNumber(transactionData.toTokenAmount) + }); + + const cryptoFeeToken = new TokenAmount({ + ...nativeTokensList[from.blockchain], + tokenAmount: new BigNumber(transactionData.chainFee) + }); + const platformFeePercent = transactionData.fee * 100; + const platformFee: OnChainPlatformFee = { + percent: platformFeePercent, + token: await PriceTokenAmount.createToken({ + ...from, + tokenAmount: from.tokenAmount.multipliedBy(platformFeePercent / 100) + }) + }; + + return new BridgersTrade( + { + from, + to, + slippageTolerance: fullOptions.slippageTolerance, + contractAddress: transactionData.contractAddress, + cryptoFeeToken, + platformFee + }, + fullOptions.providerAddress + ); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/tron/bridgers/bridgers-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/tron/bridgers/bridgers-trade.ts new file mode 100644 index 0000000..c6bde74 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/tron/bridgers/bridgers-trade.ts @@ -0,0 +1,162 @@ +import { SwapRequestError } from 'src/common/errors'; +import { PriceTokenAmount, Token, TokenAmount } from 'src/common/tokens'; +import { TronBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { TronTransactionConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/models/tron-transaction-config'; +import { TronWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/tron-web3-pure/tron-web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; +import { bridgersNativeAddress } from 'src/features/common/providers/bridgers/constants/bridgers-native-address'; +import { toBridgersBlockchain } from 'src/features/common/providers/bridgers/constants/to-bridgers-blockchain'; +import { + BridgersSwapRequest, + BridgersSwapResponse +} from 'src/features/common/providers/bridgers/models/bridgers-swap-api'; +import { createTokenNativeAddressProxy } from 'src/features/common/utils/token-native-address-proxy'; +import { TronBridgersTransactionData } from 'src/features/cross-chain/calculation-manager/providers/bridgers-provider/tron-bridgers-trade/models/tron-bridgers-transaction-data'; +import { FeeInfo } from 'src/features/cross-chain/calculation-manager/providers/common/models/fee-info'; +import { OnChainPlatformFee } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-proxy-fee-info'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { TronOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/tron-on-chain-trade/tron-on-chain-trade'; + +export class BridgersTrade extends TronOnChainTrade { + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly path: ReadonlyArray = []; + + public readonly slippageTolerance: number; + + private readonly contractAddress: string; + + public readonly cryptoFeeToken: TokenAmount; + + public readonly platformFee: OnChainPlatformFee; + + public get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.BRIDGERS; + } + + protected get spenderAddress(): string { + return this.contractAddress; + } + + public get toTokenAmountMin(): PriceTokenAmount { + return this.to; + } + + public readonly feeInfo: FeeInfo; + + constructor( + tradeStruct: { + from: PriceTokenAmount; + to: PriceTokenAmount; + slippageTolerance: number; + contractAddress: string; + cryptoFeeToken: TokenAmount; + platformFee: OnChainPlatformFee; + }, + providerAddress: string + ) { + super(providerAddress); + + this.from = tradeStruct.from; + this.to = tradeStruct.to; + this.slippageTolerance = tradeStruct.slippageTolerance; + this.contractAddress = tradeStruct.contractAddress; + this.cryptoFeeToken = tradeStruct.cryptoFeeToken; + this.platformFee = tradeStruct.platformFee; + this.feeInfo = { + rubicProxy: { + platformFee: { + percent: tradeStruct.platformFee.percent, + token: tradeStruct.platformFee.token + } + } + }; + } + + public async swap(options: SwapTransactionOptions = {}): Promise { + await this.checkWalletState(options?.testMode); + await this.checkAllowanceAndApprove(options); + + try { + const transactionData = await this.getTransactionData(options); + + return await this.web3Private.triggerContract( + this.contractAddress, + transactionData.functionName, + transactionData.parameter, + { ...transactionData.options, onTransactionHash: options.onConfirm } + ); + } catch (err) { + if ([400, 500, 503].includes(err.code)) { + throw new SwapRequestError(); + } + throw err; + } + } + + public async encode(options: EncodeTransactionOptions): Promise { + try { + const transactionData = await this.getTransactionData(options); + const encodedData = TronWeb3Pure.encodeMethodSignature( + transactionData.functionName, + transactionData.parameter + ); + + return { + to: this.contractAddress, + data: encodedData, + callValue: transactionData.options.callValue, + feeLimit: options.feeLimit || transactionData.options.feeLimit + }; + } catch (err) { + if ([400, 500, 503].includes(err.code)) { + throw new SwapRequestError(); + } + throw err; + } + } + + private async getTransactionData(options: { + fromAddress?: string; + receiverAddress?: string; + }): Promise { + const fromTokenAddress = createTokenNativeAddressProxy( + this.from, + bridgersNativeAddress + ).address; + const toTokenAddress = createTokenNativeAddressProxy( + this.to, + bridgersNativeAddress + ).address; + + const fromAddress = options.fromAddress || this.walletAddress; + const toAddress = options.receiverAddress || fromAddress; + + const amountOutMin = this.toTokenAmountMin.stringWeiAmount; + + const swapRequest: BridgersSwapRequest = { + fromTokenAddress, + toTokenAddress, + fromAddress, + toAddress, + fromTokenChain: toBridgersBlockchain[this.from.blockchain], + toTokenChain: toBridgersBlockchain[this.to.blockchain], + fromTokenAmount: this.from.stringWeiAmount, + amountOutMin, + equipmentNo: fromAddress.slice(0, 32), + sourceFlag: 'rubic_widget' + }; + + const swapData = await Injector.httpClient.post< + BridgersSwapResponse + >('https://sswap.swft.pro/api/sswap/swap', swapRequest); + return swapData.data.txData; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/astro-swap/astro-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/astro-swap/astro-swap-provider.ts new file mode 100644 index 0000000..7ad0668 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/astro-swap/astro-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { AstroSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/velas/astro-swap/astro-swap-trade'; +import { ASTRO_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/velas/astro-swap/constants'; + +export class AstroSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.VELAS; + + public readonly UniswapV2TradeClass = AstroSwapTrade; + + public readonly providerSettings = ASTRO_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/astro-swap/astro-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/astro-swap/astro-swap-trade.ts new file mode 100644 index 0000000..be97619 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/astro-swap/astro-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { ASTRO_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/velas/astro-swap/constants'; + +export class AstroSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.ASTRO_SWAP; + } + + public readonly dexContractAddress = ASTRO_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/astro-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/astro-swap/constants.ts new file mode 100644 index 0000000..f110279 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/astro-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultVelasProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/velas/default-constants'; + +export const ASTRO_SWAP_CONTRACT_ADDRESS = '0x3328cd3a9A295cd00fBb1d71Bf097e002B4614ad'; + +export const ASTRO_SWAP_PROVIDER_CONFIGURATION = defaultVelasProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/default-constants.ts new file mode 100644 index 0000000..5d280da --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/default-constants.ts @@ -0,0 +1,19 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultVelasRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.VELAS]!.address, // WVLX + '0x40c8002c2887ade2297ad48d9dc101de08bd104c', // WAG + '0xdf44aed1684b9cfd0fbe07c43a3bbcd20cde0145', // USDV (USD Velero Stablecoin) + '0x3611fbfb06ffbcef9afb210f6ace86742e6c14a4', // ADA (Wrapped Cardano) + '0x72eb7ca07399ec402c5b7aa6a65752b6a1dc0c27' // ASTRO (AstroSwap) +]; + +const defaultVelasWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.VELAS]!.address; + +export const defaultVelasProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 2, + routingProvidersAddresses: defaultVelasRoutingProvidersAddresses, + wethAddress: defaultVelasWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/wagyu-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/wagyu-swap/constants.ts new file mode 100644 index 0000000..aa65ae8 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/wagyu-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultVelasProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/velas/default-constants'; + +export const WAGYU_SWAP_CONTRACT_ADDRESS = '0x3D1c58B6d4501E34DF37Cf0f664A58059a188F00'; + +export const WAGYU_SWAP_PROVIDER_CONFIGURATION = defaultVelasProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/wagyu-swap/wagyu-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/wagyu-swap/wagyu-swap-provider.ts new file mode 100644 index 0000000..372cf8d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/wagyu-swap/wagyu-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { WAGYU_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/velas/wagyu-swap/constants'; +import { WagyuSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/velas/wagyu-swap/wagyu-swap-trade'; + +export class WagyuSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.VELAS; + + public readonly UniswapV2TradeClass = WagyuSwapTrade; + + public readonly providerSettings = WAGYU_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/wagyu-swap/wagyu-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/wagyu-swap/wagyu-swap-trade.ts new file mode 100644 index 0000000..81beca5 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/velas/wagyu-swap/wagyu-swap-trade.ts @@ -0,0 +1,14 @@ +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { WAGYU_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/velas/wagyu-swap/constants'; + +export class WagyuSwapTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.WAGYU_SWAP; + } + + public readonly dexContractAddress = WAGYU_SWAP_CONTRACT_ADDRESS; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/xlayer/izumi-xlayer/izumi-xlayer-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/xlayer/izumi-xlayer/izumi-xlayer-provider.ts new file mode 100644 index 0000000..8dabde3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/xlayer/izumi-xlayer/izumi-xlayer-provider.ts @@ -0,0 +1,22 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiXlayerProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.XLAYER; + + protected readonly dexAddress = '0xd7de110Bd452AAB96608ac3750c3730A17993DE0'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0x93C22Fbeff4448F2fb6e432579b0638838Ff9581', + liquidityManagerAddress: '0xF42C48f971bDaA130573039B6c940212EeAb8496', + routingTokenAddresses: [ + '0xe538905cf8410324e03a5a23c1c177a474d59b2b', // WOKB + wrappedNativeTokensList[BLOCKCHAIN_NAME.XLAYER]!.address, // WBNB + '0x1e4a5963abfd975d8c9021ce480b42188849d41d' // USDT + ], + multicallAddress: '0x14323AfbC2b82fE58F0D9c203830EE969B4d1bE2', + supportedFees: [10000, 3000, 500] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/default-constants.ts new file mode 100644 index 0000000..d5ce65d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/default-constants.ts @@ -0,0 +1,22 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultZetachainRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.ZETACHAIN]!.address, // WZETA + '0x05BA149A7bd6dC1F937fA9046A9e05C05f3b18b0', // USDC.BSC + '0x0cbe0dF132a6c6B4a2974Fa1b7Fb953CF0Cc798a', // USDC.ETH + '0x7c8dDa80bbBE1254a7aACf3219EBe1481c6E01d7', // USDT.ETH + '0x91d4F0D54090Df2D81e834c3c8CE71C6c865e79F', // USDT.BSC + '0x48f80608B672DC30DC7e3dbBd0343c5F02C738Eb', // BNB.BSC + '0xd97B1de3619ed2c6BEb3860147E30cA8A7dC9891', // ETH.ETH + '0x13A0c5930C028511Dc02665E7285134B6d11A5f4' // BTC.BTC +]; + +const defaultZetachainWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.ZETACHAIN]!.address; + +export const defaultZetachainProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 1, + routingProvidersAddresses: defaultZetachainRoutingProvidersAddresses, + wethAddress: defaultZetachainWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/eddy-finance/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/eddy-finance/constants.ts new file mode 100644 index 0000000..bcfa280 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/eddy-finance/constants.ts @@ -0,0 +1,113 @@ +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; +import { defaultZetachainProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/zetachain/default-constants'; +import { AbiItem } from 'web3-utils'; + +export const EDDY_FINANCE_PROVIDER_CONFIGURATION: UniswapV2ProviderConfiguration = { + ...defaultZetachainProviderConfiguration, + maxTransitTokens: 1 +}; + +export const EDDY_FINANCE_SWAP_CONTRACT_ADDRESS = '0xde3167958ad6251e8d6ff1791648b322fc6b51bd'; + +export const EDDY_FINANCE_SWAP_CONTRACT_ABI = [ + { + inputs: [], + name: 'WZETA', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + // Zeta → any + { + inputs: [ + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + } + ], + name: 'swapEddyExactETHForTokens', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'payable', + type: 'function' + }, + // Any → Zeta + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + } + ], + name: 'swapEddyExactTokensForEth', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + // Any → Any + { + inputs: [ + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'amountOutMin', + type: 'uint256' + }, + { + internalType: 'address[]', + name: 'path', + type: 'address[]' + } + ], + name: 'swapEddyTokensForTokens', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; + +export const EDDY_FINANCE_CALCULATE_CONTRACT_ADDRESS = '0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe'; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/eddy-finance/eddy-finance-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/eddy-finance/eddy-finance-provider.ts new file mode 100644 index 0000000..36b969f --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/eddy-finance/eddy-finance-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { EDDY_FINANCE_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/zetachain/eddy-finance/constants'; +import { EddyFinanceTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/zetachain/eddy-finance/eddy-finance-trade'; + +export class EddyFinanceProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ZETACHAIN; + + public readonly UniswapV2TradeClass = EddyFinanceTrade; + + public readonly providerSettings = EDDY_FINANCE_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/eddy-finance/eddy-finance-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/eddy-finance/eddy-finance-trade.ts new file mode 100644 index 0000000..c2f0565 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/eddy-finance/eddy-finance-trade.ts @@ -0,0 +1,161 @@ +import { NotWhitelistedProviderError, RubicSdkError } from 'src/common/errors'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { checkUnsupportedReceiverAddress } from 'src/features/common/utils/check-unsupported-receiver-address'; +import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address'; +import { GetContractParamsOptions } from 'src/features/cross-chain/calculation-manager/providers/common/models/get-contract-params-options'; +import { ProxyCrossChainEvmTrade } from 'src/features/cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types'; +import { EDDY_SWAP_METHOD } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/EDDY_SWAP_METHOD'; +import { ExactInputOutputSwapMethodsList } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/constants/SWAP_METHOD'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { + EDDY_FINANCE_CALCULATE_CONTRACT_ADDRESS, + EDDY_FINANCE_SWAP_CONTRACT_ABI, + EDDY_FINANCE_SWAP_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/zetachain/eddy-finance/constants'; + +export class EddyFinanceTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.EDDY_FINANCE; + } + + public readonly dexContractAddress = EDDY_FINANCE_CALCULATE_CONTRACT_ADDRESS; + + public static readonly swapMethods: ExactInputOutputSwapMethodsList = EDDY_SWAP_METHOD; + + protected get spenderAddress(): string { + return this.useProxy + ? rubicProxyContractAddress[this.from.blockchain].gateway + : EDDY_FINANCE_SWAP_CONTRACT_ADDRESS; + } + + protected async getTransactionConfigAndAmount( + options: EncodeTransactionOptions + ): Promise { + await this.checkFromAddress(options.fromAddress, true); + checkUnsupportedReceiverAddress( + options?.receiverAddress, + options?.fromAddress || this.walletAddress + ); + + if (options.supportFee === undefined) { + const needApprove = await this.needApprove(options.fromAddress); + if (needApprove) { + throw new RubicSdkError( + 'To use `encode` function, token must be approved for wallet' + ); + } + + try { + await this.checkBalance(); + } catch (_err) { + throw new RubicSdkError( + 'To use `encode` function, wallet must have enough balance or you must provider `supportFee` parameter in options.' + ); + } + } + + const methodName = await this.getMethodName(options, options.fromAddress, false); + const gasParams = this.getGasParams(options); + + const config = EvmWeb3Pure.encodeMethodCall( + EDDY_FINANCE_SWAP_CONTRACT_ADDRESS, + EDDY_FINANCE_SWAP_CONTRACT_ABI, + methodName, + this.getCallParameters(), + this.nativeValueToSend, + gasParams + ); + + return { tx: config, toAmount: this.to.stringWeiAmount }; + } + + protected getCallParameters(): unknown[] { + const { amountIn } = this.getAmountInAndAmountOut(); + // EddyFinance 13.05.2024:: + // amountOut is handled at our contracts for now so you can pass it as 0(Will be changed in future) + const amountOut = '0'; + const amountParameters = this.from.isNative ? [amountOut] : [amountIn, amountOut]; + + return [...amountParameters, this.wrappedPath.map(t => t.address)]; + } + + protected getSwapParametersByMethod( + method: string + ): Parameters['executeContractMethod']> { + const value = this.nativeValueToSend; + + return [ + EDDY_FINANCE_SWAP_CONTRACT_ADDRESS, + EDDY_FINANCE_SWAP_CONTRACT_ABI, + method, + this.getCallParameters(), + { value } + ]; + } + + protected async getSwapData(options: GetContractParamsOptions): Promise { + const directTransactionConfig = await this.encodeDirect({ + ...options, + fromAddress: rubicProxyContractAddress[this.from.blockchain].router, + supportFee: false, + receiverAddress: + options.receiverAddress || rubicProxyContractAddress[this.from.blockchain].router + }); + + const availableDexs = ( + await ProxyCrossChainEvmTrade.getWhitelistedDexes(this.from.blockchain) + ).map(address => address.toLowerCase()); + + const routerAddress = directTransactionConfig.to; + const method = directTransactionConfig.data.slice(0, 10); + + if (!availableDexs.includes(routerAddress.toLowerCase())) { + throw new NotWhitelistedProviderError(routerAddress, undefined, 'dex'); + } + await ProxyCrossChainEvmTrade.checkDexWhiteList( + this.from.blockchain, + routerAddress, + method + ); + + return [ + [ + routerAddress, + routerAddress, + this.from.address, + this.to.address, + this.from.stringWeiAmount, + directTransactionConfig.data, + true + ] + ]; + } + + protected getAmountInAndAmountOut(): { amountIn: string; amountOut: string } { + let amountIn = this.fromWithoutFee.stringWeiAmount; + let amountOut = this.toTokenAmountMin.stringWeiAmount; + + if ( + this.from.blockchain === BLOCKCHAIN_NAME.ZETACHAIN && + this.type === ON_CHAIN_TRADE_TYPE.EDDY_FINANCE && + !this.from.isNative + ) { + amountIn = this.fromWithoutFee.weiAmount.minus(1).toFixed(); + } + + if (this.exact === 'output') { + amountIn = this.fromWithoutFee.weiAmountPlusSlippage(this.slippageTolerance).toFixed(0); + amountOut = this.to.stringWeiAmount; + } + + return { amountIn, amountOut }; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/izumi-zetachain/izumi-zetachain-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/izumi-zetachain/izumi-zetachain-provider.ts new file mode 100644 index 0000000..ec1cfc3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/izumi-zetachain/izumi-zetachain-provider.ts @@ -0,0 +1,27 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiZetachainProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ZETACHAIN; + + protected readonly dexAddress = '0x34bc1b87f60e0a30c0e24FD7Abada70436c71406'; + + protected readonly config = { + maxTransitTokens: 1, + quoterAddress: '0x04830cfCED9772b8ACbAF76Cfc7A630Ad82c9148', + liquidityManagerAddress: '0x2db0AFD0045F3518c77eC6591a542e326Befd3D7', + routingTokenAddresses: [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.ZETACHAIN]!.address, // WZETA + '0x05BA149A7bd6dC1F937fA9046A9e05C05f3b18b0', // USDC.BSC + '0x0cbe0dF132a6c6B4a2974Fa1b7Fb953CF0Cc798a', // USDC.ETH + '0x7c8dDa80bbBE1254a7aACf3219EBe1481c6E01d7', // USDT.ETH + '0x91d4F0D54090Df2D81e834c3c8CE71C6c865e79F', // USDT.BSC + '0x48f80608B672DC30DC7e3dbBd0343c5F02C738Eb', // BNB.BSC + '0xd97B1de3619ed2c6BEb3860147E30cA8A7dC9891', // ETH.ETH + '0x13A0c5930C028511Dc02665E7285134B6d11A5f4' // BTC.BTC + ], + multicallAddress: '0x7a524c7e82874226F0b51aade60A1BE4D430Cf0F', + supportedFees: [500, 3000, 10000] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/sushi-swap-zetachain/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/sushi-swap-zetachain/constants.ts new file mode 100644 index 0000000..ff356eb --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/sushi-swap-zetachain/constants.ts @@ -0,0 +1,5 @@ +import { defaultZetachainProviderConfiguration } from '../default-constants'; + +export const SUSHI_SWAP_ZETACHAIN_CONTRACT_ADDRESS = '0x1f2FCf1d036b375b384012e61D3AA33F8C256bbE'; + +export const SUSHI_SWAP_ZETACHAIN_PROVIDER_CONFIGURATION = defaultZetachainProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/sushi-swap-zetachain/sushi-swap-zetachain-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/sushi-swap-zetachain/sushi-swap-zetachain-provider.ts new file mode 100644 index 0000000..2df23cc --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/sushi-swap-zetachain/sushi-swap-zetachain-provider.ts @@ -0,0 +1,13 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { UniswapV2AbstractProvider } from '../../common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { SUSHI_SWAP_ZETACHAIN_PROVIDER_CONFIGURATION } from './constants'; +import { SushiSwapZetachainTrade } from './sushi-swap-zetachain-trade'; + +export class SushiSwapZetachainProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ZETACHAIN; + + public readonly UniswapV2TradeClass = SushiSwapZetachainTrade; + + public readonly providerSettings = SUSHI_SWAP_ZETACHAIN_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/sushi-swap-zetachain/sushi-swap-zetachain-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/sushi-swap-zetachain/sushi-swap-zetachain-trade.ts new file mode 100644 index 0000000..21e6b5d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zetachain/sushi-swap-zetachain/sushi-swap-zetachain-trade.ts @@ -0,0 +1,26 @@ +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options'; +import { checkUnsupportedReceiverAddress } from 'src/features/common/utils/check-unsupported-receiver-address'; + +import { ON_CHAIN_TRADE_TYPE, OnChainTradeType } from '../../../common/models/on-chain-trade-type'; +import { UniswapV2AbstractTrade } from '../../common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { SUSHI_SWAP_ZETACHAIN_CONTRACT_ADDRESS } from './constants'; + +export class SushiSwapZetachainTrade extends UniswapV2AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.SUSHI_SWAP; + } + + public readonly dexContractAddress = SUSHI_SWAP_ZETACHAIN_CONTRACT_ADDRESS; + + public async encode(options: EncodeTransactionOptions): Promise { + await checkUnsupportedReceiverAddress(options.receiverAddress, this.walletAddress); + await this.checkFromAddress(options.fromAddress, true); + await this.checkReceiverAddress(options.receiverAddress); + + if (this.useProxy) { + return this.encodeProxy(options); + } + return this.encodeDirect(options); + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zkfair/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zkfair/default-constants.ts new file mode 100644 index 0000000..71237ff --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zkfair/default-constants.ts @@ -0,0 +1,20 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultZkFairRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.ZK_FAIR]!.address, // WUSDC + '0x1cD3E2A23C45A690a18Ed93FD1412543f464158F', // ZKF + '0x4b21b980d0Dc7D3C0C6175b0A412694F3A1c7c6b', // ETH + '0x3f97bf3Cd76B5cA9D4A4E9cD8a73C24E32d6C193', // USDT + '0x825b4244684d5A07fCeF8124D9B21FD868b39654', // HPX + '0x450C29E6E799efECc6811954F47756af602D7930' // FAIR +]; + +const defaultZkFairWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.ZK_FAIR]!.address; + +export const defaultZkFairProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 1, + routingProvidersAddresses: defaultZkFairRoutingProvidersAddresses, + wethAddress: defaultZkFairWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zkfair/izumi-zkfair/izumi-zkfair-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zkfair/izumi-zkfair/izumi-zkfair-provider.ts new file mode 100644 index 0000000..025fe60 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zkfair/izumi-zkfair/izumi-zkfair-provider.ts @@ -0,0 +1,22 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiZkfairProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ZK_FAIR; + + protected readonly dexAddress = '0x02F55D53DcE23B4AA962CC68b0f685f26143Bdb2'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0x3EF68D3f7664b2805D4E88381b64868a56f88bC4', + liquidityManagerAddress: '0x110dE362cc436D7f54210f96b8C7652C2617887D', + routingTokenAddresses: [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.ZK_FAIR]!.address, // WRAP + '0x1cD3E2A23C45A690a18Ed93FD1412543f464158F', // ZKF + '0x450C29E6E799efECc6811954F47756af602D7930' // FAIR + ], + multicallAddress: '0x7a524c7e82874226F0b51aade60A1BE4D430Cf0F', + supportedFees: [10000, 3000, 500] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zklink/izumi-zklink/izumi-zklink-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zklink/izumi-zklink/izumi-zklink-provider.ts new file mode 100644 index 0000000..dc6dbfa --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zklink/izumi-zklink/izumi-zklink-provider.ts @@ -0,0 +1,22 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiZkLinkProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ZK_LINK; + + protected readonly dexAddress = '0x377EC7c9ae5a0787F384668788a1654249059dD6'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0x7dEe7de9814ed6C1e20B3E4E2fA9b1B96E15FDe1', + liquidityManagerAddress: '0x936c9A1B8f88BFDbd5066ad08e5d773BC82EB15F', + routingTokenAddresses: [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.ZK_LINK]!.address, // WRAP + '0x1a1A3b2ff016332e866787B311fcB63928464509', // USDC + '0x2F8A25ac62179B31D62D7F80884AE57464699059' // USDT + ], + multicallAddress: 'fd5f0acaaa666f3d816fe3dd54a96dacd6e7bb16', + supportedFees: [10000, 3000, 500] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/default-constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/default-constants.ts new file mode 100644 index 0000000..01696e3 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/default-constants.ts @@ -0,0 +1,17 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2ProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/uniswap-v2-provider-configuration'; + +const defaultZkSyncRoutingProvidersAddresses = [ + wrappedNativeTokensList[BLOCKCHAIN_NAME.ZK_SYNC]!.address, // WETH + '0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4', // USDC + '0x0e97c7a0f8b2c9885c8ac9fc6136e829cbc21d42' // MUTE +]; + +const defaultZkSyncWethAddress = wrappedNativeTokensList[BLOCKCHAIN_NAME.ZK_SYNC]!.address; + +export const defaultZkSyncProviderConfiguration: UniswapV2ProviderConfiguration = { + maxTransitTokens: 1, + routingProvidersAddresses: defaultZkSyncRoutingProvidersAddresses, + wethAddress: defaultZkSyncWethAddress +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/izumi-zksync/izumi-zksync-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/izumi-zksync/izumi-zksync-provider.ts new file mode 100644 index 0000000..ea5239d --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/izumi-zksync/izumi-zksync-provider.ts @@ -0,0 +1,23 @@ +import { wrappedNativeTokensList } from 'src/common/tokens/constants/wrapped-native-tokens'; +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { IzumiProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/izumi-abstract/izumi-provider'; + +export class IzumiZksyncProvider extends IzumiProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ZK_SYNC; + + protected readonly dexAddress = '0x943ac2310D9BC703d6AB5e5e76876e212100f894'; + + protected readonly config = { + maxTransitTokens: 2, + quoterAddress: '0x1a447a8Ec35B8120549C6567fC6FCC0768b318C2', + liquidityManagerAddress: '0x483FDE31bcE3DCc168E23a870831b50Ce2cCd1F1', + routingTokenAddresses: [ + '0x3355df6d4c9c3035724fd0e3914de96a5a83aaf4', // USDC + wrappedNativeTokensList[BLOCKCHAIN_NAME.ZK_SYNC]!.address, // WRAP + '0x2039bb4116b4efc145ec4f0e2ea75012d6c0f181', // BUSD, + '0x496d88d1efc3e145b7c12d53b78ce5e7eda7a42c' // SL USDT + ], + multicallAddress: '0x18d6b2F2A5F88380D42AdD6588F4484Cfb27EE07', + supportedFees: [10000, 3000, 2000, 400] + }; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/constants.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/constants.ts new file mode 100644 index 0000000..583b966 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/constants.ts @@ -0,0 +1,5 @@ +import { defaultZkSyncProviderConfiguration } from 'src/features/on-chain/calculation-manager/providers/dexes/zksync/default-constants'; + +export const MUTE_SWAP_CONTRACT_ADDRESS = '0x8B791913eB07C32779a16750e3868aA8495F5964'; + +export const MUTE_SWAP_PROVIDER_CONFIGURATION = defaultZkSyncProviderConfiguration; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/mute-swap-abi.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/mute-swap-abi.ts new file mode 100644 index 0000000..e098ccb --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/mute-swap-abi.ts @@ -0,0 +1,126 @@ +import { AbiItem } from 'web3-utils'; + +export const muteSwapAbi: AbiItem[] = [ + { + inputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' } + ], + name: 'getAmountOut', + outputs: [ + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'bool', name: 'stable', type: 'bool' }, + { internalType: 'uint256', name: 'fee', type: 'uint256' } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'address[]', name: 'path', type: 'address[]' }, + { internalType: 'bool[]', name: 'stable', type: 'bool[]' } + ], + name: 'getAmountsOut', + outputs: [{ internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'address[]', name: 'path', type: 'address[]' } + ], + name: 'getAmountsOutExpanded', + outputs: [ + { internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }, + { internalType: 'bool[]', name: 'stable', type: 'bool[]' }, + { internalType: 'uint256[]', name: 'fees', type: 'uint256[]' } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' }, + { internalType: 'address[]', name: 'path', type: 'address[]' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'bool[]', name: 'stable', type: 'bool[]' } + ], + name: 'swapExactETHForTokens', + outputs: [{ internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' }, + { internalType: 'address[]', name: 'path', type: 'address[]' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'bool[]', name: 'stable', type: 'bool[]' } + ], + name: 'swapExactETHForTokensSupportingFeeOnTransferTokens', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' }, + { internalType: 'address[]', name: 'path', type: 'address[]' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'bool[]', name: 'stable', type: 'bool[]' } + ], + name: 'swapExactTokensForETH', + outputs: [{ internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' }, + { internalType: 'address[]', name: 'path', type: 'address[]' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'bool[]', name: 'stable', type: 'bool[]' } + ], + name: 'swapExactTokensForETHSupportingFeeOnTransferTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' }, + { internalType: 'address[]', name: 'path', type: 'address[]' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'bool[]', name: 'stable', type: 'bool[]' } + ], + name: 'swapExactTokensForTokens', + outputs: [{ internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' }, + { internalType: 'address[]', name: 'path', type: 'address[]' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'bool[]', name: 'stable', type: 'bool[]' } + ], + name: 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + } +]; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/mute-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/mute-swap-provider.ts new file mode 100644 index 0000000..0d6b362 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/mute-swap-provider.ts @@ -0,0 +1,12 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { UniswapV2AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-provider'; +import { MUTE_SWAP_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/constants'; +import { MuteSwapTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/mute-swap-trade'; + +export class MuteSwapProvider extends UniswapV2AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ZK_SYNC; + + public readonly UniswapV2TradeClass = MuteSwapTrade; + + public readonly providerSettings = MUTE_SWAP_PROVIDER_CONFIGURATION; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/mute-swap-trade.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/mute-swap-trade.ts new file mode 100644 index 0000000..5aba851 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/mute-swap-trade.ts @@ -0,0 +1,52 @@ +import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { ContractMulticallResponse } from 'src/core/blockchain/web3-public-service/web3-public/models/contract-multicall-response'; +import { Injector } from 'src/core/injector/injector'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { DefaultRoutesMethodArgument } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/models/route-method-arguments'; +import { UniswapV2AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v2-abstract/uniswap-v2-abstract-trade'; +import { MUTE_SWAP_CONTRACT_ADDRESS } from 'src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/constants'; +import { muteSwapAbi } from 'src/features/on-chain/calculation-manager/providers/dexes/zksync/mute-swap/mute-swap-abi'; + +export class MuteSwapTrade extends UniswapV2AbstractTrade { + public static callForRoutes( + blockchain: EvmBlockchainName, + exact: Exact, + routesMethodArguments: DefaultRoutesMethodArgument[] + ): Promise[]> { + const web3Public = Injector.web3PublicService.getWeb3Public(blockchain); + const methodName = exact === 'input' ? 'getAmountsOut' : 'getAmountsIn'; + const args = routesMethodArguments.map(arg => [arg[0], arg[1], arg[1].map(() => false)]); + + return web3Public.multicallContractMethod( + this.getDexContractAddress(blockchain), + this.contractAbi, + methodName, + args + ); + } + + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.MUTE_SWAP; + } + + public static readonly contractAbi = muteSwapAbi; + + public readonly dexContractAddress = MUTE_SWAP_CONTRACT_ADDRESS; + + protected getCallParameters(receiverAddress?: string): unknown[] { + const { amountIn, amountOut } = this.getAmountInAndAmountOut(); + const amountParameters = this.from.isNative ? [amountOut] : [amountIn, amountOut]; + + return [ + ...amountParameters, + this.wrappedPath.map(t => t.address), + receiverAddress || this.walletAddress, + this.deadlineMinutesTimestamp, + this.wrappedPath.map(() => false) + ]; + } +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/sync-swap/zksync-sync-swap-provider.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/sync-swap/zksync-sync-swap-provider.ts new file mode 100644 index 0000000..8378ff5 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/sync-swap/zksync-sync-swap-provider.ts @@ -0,0 +1,26 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { SyncSwapAbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/sync-swap-abstract/sync-swap-abstract-provider'; + +export class ZkSyncSyncSwapProvider extends SyncSwapAbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.ZK_SYNC; + + public readonly dexContractAddress = '0x2da10A1e27bF85cEdD8FFb1AbBe97e53391C0295'; + + public readonly routerHelperContract = '0x5c07e74cb541c3d1875aeee441d691ded6eba204'; + + public readonly vault = '0x621425a1Ef6abE91058E9712575dcc4258F8d091'; + + public readonly factories = [ + '0xf2dad89f2788a8cd54625c60b55cd3d2d0aca7cb', + '0x5b9f21d407f35b10cbfddca17d5d84b129356ea3' + ]; + + public readonly routeTokens = [ + '0x5aea5775959fbc2557cc8789bc1bf90a239d9a91', // WETH + '0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4' // USDC + ]; + + public readonly masterAddress = '0xbb05918e9b4ba9fe2c8384d223f0844867909ffb'; + + public readonly maxTransitTokens = 1; +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/utils.ts b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/utils.ts new file mode 100644 index 0000000..e396c5c --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/providers/dexes/zksync/utils.ts @@ -0,0 +1,78 @@ +import { BigNumber } from 'ethers'; + +interface GetAmountOutParams { + amountIn: BigNumber; + reserveIn: BigNumber; + reserveOut: BigNumber; + swapFee: BigNumber; + A?: BigNumber; + tokenInPrecisionMultiplier?: BigNumber; + tokenOutPrecisionMultiplier?: BigNumber; +} + +const MAX_LOOP_LIMIT = 256; +const ZERO = BigNumber.from(0); + +function within1(a: BigNumber, b: BigNumber): boolean { + if (a.gt(b)) { + return a.sub(b).lte(1); + } + return b.sub(a).lte(1); +} + +function computeDFromAdjustedBalances(A: BigNumber, xp0: BigNumber, xp1: BigNumber): BigNumber { + const s = xp0.add(xp1); + + if (s.isZero()) { + return ZERO; + } + let prevD; + let d = s; + const nA = A.mul(2); + for (let i = 0; i < MAX_LOOP_LIMIT; i++) { + const dP = d.mul(d).div(xp0).mul(d).div(xp1).div(4); + prevD = d; + d = nA + .mul(s) + .add(dP.mul(2)) + .mul(d) + .div(nA.sub(1).mul(d).add(dP.mul(3))); + if (within1(d, prevD)) { + return d; + } + } + return d; +} + +const MAX_FEE = BigNumber.from(100000); // 1e5 + +function getY(A: BigNumber, x: BigNumber, d: BigNumber): BigNumber { + let c = d.mul(d).div(x.mul(2)); + const nA = A.mul(2); + c = c.mul(d).div(nA.mul(2)); + const b = x.add(d.div(nA)); + let yPrev; + let y = d; + for (let i = 0; i < MAX_LOOP_LIMIT; i++) { + yPrev = y; + y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); + if (within1(y, yPrev)) { + break; + } + } + return y; +} + +export function getAmountOutStable(params: GetAmountOutParams): BigNumber { + const adjustedReserveIn = params.reserveIn.mul(params.tokenInPrecisionMultiplier!); + const adjustedReserveOut = params.reserveOut.mul(params.tokenOutPrecisionMultiplier!); + const feeDeductedAmountIn = params.amountIn.sub( + params.amountIn.mul(params.swapFee).div(MAX_FEE) + ); + const d = computeDFromAdjustedBalances(params.A!, adjustedReserveIn, adjustedReserveOut); + + const x = adjustedReserveIn.add(feeDeductedAmountIn.mul(params.tokenInPrecisionMultiplier!)); + const y = getY(params.A!, x, d); + const dy = adjustedReserveOut.sub(y).sub(1); + return dy.div(params.tokenOutPrecisionMultiplier!); +} diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/utils/get-on-chain-gas-data.ts b/SDK-mstr/src/features/on-chain/calculation-manager/utils/get-on-chain-gas-data.ts new file mode 100644 index 0000000..ac0b8dd --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/utils/get-on-chain-gas-data.ts @@ -0,0 +1,44 @@ +import BigNumber from 'bignumber.js'; +import { Injector } from 'src/core/injector/injector'; +import { AggregatorEvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract'; + +export const getOnChainGasData: ( + trade: AggregatorEvmOnChainTrade, + receiverAddress?: string +) => Promise = async ( + trade: AggregatorEvmOnChainTrade, + receiverAddress?: string +) => { + const fromBlockchain = trade.from.blockchain; + const walletAddress = + Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address; + if (!walletAddress) { + return null; + } + + try { + const transactionConfig = await trade.encode({ fromAddress: walletAddress }); + + const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain); + const gasLimit = ( + await web3Public.batchEstimatedGas(walletAddress, [transactionConfig]) + )[0]; + + if (gasLimit?.isFinite()) { + return gasLimit; + } + } catch {} + try { + const transactionData = await trade.getTxConfigAndCheckAmount({ + fromAddress: walletAddress, + skipAmountCheck: true, + useCacheData: true, + receiverAddress: receiverAddress || walletAddress + }); + + if (transactionData.gas) { + return new BigNumber(transactionData.gas); + } + } catch {} + return null; +}; diff --git a/SDK-mstr/src/features/on-chain/calculation-manager/utils/type-guards.ts b/SDK-mstr/src/features/on-chain/calculation-manager/utils/type-guards.ts new file mode 100644 index 0000000..8d7dd18 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/calculation-manager/utils/type-guards.ts @@ -0,0 +1,12 @@ +type Indices = T['length'] extends L + ? T[number] + : Indices; + +type LengthAtLeast = Pick, Indices>; + +export function hasLengthAtLeast( + arr: T, + len: L +): arr is T & LengthAtLeast { + return arr.length >= len; +} diff --git a/SDK-mstr/src/features/on-chain/status-manager/on-chain-status-manager.ts b/SDK-mstr/src/features/on-chain/status-manager/on-chain-status-manager.ts new file mode 100644 index 0000000..8590c95 --- /dev/null +++ b/SDK-mstr/src/features/on-chain/status-manager/on-chain-status-manager.ts @@ -0,0 +1,22 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; +import { TX_STATUS } from 'src/core/blockchain/web3-public-service/web3-public/models/tx-status'; +import { TxStatusData } from 'src/features/common/status-manager/models/tx-status-data'; +import { getBridgersTradeStatus } from 'src/features/common/status-manager/utils/get-bridgers-trade-status'; +import { getSrcTxStatus } from 'src/features/common/status-manager/utils/get-src-tx-status'; + +export class OnChainStatusManager { + /** + * Get Bridgers trade transaction status. + */ + public async getBridgersSwapStatus(srcTxHash: string): Promise { + const srcTxStatus = await getSrcTxStatus(BLOCKCHAIN_NAME.TRON, srcTxHash); + if (srcTxStatus === TX_STATUS.FAIL) { + return { + status: TX_STATUS.FAIL, + hash: srcTxHash + }; + } + + return getBridgersTradeStatus(srcTxHash, BLOCKCHAIN_NAME.TRON, 'rubic_widget'); + } +} diff --git a/SDK-mstr/tsconfig.json b/SDK-mstr/tsconfig.json new file mode 100644 index 0000000..42a2fc6 --- /dev/null +++ b/SDK-mstr/tsconfig.json @@ -0,0 +1,48 @@ +{ + "compilerOptions": { + "declaration": true, + "lib": [ + "ES2015", + "ES2016", + "ES2017", + "ES2018", + "ES2019", + "ES2020", + "ESNext" + ], + "experimentalDecorators": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "target": "es2020", + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "useUnknownInCatchVariables": false, + "noUncheckedIndexedAccess": true, + "outDir": "lib", + "baseUrl": ".", + "paths": { + "src/*": ["./src/*"] + }, + "plugins": [ + { "transform": "typescript-transform-paths" }, + { "transform": "typescript-transform-paths", "afterDeclarations": true } + ], + "rootDir": "src", + "allowJs": true, + "skipLibCheck": true + }, + "include": ["src"], + "exclude": ["__tests__" ,"node_modules", "lib", "types"] +} diff --git a/SDK-mstr/tsconfig.test.json b/SDK-mstr/tsconfig.test.json new file mode 100644 index 0000000..b1c22bc --- /dev/null +++ b/SDK-mstr/tsconfig.test.json @@ -0,0 +1,40 @@ +{ + "compilerOptions": { + "declaration": true, + "lib": [ + "ES2015", + "ES2016", + "ES2017", + "ES2018", + "ES2019", + "ES2020", + "ESNext" + ], + "experimentalDecorators": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "target": "es2020", + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "useUnknownInCatchVariables": false, + "outDir": "dist-tests", + "baseUrl": ".", + "paths": { + "src/*": ["./src/*"], + "__tests__/*": ["./__tests__/*"] + }, + "allowJs": true + } +} diff --git a/SDK-mstr/typedoc.json b/SDK-mstr/typedoc.json new file mode 100644 index 0000000..adb75c9 --- /dev/null +++ b/SDK-mstr/typedoc.json @@ -0,0 +1,7 @@ +{ + "entryPoints": ["src/index.ts"], + "excludeInternal": true, + "excludePrivate": true, + "excludeProtected": true, + "out": "docs" +} diff --git a/SDK-mstr/webpack.config.js b/SDK-mstr/webpack.config.js new file mode 100644 index 0000000..4581d21 --- /dev/null +++ b/SDK-mstr/webpack.config.js @@ -0,0 +1,85 @@ +const path = require('path'); +const webpack = require('webpack'); +const TerserPlugin = require('terser-webpack-plugin'); + +module.exports = function(env, argv) { + const isProduction = argv.mode === 'production'; + return { + entry: './src/index.ts', + module: { + rules: [ + { + test: /\.ts?$/, + use: [ + { + loader: require.resolve('ts-loader'), + options: { + compiler: 'ts-patch/compiler', + }, + }, + ], + exclude: ['/node_modules', '/lib'], + }, + { + test: /\.m?js/, + resolve: { + fullySpecified: false + } + } + ] + }, + plugins: [ + new webpack.IgnorePlugin({ + checkResource(resource) { + // "@ethereumjs/common/genesisStates" consists ~800KB static files which are no more needed + return /(.*\/genesisStates\/.*\.json)/.test(resource) + }, + }), + new webpack.ProvidePlugin({ + Buffer: ['buffer', 'Buffer'], + process: 'process/browser' + }), + new webpack.SourceMapDevToolPlugin({ + test: [/\.ts$/], + exclude: 'vendor', + filename: "app.[hash].js.map", + append: "//# sourceMappingURL=[url]", + moduleFilenameTemplate: '[resource-path]', + fallbackModuleFilenameTemplate: '[resource-path]', + }), + ], + resolve: { + extensions: ['.ts', '.js'], + alias: { + "src": path.resolve(__dirname, 'src'), + // To avoid blotting up the `bn.js` library all over the packages + // use single library instance. + "bn.js": path.resolve(__dirname, 'node_modules/bn.js') + }, + fallback: { + "path": false, + "os": false, + "url": require.resolve("url"), + "http": require.resolve("http-browserify"), + "https": require.resolve("https-browserify"), + "stream": require.resolve("stream-browserify"), + "crypto": require.resolve("crypto-browserify"), + "querystring": require.resolve('querystring-es3'), + "zlib": require.resolve('browserify-zlib') + } + }, + output: { + filename: 'rubic-sdk.min.js', + path: path.resolve(__dirname, 'dist'), + library: "RubicSDK", + clean: true + }, + // optimization: { + // minimizer: [new TerserPlugin({ + // extractComments: false + // })], + // }, + devtool: 'source-map', + mode: 'development', + } +}; diff --git a/SDK-mstr/yarn.lock b/SDK-mstr/yarn.lock new file mode 100644 index 0000000..2f1bbfa --- /dev/null +++ b/SDK-mstr/yarn.lock @@ -0,0 +1,10828 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@1inch/limit-order-protocol-utils@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@1inch/limit-order-protocol-utils/-/limit-order-protocol-utils-3.0.1.tgz#0f7437ec7c2eb1162e4a1f0738df771d30c122d4" + integrity sha512-HpZ1ZRP0PKNOuma1zvffdEfXl64CH/9oajug9Q26HPgyf0Ot+epiArI6rzqKRFvALOHp5Kl6kixiA2qFmGDQzA== + dependencies: + "@ethersproject/abi" "^5.1.1" + "@ethersproject/bignumber" "^5.1.1" + "@metamask/eth-sig-util" "^4.0.1" + prompts "^2.4.1" + web3 "^1.8.1" + yargs "^17.0.1" + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@adraffy/ens-normalize@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" + integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== + +"@adraffy/ens-normalize@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.9.0.tgz#223572538f6bea336750039bb43a4016dcc8182d" + integrity sha512-iowxq3U30sghZotgl4s/oJRci6WPBfNO5YYgk2cIOMCHr3LeGPcsZjCEr+33Q4N+oV3OABDAtA+pyvWjbvBifQ== + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@arbitrum/sdk@^3.1.3": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-3.1.6.tgz#0a64728040bd1ca4d6d8024946991308d248f303" + integrity sha512-wY7RHmvY26yc/OuwpJY+kjgAmUJZDGDXaQxfSQTp2t6sSFO+8oFFVsKIthBCY2RpDGFUVTGpRjCUEXiuJ6/SFA== + dependencies: + "@ethersproject/address" "^5.0.8" + "@ethersproject/bignumber" "^5.1.1" + "@ethersproject/bytes" "^5.0.8" + ethers "^5.1.0" + +"@aws-crypto/crc32@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-3.0.0.tgz#07300eca214409c33e3ff769cd5697b57fdd38fa" + integrity sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA== + dependencies: + "@aws-crypto/util" "^3.0.0" + "@aws-sdk/types" "^3.222.0" + tslib "^1.11.1" + +"@aws-crypto/crc32c@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32c/-/crc32c-3.0.0.tgz#016c92da559ef638a84a245eecb75c3e97cb664f" + integrity sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w== + dependencies: + "@aws-crypto/util" "^3.0.0" + "@aws-sdk/types" "^3.222.0" + tslib "^1.11.1" + +"@aws-crypto/ie11-detection@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz#640ae66b4ec3395cee6a8e94ebcd9f80c24cd688" + integrity sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q== + dependencies: + tslib "^1.11.1" + +"@aws-crypto/sha1-browser@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha1-browser/-/sha1-browser-3.0.0.tgz#f9083c00782b24714f528b1a1fef2174002266a3" + integrity sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw== + dependencies: + "@aws-crypto/ie11-detection" "^3.0.0" + "@aws-crypto/supports-web-crypto" "^3.0.0" + "@aws-crypto/util" "^3.0.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-browser@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz#05f160138ab893f1c6ba5be57cfd108f05827766" + integrity sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ== + dependencies: + "@aws-crypto/ie11-detection" "^3.0.0" + "@aws-crypto/sha256-js" "^3.0.0" + "@aws-crypto/supports-web-crypto" "^3.0.0" + "@aws-crypto/util" "^3.0.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-js@3.0.0", "@aws-crypto/sha256-js@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz#f06b84d550d25521e60d2a0e2a90139341e007c2" + integrity sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ== + dependencies: + "@aws-crypto/util" "^3.0.0" + "@aws-sdk/types" "^3.222.0" + tslib "^1.11.1" + +"@aws-crypto/supports-web-crypto@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz#5d1bf825afa8072af2717c3e455f35cda0103ec2" + integrity sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg== + dependencies: + tslib "^1.11.1" + +"@aws-crypto/util@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-3.0.0.tgz#1c7ca90c29293f0883468ad48117937f0fe5bfb0" + integrity sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w== + dependencies: + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-sdk/client-s3@^3.54.1": + version "3.503.1" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.503.1.tgz#bd0790ddfe0923c7c32f99f745486bbb88a53374" + integrity sha512-wN7MjCkeWgO55Tu85zpsGXQQOPNBm11elgik2AV2fJQMq5PCFQEZK/3N4ICNt0ZsGkQPVsXQTf+k9GKBxolt0g== + dependencies: + "@aws-crypto/sha1-browser" "3.0.0" + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/client-sts" "3.502.0" + "@aws-sdk/core" "3.496.0" + "@aws-sdk/credential-provider-node" "3.503.1" + "@aws-sdk/middleware-bucket-endpoint" "3.502.0" + "@aws-sdk/middleware-expect-continue" "3.502.0" + "@aws-sdk/middleware-flexible-checksums" "3.502.0" + "@aws-sdk/middleware-host-header" "3.502.0" + "@aws-sdk/middleware-location-constraint" "3.502.0" + "@aws-sdk/middleware-logger" "3.502.0" + "@aws-sdk/middleware-recursion-detection" "3.502.0" + "@aws-sdk/middleware-sdk-s3" "3.502.0" + "@aws-sdk/middleware-signing" "3.502.0" + "@aws-sdk/middleware-ssec" "3.502.0" + "@aws-sdk/middleware-user-agent" "3.502.0" + "@aws-sdk/region-config-resolver" "3.502.0" + "@aws-sdk/signature-v4-multi-region" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@aws-sdk/util-user-agent-browser" "3.502.0" + "@aws-sdk/util-user-agent-node" "3.502.0" + "@aws-sdk/xml-builder" "3.496.0" + "@smithy/config-resolver" "^2.1.1" + "@smithy/core" "^1.3.1" + "@smithy/eventstream-serde-browser" "^2.1.1" + "@smithy/eventstream-serde-config-resolver" "^2.1.1" + "@smithy/eventstream-serde-node" "^2.1.1" + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/hash-blob-browser" "^2.1.1" + "@smithy/hash-node" "^2.1.1" + "@smithy/hash-stream-node" "^2.1.1" + "@smithy/invalid-dependency" "^2.1.1" + "@smithy/md5-js" "^2.1.1" + "@smithy/middleware-content-length" "^2.1.1" + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/middleware-retry" "^2.1.1" + "@smithy/middleware-serde" "^2.1.1" + "@smithy/middleware-stack" "^2.1.1" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/url-parser" "^2.1.1" + "@smithy/util-base64" "^2.1.1" + "@smithy/util-body-length-browser" "^2.1.1" + "@smithy/util-body-length-node" "^2.2.1" + "@smithy/util-defaults-mode-browser" "^2.1.1" + "@smithy/util-defaults-mode-node" "^2.1.1" + "@smithy/util-endpoints" "^1.1.1" + "@smithy/util-retry" "^2.1.1" + "@smithy/util-stream" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" + "@smithy/util-waiter" "^2.1.1" + fast-xml-parser "4.2.5" + tslib "^2.5.0" + +"@aws-sdk/client-sso-oidc@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.502.0.tgz#f3d151a9185baedcb7f511a1149b65cb2973df30" + integrity sha512-Yc9tZqTOMWtdgpkrdjKShgWb9oKNsFQrItfoiN1xWDllaFFRPi2KTiZiR0AbSTrNasJy13d210DOxrIdte+kWQ== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/client-sts" "3.502.0" + "@aws-sdk/core" "3.496.0" + "@aws-sdk/middleware-host-header" "3.502.0" + "@aws-sdk/middleware-logger" "3.502.0" + "@aws-sdk/middleware-recursion-detection" "3.502.0" + "@aws-sdk/middleware-signing" "3.502.0" + "@aws-sdk/middleware-user-agent" "3.502.0" + "@aws-sdk/region-config-resolver" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@aws-sdk/util-user-agent-browser" "3.502.0" + "@aws-sdk/util-user-agent-node" "3.502.0" + "@smithy/config-resolver" "^2.1.1" + "@smithy/core" "^1.3.1" + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/hash-node" "^2.1.1" + "@smithy/invalid-dependency" "^2.1.1" + "@smithy/middleware-content-length" "^2.1.1" + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/middleware-retry" "^2.1.1" + "@smithy/middleware-serde" "^2.1.1" + "@smithy/middleware-stack" "^2.1.1" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/url-parser" "^2.1.1" + "@smithy/util-base64" "^2.1.1" + "@smithy/util-body-length-browser" "^2.1.1" + "@smithy/util-body-length-node" "^2.2.1" + "@smithy/util-defaults-mode-browser" "^2.1.1" + "@smithy/util-defaults-mode-node" "^2.1.1" + "@smithy/util-endpoints" "^1.1.1" + "@smithy/util-retry" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" + tslib "^2.5.0" + +"@aws-sdk/client-sso@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.502.0.tgz#8cf21d8f52a5bef65bf7b458051b3f61f7db822c" + integrity sha512-OZAYal1+PQgUUtWiHhRayDtX0OD+XpXHKAhjYgEIPbyhQaCMp3/Bq1xDX151piWXvXqXLJHFKb8DUEqzwGO9QA== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/core" "3.496.0" + "@aws-sdk/middleware-host-header" "3.502.0" + "@aws-sdk/middleware-logger" "3.502.0" + "@aws-sdk/middleware-recursion-detection" "3.502.0" + "@aws-sdk/middleware-user-agent" "3.502.0" + "@aws-sdk/region-config-resolver" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@aws-sdk/util-user-agent-browser" "3.502.0" + "@aws-sdk/util-user-agent-node" "3.502.0" + "@smithy/config-resolver" "^2.1.1" + "@smithy/core" "^1.3.1" + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/hash-node" "^2.1.1" + "@smithy/invalid-dependency" "^2.1.1" + "@smithy/middleware-content-length" "^2.1.1" + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/middleware-retry" "^2.1.1" + "@smithy/middleware-serde" "^2.1.1" + "@smithy/middleware-stack" "^2.1.1" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/url-parser" "^2.1.1" + "@smithy/util-base64" "^2.1.1" + "@smithy/util-body-length-browser" "^2.1.1" + "@smithy/util-body-length-node" "^2.2.1" + "@smithy/util-defaults-mode-browser" "^2.1.1" + "@smithy/util-defaults-mode-node" "^2.1.1" + "@smithy/util-endpoints" "^1.1.1" + "@smithy/util-retry" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" + tslib "^2.5.0" + +"@aws-sdk/client-sts@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.502.0.tgz#975efa09066a25af1c9f47b6e98f241c4445ae63" + integrity sha512-0q08gsvn6nuRqjK+i/e30PT/t7vvYwmGJS0PhJikZWv5yRDNSUxSYG0uDwKSbLDzmc2UX5+mLeyjPHlL4hbGlA== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/core" "3.496.0" + "@aws-sdk/middleware-host-header" "3.502.0" + "@aws-sdk/middleware-logger" "3.502.0" + "@aws-sdk/middleware-recursion-detection" "3.502.0" + "@aws-sdk/middleware-user-agent" "3.502.0" + "@aws-sdk/region-config-resolver" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@aws-sdk/util-user-agent-browser" "3.502.0" + "@aws-sdk/util-user-agent-node" "3.502.0" + "@smithy/config-resolver" "^2.1.1" + "@smithy/core" "^1.3.1" + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/hash-node" "^2.1.1" + "@smithy/invalid-dependency" "^2.1.1" + "@smithy/middleware-content-length" "^2.1.1" + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/middleware-retry" "^2.1.1" + "@smithy/middleware-serde" "^2.1.1" + "@smithy/middleware-stack" "^2.1.1" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/url-parser" "^2.1.1" + "@smithy/util-base64" "^2.1.1" + "@smithy/util-body-length-browser" "^2.1.1" + "@smithy/util-body-length-node" "^2.2.1" + "@smithy/util-defaults-mode-browser" "^2.1.1" + "@smithy/util-defaults-mode-node" "^2.1.1" + "@smithy/util-endpoints" "^1.1.1" + "@smithy/util-middleware" "^2.1.1" + "@smithy/util-retry" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" + fast-xml-parser "4.2.5" + tslib "^2.5.0" + +"@aws-sdk/core@3.496.0": + version "3.496.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.496.0.tgz#ec1394753b6b2f6e38aea593e30b2db5c7390969" + integrity sha512-yT+ug7Cw/3eJi7x2es0+46x12+cIJm5Xv+GPWsrTFD1TKgqO/VPEgfDtHFagDNbFmjNQA65Ygc/kEdIX9ICX/A== + dependencies: + "@smithy/core" "^1.3.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/signature-v4" "^2.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/credential-provider-env@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.502.0.tgz#800e63b2b9d90b078a120d474d5a3b1ec5b48514" + integrity sha512-KIB8Ae1Z7domMU/jU4KiIgK4tmYgvuXlhR54ehwlVHxnEoFPoPuGHFZU7oFn79jhhSLUFQ1lRYMxP0cEwb7XeQ== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/credential-provider-http@3.503.1": + version "3.503.1" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.503.1.tgz#e882a4b740c9193650053033b3001b03ca4b12c8" + integrity sha512-rTdlFFGoPPFMF2YjtlfRuSgKI+XsF49u7d98255hySwhsbwd3Xp+utTTPquxP+CwDxMHbDlI7NxDzFiFdsoZug== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/property-provider" "^2.1.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/util-stream" "^2.1.1" + tslib "^2.5.0" + +"@aws-sdk/credential-provider-ini@3.503.1": + version "3.503.1" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.503.1.tgz#888034b9c47901c76c5d0edc80c331832abcbe1c" + integrity sha512-1RiC72NdWJ5w2IaX/91Fku+FrrChzaHuMCD5wbjk5TMHjwiDZ622wvMKYVmn30biW0RbJidaw38Y9PAGivdZxw== + dependencies: + "@aws-sdk/client-sts" "3.502.0" + "@aws-sdk/credential-provider-env" "3.502.0" + "@aws-sdk/credential-provider-process" "3.502.0" + "@aws-sdk/credential-provider-sso" "3.503.1" + "@aws-sdk/credential-provider-web-identity" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@smithy/credential-provider-imds" "^2.2.1" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/credential-provider-node@3.503.1": + version "3.503.1" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.503.1.tgz#05348bf2bf94120929aa2a6b8548efda6477e985" + integrity sha512-1qsRWnXl8OUZEDpUFF/gjCGjePjZB6fIpX+XQuTpKokeDzYZk0HwQSakPspfmzT8MkyB9IBJVWb7KbFCjKNt0A== + dependencies: + "@aws-sdk/credential-provider-env" "3.502.0" + "@aws-sdk/credential-provider-http" "3.503.1" + "@aws-sdk/credential-provider-ini" "3.503.1" + "@aws-sdk/credential-provider-process" "3.502.0" + "@aws-sdk/credential-provider-sso" "3.503.1" + "@aws-sdk/credential-provider-web-identity" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@smithy/credential-provider-imds" "^2.2.1" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/credential-provider-process@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.502.0.tgz#6c41d8845a1c7073491a064c158363de04640381" + integrity sha512-fJJowOjQ4infYQX0E1J3xFVlmuwEYJAFk0Mo1qwafWmEthsBJs+6BR2RiWDELHKrSK35u4Pf3fu3RkYuCtmQFw== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/credential-provider-sso@3.503.1": + version "3.503.1" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.503.1.tgz#0d71247cd086d74ba4b9b3dafa562f5227a5bcff" + integrity sha512-Wj+rgpD4EcGB+j6mMYPD4SPNEN0sUSq7UMTTfdzZ+1MSTnbPDC9rgde0D3yJPrJ1le/0j+sXPALvr5RKSpENbg== + dependencies: + "@aws-sdk/client-sso" "3.502.0" + "@aws-sdk/token-providers" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/credential-provider-web-identity@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.502.0.tgz#d45a8e78578f4613dc62275548a7196c01f5ed17" + integrity sha512-veBAjDqjMMgA2Qxxf9ywDfHYLeJpaeHWLWCQ9XCHwJJ6ZIGWmAZPTq3he/UMr5JIQXooIccqqyqXMDIXPenXpA== + dependencies: + "@aws-sdk/client-sts" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/middleware-bucket-endpoint@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.502.0.tgz#580ef680c8b3b1230d78598089414769331f2a4a" + integrity sha512-mUSP2DUcjhO5zM2b21CvZ9AqwI8DaAeZA6NYHOxWGTV9BUxHcdGWXEjDkcVj9CQ0gvNwTtw6B5L/q52rVAnZbw== + dependencies: + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-arn-parser" "3.495.0" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/types" "^2.9.1" + "@smithy/util-config-provider" "^2.2.1" + tslib "^2.5.0" + +"@aws-sdk/middleware-expect-continue@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.502.0.tgz#fd265571e1f3a4855714fda34252de751fb054dd" + integrity sha512-DxfAuBVuPSt8as9xP57o8ks6ySVSjwO2NNNAdpLwk4KhEAPYEpHlf2yWYorYLrS+dDmwfYgOhRNoguuBdCu6ow== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/protocol-http" "^3.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/middleware-flexible-checksums@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.502.0.tgz#e4fa059f3c4603bd815f16d69e1c4277c4c4830a" + integrity sha512-kCt2zQDFumz/LnJJJOSd2GW4dr8oT8YMJKgxC/pph3aRXoSHXRwhrMbFnQ8swEE9vjywxtcED8sym0b0tNhhoA== + dependencies: + "@aws-crypto/crc32" "3.0.0" + "@aws-crypto/crc32c" "3.0.0" + "@aws-sdk/types" "3.502.0" + "@smithy/is-array-buffer" "^2.1.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/types" "^2.9.1" + "@smithy/util-utf8" "^2.1.1" + tslib "^2.5.0" + +"@aws-sdk/middleware-host-header@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.502.0.tgz#2651fb3509990271c89eb50133fb17cb8ae435f6" + integrity sha512-EjnG0GTYXT/wJBmm5/mTjDcAkzU8L7wQjOzd3FTXuTCNNyvAvwrszbOj5FlarEw5XJBbQiZtBs+I5u9+zy560w== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/protocol-http" "^3.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/middleware-location-constraint@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.502.0.tgz#188c3bae2e908aff11030af10bc3b64c87905910" + integrity sha512-fLRwPuTZvEWQkPjys03m3D6tYN4kf7zU6+c8mJxwvEg+yfBuv2RBsbd+Vn2bTisUjXvIg1kyBzONlpHoIyFneg== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/middleware-logger@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.502.0.tgz#558cefdd233779f15687957f9f07497199b22d72" + integrity sha512-FDyv6K4nCoHxbjLGS2H8ex8I0KDIiu4FJgVRPs140ZJy6gE5Pwxzv6YTzZGLMrnqcIs9gh065Lf6DjwMelZqaw== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/middleware-recursion-detection@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.502.0.tgz#c22e2c0c1d551e58c788264687324bb7186af2cc" + integrity sha512-hvbyGJbxeuezxOu8VfFmcV4ql1hKXLxHTe5FNYfEBat2KaZXVhc1Hg+4TvB06/53p+E8J99Afmumkqbxs2esUA== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/protocol-http" "^3.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/middleware-sdk-s3@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.502.0.tgz#a2d968414247fd9cbfc90e1071f29e4375cb25b8" + integrity sha512-GbGugrfyL5bNA/zw8iQll92yXBONfWSC8Ns00DtkOU1saPXp4/7WHtyyZGYdvPa73T1IsuZy9egpoYRBmRcd5Q== + dependencies: + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-arn-parser" "3.495.0" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/signature-v4" "^2.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/util-config-provider" "^2.2.1" + tslib "^2.5.0" + +"@aws-sdk/middleware-signing@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-signing/-/middleware-signing-3.502.0.tgz#48b3503147eecb1a53a63633462de353668f635a" + integrity sha512-4hF08vSzJ7L6sB+393gOFj3s2N6nLusYS0XrMW6wYNFU10IDdbf8Z3TZ7gysDJJHEGQPmTAesPEDBsasGWcMxg== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/signature-v4" "^2.1.1" + "@smithy/types" "^2.9.1" + "@smithy/util-middleware" "^2.1.1" + tslib "^2.5.0" + +"@aws-sdk/middleware-ssec@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.502.0.tgz#0abdebedd98b284112c2eda36f707111a8a3cbd5" + integrity sha512-1nidVTIba6/aVjjzD/WNqWdzSyTrXOHO3Ddz2MGD8S1yGSrYz4iYaq4Bm/uosfdr8B1L0Ws0pjdRXrNfzSw/DQ== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/middleware-user-agent@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.502.0.tgz#dd740f150d6f3110cf5b08fedf361d202f899c93" + integrity sha512-TxbBZbRiXPH0AUxegqiNd9aM9zNSbfjtBs5MEfcBsweeT/B2O7K1EjP9+CkB8Xmk/5FLKhAKLr19b1TNoE27rw== + dependencies: + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@smithy/protocol-http" "^3.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/region-config-resolver@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.502.0.tgz#c18a04060879eb03c47c05b05fc296119ee073ba" + integrity sha512-mxmsX2AGgnSM+Sah7mcQCIneOsJQNiLX0COwEttuf8eO+6cLMAZvVudH3BnWTfea4/A9nuri9DLCqBvEmPrilg== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/types" "^2.9.1" + "@smithy/util-config-provider" "^2.2.1" + "@smithy/util-middleware" "^2.1.1" + tslib "^2.5.0" + +"@aws-sdk/signature-v4-multi-region@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.502.0.tgz#2d3fab86051eb98a4e4216e0f2f3d957a854b42c" + integrity sha512-NpOXtUXH0ZAgnyI3Y3s2fPrgwbsWoNMwdoXdFZvH0eDzzX80tim7Yuy6dzVA5zrxSzOYs1xjcOhM+4CmM0QZiw== + dependencies: + "@aws-sdk/middleware-sdk-s3" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@smithy/protocol-http" "^3.1.1" + "@smithy/signature-v4" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/token-providers@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.502.0.tgz#d477638d5bd0c47c881e63fa6395b238fe4a021c" + integrity sha512-RQgMgIXYlSf0xGl6EUeD+pqIPBlb7e29dbqHOBFc66hJVYUC2ULZX7Y+jLvcGIEaMiIaTPyvntZRFip+U+9hag== + dependencies: + "@aws-sdk/client-sso-oidc" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/types@3.502.0", "@aws-sdk/types@^3.222.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.502.0.tgz#c23dda4df7fdbe32642d4f5ab23516f455fb6aba" + integrity sha512-M0DSPYe/gXhwD2QHgoukaZv5oDxhW3FfvYIrJptyqUq3OnPJBcDbihHjrE0PBtfh/9kgMZT60/fQ2NVFANfa2g== + dependencies: + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/util-arn-parser@3.495.0": + version "3.495.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.495.0.tgz#539f2d6dfef343a80324348f1f9a1b7eed2390f3" + integrity sha512-hwdA3XAippSEUxs7jpznwD63YYFR+LtQvlEcebPTgWR9oQgG9TfS+39PUfbnEeje1ICuOrN3lrFqFbmP9uzbMg== + dependencies: + tslib "^2.5.0" + +"@aws-sdk/util-endpoints@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.502.0.tgz#aee818c0c53dfedfd49599fc260cd880faea5e82" + integrity sha512-6LKFlJPp2J24r1Kpfoz5ESQn+1v5fEjDB3mtUKRdpwarhm3syu7HbKlHCF3KbcCOyahobvLvhoedT78rJFEeeg== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/types" "^2.9.1" + "@smithy/util-endpoints" "^1.1.1" + tslib "^2.5.0" + +"@aws-sdk/util-locate-window@^3.0.0": + version "3.495.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.495.0.tgz#9034fd8db77991b28ed20e067acdd53e8b8f824b" + integrity sha512-MfaPXT0kLX2tQaR90saBT9fWQq2DHqSSJRzW+MZWsmF+y5LGCOhO22ac/2o6TKSQm7h0HRc2GaADqYYYor62yg== + dependencies: + tslib "^2.5.0" + +"@aws-sdk/util-user-agent-browser@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.502.0.tgz#87b42abff6944052c78a84981637ac21859dd016" + integrity sha512-v8gKyCs2obXoIkLETAeEQ3AM+QmhHhst9xbM1cJtKUGsRlVIak/XyyD+kVE6kmMm1cjfudHpHKABWk9apQcIZQ== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/types" "^2.9.1" + bowser "^2.11.0" + tslib "^2.5.0" + +"@aws-sdk/util-user-agent-node@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.502.0.tgz#04ac4d0371d4f243f12ddc23b42ca8ceb27dfad9" + integrity sha512-9RjxpkGZKbTdl96tIJvAo+vZoz4P/cQh36SBUt9xfRfW0BtsaLyvSrvlR5wyUYhvRcC12Axqh/8JtnAPq//+Vw== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@aws-sdk/util-utf8-browser@^3.0.0": + version "3.259.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz#3275a6f5eb334f96ca76635b961d3c50259fd9ff" + integrity sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/xml-builder@3.496.0": + version "3.496.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.496.0.tgz#7d0ef487a8088ef84a5a9aad228e6173ca85341d" + integrity sha512-GvEjh537IIeOw1ZkZuB37sV12u+ipS5Z1dwjEC/HAvhl5ac23ULtTr1/n+U1gLNN+BAKSWjKiQ2ksj8DiUzeyw== + dependencies: + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== + dependencies: + "@babel/highlight" "^7.22.5" + +"@babel/compat-data@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" + integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== + +"@babel/core@^7.0.0-0", "@babel/core@^7.11.6", "@babel/core@^7.12.3": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.9.tgz#bd96492c68822198f33e8a256061da3cf391f58f" + integrity sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.9" + "@babel/helper-module-transforms" "^7.22.9" + "@babel/helpers" "^7.22.6" + "@babel/parser" "^7.22.7" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.8" + "@babel/types" "^7.22.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.1" + +"@babel/generator@^7.22.7", "@babel/generator@^7.22.9", "@babel/generator@^7.7.2": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d" + integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw== + dependencies: + "@babel/types" "^7.22.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-compilation-targets@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892" + integrity sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.5" + browserslist "^4.21.9" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== + +"@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-transforms@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" + integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + +"@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" + integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== + +"@babel/helpers@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd" + integrity sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA== + dependencies: + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.6" + "@babel/types" "^7.22.5" + +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.5", "@babel/parser@^7.22.7": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" + integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" + integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz#aac8d383b062c5072c647a31ef990c1d0af90272" + integrity sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/runtime@^7.0.0": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" + integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ== + dependencies: + regenerator-runtime "^0.13.11" + +"@babel/runtime@^7.17.2", "@babel/runtime@^7.23.4": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650" + integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/template@^7.22.5", "@babel/template@^7.3.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8": + version "7.22.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" + integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.7" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.7" + "@babel/types" "^7.22.5" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.3.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@bundlr-network/client@^0.7.14": + version "0.7.17" + resolved "https://registry.yarnpkg.com/@bundlr-network/client/-/client-0.7.17.tgz#0a4c41534640f9038e5d7d1d8cf1cc2571b185b7" + integrity sha512-1qTDrwgmgeh0pO24JbeGt2W8GlpWYkVnQ8AhEZ02Lm00J7RALSyma3C5pNlKuvAQBczL1r9KhLr9KHK1og3J0g== + dependencies: + "@solana/wallet-adapter-base" "^0.9.2" + "@solana/web3.js" "^1.36.0" + "@supercharge/promise-pool" "^2.1.0" + algosdk "^1.13.1" + arbundles "^0.6.20" + arweave "^1.11.4" + async-retry "^1.3.3" + axios "^0.25.0" + base64url "^3.0.1" + bignumber.js "^9.0.1" + bs58 "^4.0.1" + commander "^8.2.0" + csv "^6.0.5" + ethers "^5.5.1" + inquirer "^8.2.0" + js-sha256 "^0.9.0" + mime-types "^2.1.34" + near-api-js "^0.44.2" + near-seed-phrase "^0.2.0" + +"@debridge-finance/debridge-external-call@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@debridge-finance/debridge-external-call/-/debridge-external-call-1.0.7.tgz#f7e8f85193de27a6552a07cc52e4d8d8df2c7460" + integrity sha512-MA93NzDNhgI5KzpMFuenWp5Qfm13q1fkuvge903Kg9/o1JcDgL0coH2Nf0I0OZctqlGC3d34wj3ABQzCaB8KAw== + +"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.6.0.tgz#5b63f0df5528a44e28aa8578d393de908cc3d4d0" + integrity sha512-uiPeRISaglZnaZk8vwrjQZ1CxogZeY/4IYft6gBOTqu1WhVXWmCmZMWxUv2Q/pxSvPdp1JPaO62kLOcOkMqWrw== + +"@eslint/eslintrc@^1.3.0": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e" + integrity sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.4.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@ethereumjs/common@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.5.0.tgz#ec61551b31bef7a69d1dc634d8932468866a4268" + integrity sha512-DEHjW6e38o+JmB/NO3GZBpW4lpaiBpkFgXF6jLcJ6gETBYpEyaA5nTimsWBUJR3Vmtm/didUEbNjajskugZORg== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.1.1" + +"@ethereumjs/common@^2.5.0": + version "2.6.5" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.5.tgz#0a75a22a046272579d91919cb12d84f2756e8d30" + integrity sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.1.5" + +"@ethereumjs/tx@3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.3.2.tgz#348d4624bf248aaab6c44fec2ae67265efe3db00" + integrity sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog== + dependencies: + "@ethereumjs/common" "^2.5.0" + ethereumjs-util "^7.1.2" + +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.1.1", "@ethersproject/abi@^5.6.3", "@ethersproject/abi@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" + integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + +"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/address@5.7.0", "@ethersproject/address@^5.0.8", "@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + +"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + +"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" + integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.1.1", "@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + +"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.0.8", "@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + +"@ethersproject/contracts@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" + integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + +"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf" + integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/wordlists" "^5.7.0" + +"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360" + integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hdnode" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + +"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102" + integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + +"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/providers@5.7.2": + version "5.7.2" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" + integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + bech32 "1.1.4" + ws "7.4.6" + +"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" + integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" + integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + hash.js "1.1.7" + +"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/solidity@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" + integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.6.2", "@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + +"@ethersproject/units@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" + integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/wallet@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d" + integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/hdnode" "^5.7.0" + "@ethersproject/json-wallets" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/wordlists" "^5.7.0" + +"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5" + integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@graphql-typed-document-node/core@^3.1.1", "@graphql-typed-document-node/core@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" + integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== + +"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + +"@hapi/topo@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@humanwhocodes/config-array@^0.10.4": + version "0.10.7" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.7.tgz#6d53769fd0c222767e6452e8ebda825c22e9f0dc" + integrity sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/gitignore-to-minimatch@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d" + integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.6.1.tgz#b48ba7b9c34b51483e6d590f46e5837f1ab5f639" + integrity sha512-Aj772AYgwTSr5w8qnyoJ0eDYvN6bMsH3ORH1ivMotrInHLKdUz6BDlaEXHdM6kODaBIkNIyQGzsMvRdOv7VG7Q== + dependencies: + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + slash "^3.0.0" + +"@jest/core@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.6.1.tgz#fac0d9ddf320490c93356ba201451825231e95f6" + integrity sha512-CcowHypRSm5oYQ1obz1wfvkjZZ2qoQlrKKvlfPwh5jUXVU12TWr2qMeH8chLMuTFzHh5a1g2yaqlqDICbr+ukQ== + dependencies: + "@jest/console" "^29.6.1" + "@jest/reporters" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.5.0" + jest-config "^29.6.1" + jest-haste-map "^29.6.1" + jest-message-util "^29.6.1" + jest-regex-util "^29.4.3" + jest-resolve "^29.6.1" + jest-resolve-dependencies "^29.6.1" + jest-runner "^29.6.1" + jest-runtime "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" + jest-watcher "^29.6.1" + micromatch "^4.0.4" + pretty-format "^29.6.1" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.6.1.tgz#ee358fff2f68168394b4a50f18c68278a21fe82f" + integrity sha512-RMMXx4ws+Gbvw3DfLSuo2cfQlK7IwGbpuEWXCqyYDcqYTI+9Ju3a5hDnXaxjNsa6uKh9PQF2v+qg+RLe63tz5A== + dependencies: + "@jest/fake-timers" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + jest-mock "^29.6.1" + +"@jest/expect-utils@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.1.tgz#ab83b27a15cdd203fe5f68230ea22767d5c3acc5" + integrity sha512-o319vIf5pEMx0LmzSxxkYYxo4wrRLKHq9dP1yJU7FoPTB0LfAKSz8SWD6D/6U3v/O52t9cF5t+MeJiRsfk7zMw== + dependencies: + jest-get-type "^29.4.3" + +"@jest/expect@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.6.1.tgz#fef18265188f6a97601f1ea0a2912d81a85b4657" + integrity sha512-N5xlPrAYaRNyFgVf2s9Uyyvr795jnB6rObuPx4QFvNJz8aAjpZUDfO4bh5G/xuplMID8PrnuF1+SfSyDxhsgYg== + dependencies: + expect "^29.6.1" + jest-snapshot "^29.6.1" + +"@jest/fake-timers@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.6.1.tgz#c773efddbc61e1d2efcccac008139f621de57c69" + integrity sha512-RdgHgbXyosCDMVYmj7lLpUwXA4c69vcNzhrt69dJJdf8azUrpRh3ckFCaTPNjsEeRi27Cig0oKDGxy5j7hOgHg== + dependencies: + "@jest/types" "^29.6.1" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.6.1" + jest-mock "^29.6.1" + jest-util "^29.6.1" + +"@jest/globals@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.6.1.tgz#c8a8923e05efd757308082cc22893d82b8aa138f" + integrity sha512-2VjpaGy78JY9n9370H8zGRCFbYVWwjY6RdDMhoJHa1sYfwe6XM/azGN0SjY8kk7BOZApIejQ1BFPyH7FPG0w3A== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/expect" "^29.6.1" + "@jest/types" "^29.6.1" + jest-mock "^29.6.1" + +"@jest/reporters@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.6.1.tgz#3325a89c9ead3cf97ad93df3a427549d16179863" + integrity sha512-9zuaI9QKr9JnoZtFQlw4GREQbxgmNYXU6QuWtmuODvk5nvPUeBYapVR/VYMyi2WSx3jXTLJTJji8rN6+Cm4+FA== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + jest-worker "^29.6.1" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + +"@jest/schemas@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" + integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.0.tgz#bd34a05b5737cb1a99d43e1957020ac8e5b9ddb1" + integrity sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" + +"@jest/test-result@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.6.1.tgz#850e565a3f58ee8ca6ec424db00cb0f2d83c36ba" + integrity sha512-Ynr13ZRcpX6INak0TPUukU8GWRfm/vAytE3JbJNGAvINySWYdfE7dGZMbk36oVuK4CigpbhMn8eg1dixZ7ZJOw== + dependencies: + "@jest/console" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.6.1.tgz#e3e582ee074dd24ea9687d7d1aaf05ee3a9b068e" + integrity sha512-oBkC36PCDf/wb6dWeQIhaviU0l5u6VCsXa119yqdUosYAt7/FbQU2M2UoziO3igj/HBDEgp57ONQ3fm0v9uyyg== + dependencies: + "@jest/test-result" "^29.6.1" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.1" + slash "^3.0.0" + +"@jest/transform@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.6.1.tgz#acb5606019a197cb99beda3c05404b851f441c92" + integrity sha512-URnTneIU3ZjRSaf906cvf6Hpox3hIeJXRnz3VDSw5/X93gR8ycdfSIEy19FlVx8NFmpN7fe3Gb1xF+NjXaQLWg== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.1" + jest-regex-util "^29.4.3" + jest-util "^29.6.1" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + +"@jest/types@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.1.tgz#ae79080278acff0a6af5eb49d063385aaa897bf2" + integrity sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw== + dependencies: + "@jest/schemas" "^29.6.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@layerzerolabs/scan-client@^0.0.0-beta.6": + version "0.0.0-beta.6" + resolved "https://registry.yarnpkg.com/@layerzerolabs/scan-client/-/scan-client-0.0.0-beta.6.tgz#079275514e2a386677fc8129ced52a3c3f0a1b9a" + integrity sha512-TjhEfMcmN5Q8Gtog45lj6in+y8OzGPoLzUzSF45cJO703Lrlwf5tAA85TQnnRWPGBiChw70KG2S6nzgmMRQWJg== + dependencies: + axios "^0.27.2" + +"@metamask/eth-sig-util@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" + integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^6.2.1" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + +"@metaplex-foundation/beet-solana@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet-solana/-/beet-solana-0.1.1.tgz#85deec5d3354ee1da868bd6bb0de5ae1ea92d209" + integrity sha512-QV2DbxjaJWLkMvn12OC09g+r7a6R0uNwf8msYuOUSw4cG7amXzvFb7s0bh4IxY3Rk8/0ma0PfKi/FEdC7Hi4Pg== + dependencies: + "@metaplex-foundation/beet" ">=0.1.0" + "@solana/web3.js" "^1.31.0" + +"@metaplex-foundation/beet-solana@^0.3.0", "@metaplex-foundation/beet-solana@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet-solana/-/beet-solana-0.3.1.tgz#4b37cda5c7f32ffd2bdd8b3164edc05c6463ab35" + integrity sha512-tgyEl6dvtLln8XX81JyBvWjIiEcjTkUwZbrM5dIobTmoqMuGewSyk9CClno8qsMsFdB5T3jC91Rjeqmu/6xk2g== + dependencies: + "@metaplex-foundation/beet" ">=0.1.0" + "@solana/web3.js" "^1.56.2" + bs58 "^5.0.0" + debug "^4.3.4" + +"@metaplex-foundation/beet-solana@^0.4.0": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet-solana/-/beet-solana-0.4.1.tgz#255747aa7feee1c20202146a752c057feca1948f" + integrity sha512-/6o32FNUtwK8tjhotrvU/vorP7umBuRFvBZrC6XCk51aKidBHe5LPVPA5AjGPbV3oftMfRuXPNd9yAGeEqeCDQ== + dependencies: + "@metaplex-foundation/beet" ">=0.1.0" + "@solana/web3.js" "^1.56.2" + bs58 "^5.0.0" + debug "^4.3.4" + +"@metaplex-foundation/beet@>=0.1.0", "@metaplex-foundation/beet@^0.7.1": + version "0.7.2" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.7.2.tgz#fa4726e4cfd4fb6fed6cddc9b5213c1c2a2d0b77" + integrity sha512-K+g3WhyFxKPc0xIvcIjNyV1eaTVJTiuaHZpig7Xx0MuYRMoJLLvhLTnUXhFdR5Tu2l2QSyKwfyXDgZlzhULqFg== + dependencies: + ansicolors "^0.3.2" + assert "^2.1.0" + bn.js "^5.2.0" + debug "^4.3.3" + +"@metaplex-foundation/beet@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.2.0.tgz#e543e17fd1c4dc1251e9aea481a7429bc73f70b8" + integrity sha512-H570hkJxmx/FxET1OggPPLkPL7psYQa71rNI9NJjYRM8WXdrEvmI/IRIEUW2KR6RqwWWN3FvlRHnKoQUV/lQtA== + dependencies: + ansicolors "^0.3.2" + bn.js "^5.2.0" + debug "^4.3.3" + +"@metaplex-foundation/beet@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.4.0.tgz#eb2a0a6eb084bb25d67dd9bed2f7387ee7e63a55" + integrity sha512-2OAKJnLatCc3mBXNL0QmWVQKAWK2C7XDfepgL0p/9+8oSx4bmRAFHFqptl1A/C0U5O3dxGwKfmKluW161OVGcA== + dependencies: + ansicolors "^0.3.2" + bn.js "^5.2.0" + debug "^4.3.3" + +"@metaplex-foundation/beet@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.6.1.tgz#6331bdde0648bf2cae6f9e482f8e3552db05d69f" + integrity sha512-OYgnijLFzw0cdUlRKH5POp0unQECPOW9muJ2X3QIVyak5G6I6l/rKo72sICgPLIFKdmsi2jmnkuLY7wp14iXdw== + dependencies: + ansicolors "^0.3.2" + bn.js "^5.2.0" + debug "^4.3.3" + +"@metaplex-foundation/cusper@^0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/cusper/-/cusper-0.0.2.tgz#dc2032a452d6c269e25f016aa4dd63600e2af975" + integrity sha512-S9RulC2fFCFOQraz61bij+5YCHhSO9llJegK8c8Y6731fSi6snUSQJdCUqYS8AIgR0TKbQvdvgSyIIdbDFZbBA== + +"@metaplex-foundation/js@0.11.7": + version "0.11.7" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/js/-/js-0.11.7.tgz#72b986f2ce49698ddf46fa3800245bc41c61d5a1" + integrity sha512-zL3Ac++8lUOoKjjhDArdNSimvVYcOqg4uhxfBVRKLV6H8q6E/sOepXuzYSLXxzYWdvapYWEPKFdEDFluYzk7DA== + dependencies: + "@aws-sdk/client-s3" "^3.54.1" + "@bundlr-network/client" "^0.7.14" + "@metaplex-foundation/beet" "^0.2.0" + "@metaplex-foundation/beet-solana" "^0.1.1" + "@metaplex-foundation/mpl-auction-house" "^2.0.0" + "@metaplex-foundation/mpl-candy-machine" "^4.2.0" + "@metaplex-foundation/mpl-token-metadata" "^2.1.2" + "@solana/spl-token" "^0.2.0" + "@solana/web3.js" "^1.44.0" + abort-controller "^3.0.0" + bignumber.js "^9.0.2" + bn.js "^5.2.0" + bs58 "^5.0.0" + buffer "^6.0.3" + cross-fetch "^3.1.5" + debug "^4.3.4" + eventemitter3 "^4.0.7" + lodash.clonedeep "^4.5.0" + mime "^3.0.0" + tweetnacl "^1.0.3" + +"@metaplex-foundation/mpl-auction-house@^2.0.0": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/mpl-auction-house/-/mpl-auction-house-2.5.1.tgz#ea0e21e594b0db5e73f88688eb2e7c9b748b378b" + integrity sha512-O+IAdYVaoOvgACB8pm+1lF5BNEjl0COkqny2Ho8KQZwka6aC/vHbZ239yRwAMtJhf5992BPFdT4oifjyE0O+Mw== + dependencies: + "@metaplex-foundation/beet" "^0.6.1" + "@metaplex-foundation/beet-solana" "^0.3.1" + "@metaplex-foundation/cusper" "^0.0.2" + "@solana/spl-token" "^0.3.5" + "@solana/web3.js" "^1.56.2" + bn.js "^5.2.0" + +"@metaplex-foundation/mpl-candy-machine@^4.2.0": + version "4.7.1" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/mpl-candy-machine/-/mpl-candy-machine-4.7.1.tgz#ecf6ebbe96245084c9a186761ecc51c62ce1206f" + integrity sha512-tBNRAfBE/rYy9pe2aJD4gTFw+pgQ11o3AJjoYGB4+05ow0VjJMSt6kQGzHm2LRPgdLY4diKAq8qHvgsbV5ikNQ== + dependencies: + "@metaplex-foundation/beet" "^0.4.0" + "@metaplex-foundation/beet-solana" "^0.3.0" + "@metaplex-foundation/cusper" "^0.0.2" + "@metaplex-foundation/mpl-core" "^0.6.1" + "@solana/web3.js" "^1.35.1" + +"@metaplex-foundation/mpl-core@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/mpl-core/-/mpl-core-0.6.1.tgz#bdc76a8b447a2ef310693973bf430e5ecec6ef0b" + integrity sha512-6R4HkfAqU2EUakNbVLcCmka0YuQTLGTbHJ62ig765+NRWuB2HNGUQ1HfHcRsGnyxhlCvwKK79JE01XUjFE+dzw== + dependencies: + "@solana/web3.js" "^1.35.1" + bs58 "^4.0.1" + +"@metaplex-foundation/mpl-token-metadata@^2.1.2": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/mpl-token-metadata/-/mpl-token-metadata-2.13.0.tgz#ea498190ad4ed1d4c0b8218a72d03bd17a883d11" + integrity sha512-Fl/8I0L9rv4bKTV/RAl5YIbJe9SnQPInKvLz+xR1fEc4/VQkuCn3RPgypfUMEKWmCznzaw4sApDxy6CFS4qmJw== + dependencies: + "@metaplex-foundation/beet" "^0.7.1" + "@metaplex-foundation/beet-solana" "^0.4.0" + "@metaplex-foundation/cusper" "^0.0.2" + "@solana/spl-token" "^0.3.6" + "@solana/web3.js" "^1.66.2" + bn.js "^5.2.0" + debug "^4.3.4" + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@noble/curves@1.0.0", "@noble/curves@~1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932" + integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw== + dependencies: + "@noble/hashes" "1.3.0" + +"@noble/curves@1.1.0", "@noble/curves@~1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d" + integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA== + dependencies: + "@noble/hashes" "1.3.1" + +"@noble/curves@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" + +"@noble/curves@^1.2.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e" + integrity sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA== + dependencies: + "@noble/hashes" "1.3.3" + +"@noble/ed25519@^1.6.1": + version "1.7.3" + resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.3.tgz#57e1677bf6885354b466c38e2b620c62f45a7123" + integrity sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ== + +"@noble/hashes@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" + integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== + +"@noble/hashes@1.3.1", "@noble/hashes@^1.2.0", "@noble/hashes@~1.3.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" + integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== + +"@noble/hashes@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + +"@noble/hashes@1.3.3", "@noble/hashes@^1.3.2", "@noble/hashes@~1.3.1": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== + +"@noble/secp256k1@^1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@openzeppelin/contracts@3.4.1-solc-0.7-2": + version "3.4.1-solc-0.7-2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.1-solc-0.7-2.tgz#371c67ebffe50f551c3146a9eec5fe6ffe862e92" + integrity sha512-tAG9LWg8+M2CMu7hIsqHPaTyG4uDzjr6mhvH96LvOpLZZj6tgzTluBt+LsCf1/QaYrlis6pITvpIaIhE+iZB+Q== + +"@openzeppelin/contracts@3.4.2-solc-0.7": + version "3.4.2-solc-0.7" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2-solc-0.7.tgz#38f4dbab672631034076ccdf2f3201fab1726635" + integrity sha512-W6QmqgkADuFcTLzHL8vVoNBtkwjvQRpYIAom7KiUNoLKghyx3FgH0GBjt8NRvigV1ZmMOBllvE1By1C+bi8WpA== + +"@pancakeswap/sdk@5.1.0", "@pancakeswap/sdk@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@pancakeswap/sdk/-/sdk-5.1.0.tgz#ab15c565c081d62cf0d186d18c7fcac114a6b192" + integrity sha512-/vFAR8zzyWT9n0twC0MdnkV7QOr40IAgC48wkbL2S07Anjgkd38dkRfP8X3dYCsY8aGDT+c9cCJmGBdte82Lng== + dependencies: + "@pancakeswap/swap-sdk-core" "1.0.0" + big.js "^5.2.2" + decimal.js-light "^2.5.0" + tiny-invariant "^1.1.0" + tiny-warning "^1.0.3" + toformat "^2.0.0" + viem "^1.2.9" + +"@pancakeswap/smart-router@4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@pancakeswap/smart-router/-/smart-router-4.2.1.tgz#0d2336748da06f657eb33b139cd78986f14850f1" + integrity sha512-CXeV02NaEMuYd97Kr2Ak1Q3d9OUy6jMSu2Lgwc7ZPrb/lJCQuTVsiUeSWdIk56dbRJY26HvWs4CUyjM7Y6NUCw== + dependencies: + "@pancakeswap/sdk" "5.1.0" + "@pancakeswap/swap-sdk-core" "1.0.0" + "@pancakeswap/tokens" "0.1.6" + "@pancakeswap/v3-sdk" "3.1.1" + async-retry "^1.3.1" + debug "^4.3.4" + graphql "^16.6.0" + graphql-request "^5.0.0" + lodash "^4.17.21" + mnemonist "^0.38.3" + stats-lite "^2.2.0" + tiny-invariant "^1.1.0" + viem "^1.2.9" + zod "^3.21.4" + +"@pancakeswap/swap-sdk-core@1.0.0", "@pancakeswap/swap-sdk-core@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@pancakeswap/swap-sdk-core/-/swap-sdk-core-1.0.0.tgz#30ab333943ecca5484cd10cbc493841fb553728d" + integrity sha512-MZBF8FHpCX/ffY8/gd2zz6TpiftwqRouVPr1kB88wm7desYh+E2y4kWPcZX1roWR38UkjBv4/DoXVSDeSTBgRg== + dependencies: + big.js "^5.2.2" + decimal.js-light "^2.5.0" + tiny-invariant "^1.1.0" + tiny-warning "^1.0.3" + toformat "^2.0.0" + +"@pancakeswap/token-lists@0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@pancakeswap/token-lists/-/token-lists-0.0.8.tgz#a168cb8cdc7bf224812cc0e7f0382c46f3b93166" + integrity sha512-KTq31adfPqwqpHBLZ/rRkVlFpVIehB8UcmsuZpY1hjXqdNXUf5lO/Kad4tIxYtIlx9nrWM6R1laYxfISGzAMYQ== + dependencies: + "@pancakeswap/swap-sdk-core" "1.0.0" + ajv "^6.12.3" + lodash "^4.17.21" + +"@pancakeswap/tokens@0.1.6": + version "0.1.6" + resolved "https://registry.yarnpkg.com/@pancakeswap/tokens/-/tokens-0.1.6.tgz#c6f59c246bdc9f37322101acc2be700aee9b584b" + integrity sha512-JrHNVyBIxSRD62Prn+yeMbxcZdLs5yFY1Y1Xaex6+kCxDz/WNAKI+/Q9WnJZ7m/jsfuB/vjJN89gqCxGAc/VVw== + dependencies: + "@pancakeswap/sdk" "5.1.0" + "@pancakeswap/token-lists" "0.0.8" + +"@pancakeswap/v3-sdk@3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@pancakeswap/v3-sdk/-/v3-sdk-3.1.1.tgz#1482c54850fdd07a4a8d553c12460c616b6d8929" + integrity sha512-+Y5f6lSnLr1GzCzwoWUSuPD4hb5XbBbC4uJLYtOCa9i2Ug4zBpIJPvccUVCzGfqlqGO49JBuzseA+FnpgSDuFQ== + dependencies: + "@pancakeswap/sdk" "5.1.0" + "@pancakeswap/swap-sdk-core" "1.0.0" + "@pancakeswap/tokens" "0.1.6" + "@uniswap/v3-staker" "1.0.0" + big.js "^5.2.2" + decimal.js-light "^2.5.0" + tiny-invariant "^1.1.0" + tiny-warning "^1.0.3" + toformat "^2.0.0" + viem "^1.2.9" + +"@polka/url@^1.0.0-next.20": + version "1.0.0-next.21" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" + integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== + +"@randlabs/communication-bridge@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@randlabs/communication-bridge/-/communication-bridge-1.0.1.tgz#d1ecfc29157afcbb0ca2d73122d67905eecb5bf3" + integrity sha512-CzS0U8IFfXNK7QaJFE4pjbxDGfPjbXBEsEaCn9FN15F+ouSAEUQkva3Gl66hrkBZOGexKFEWMwUHIDKpZ2hfVg== + +"@randlabs/myalgo-connect@^1.1.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@randlabs/myalgo-connect/-/myalgo-connect-1.4.2.tgz#ce3ad97b3889ea21da75852187511d3f6be0fa05" + integrity sha512-K9hEyUi7G8tqOp7kWIALJLVbGCByhilcy6123WfcorxWwiE1sbQupPyIU5f3YdQK6wMjBsyTWiLW52ZBMp7sXA== + dependencies: + "@randlabs/communication-bridge" "1.0.1" + +"@scure/base@~1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" + integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== + +"@scure/bip32@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.0.tgz#6c8d980ef3f290987736acd0ee2e0f0d50068d87" + integrity sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q== + dependencies: + "@noble/curves" "~1.0.0" + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@scure/bip32@1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.1.tgz#7248aea723667f98160f593d621c47e208ccbb10" + integrity sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A== + dependencies: + "@noble/curves" "~1.1.0" + "@noble/hashes" "~1.3.1" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.0.tgz#a207e2ef96de354de7d0002292ba1503538fc77b" + integrity sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" + integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@sideway/address@^4.1.5": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" + integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.6.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + +"@smithy/abort-controller@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-2.1.1.tgz#bb68596a7c8213c2ef259bc7fb0f0c118c67ea9d" + integrity sha512-1+qdrUqLhaALYL0iOcN43EP6yAXXQ2wWZ6taf4S2pNGowmOc5gx+iMQv+E42JizNJjB0+gEadOXeV1Bf7JWL1Q== + dependencies: + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/chunked-blob-reader-native@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-2.1.1.tgz#6b98479c8f6ea94832dd6a6e5ca78969a44eafe1" + integrity sha512-zNW+43dltfNMUrBEYLMWgI8lQr0uhtTcUyxkgC9EP4j17WREzgSFMPUFVrVV6Rc2+QtWERYjb4tzZnQGa7R9fQ== + dependencies: + "@smithy/util-base64" "^2.1.1" + tslib "^2.5.0" + +"@smithy/chunked-blob-reader@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader/-/chunked-blob-reader-2.1.1.tgz#997faba8e197e0cb9824dad30ae581466e386e57" + integrity sha512-NjNFCKxC4jVvn+lUr3Yo4/PmUJj3tbyqH6GNHueyTGS5Q27vlEJ1MkNhUDV8QGxJI7Bodnc2pD18lU2zRfhHlQ== + dependencies: + tslib "^2.5.0" + +"@smithy/config-resolver@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-2.1.1.tgz#fc6b036084b98fd26a8ff01a5d7eb676e41749c7" + integrity sha512-lxfLDpZm+AWAHPFZps5JfDoO9Ux1764fOgvRUBpHIO8HWHcSN1dkgsago1qLRVgm1BZ8RCm8cgv99QvtaOWIhw== + dependencies: + "@smithy/node-config-provider" "^2.2.1" + "@smithy/types" "^2.9.1" + "@smithy/util-config-provider" "^2.2.1" + "@smithy/util-middleware" "^2.1.1" + tslib "^2.5.0" + +"@smithy/core@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-1.3.1.tgz#ecedc564e68453b02c20db9e8435d59005c066d8" + integrity sha512-tf+NIu9FkOh312b6M9G4D68is4Xr7qptzaZGZUREELF8ysE1yLKphqt7nsomjKZVwW7WE5pDDex9idowNGRQ/Q== + dependencies: + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/middleware-retry" "^2.1.1" + "@smithy/middleware-serde" "^2.1.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/util-middleware" "^2.1.1" + tslib "^2.5.0" + +"@smithy/credential-provider-imds@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-2.2.1.tgz#4805bf5e104718b959cf8699113fa9de6ddeeafa" + integrity sha512-7XHjZUxmZYnONheVQL7j5zvZXga+EWNgwEAP6OPZTi7l8J4JTeNh9aIOfE5fKHZ/ee2IeNOh54ZrSna+Vc6TFA== + dependencies: + "@smithy/node-config-provider" "^2.2.1" + "@smithy/property-provider" "^2.1.1" + "@smithy/types" "^2.9.1" + "@smithy/url-parser" "^2.1.1" + tslib "^2.5.0" + +"@smithy/eventstream-codec@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-2.1.1.tgz#4405ab0f9c77d439c575560c4886e59ee17d6d38" + integrity sha512-E8KYBxBIuU4c+zrpR22VsVrOPoEDzk35bQR3E+xm4k6Pa6JqzkDOdMyf9Atac5GPNKHJBdVaQ4JtjdWX2rl/nw== + dependencies: + "@aws-crypto/crc32" "3.0.0" + "@smithy/types" "^2.9.1" + "@smithy/util-hex-encoding" "^2.1.1" + tslib "^2.5.0" + +"@smithy/eventstream-serde-browser@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-2.1.1.tgz#743a374639e9e2dd858b6fda1fd814eb6c604946" + integrity sha512-JvEdCmGlZUay5VtlT8/kdR6FlvqTDUiJecMjXsBb0+k1H/qc9ME5n2XKPo8q/MZwEIA1GmGgYMokKGjVvMiDow== + dependencies: + "@smithy/eventstream-serde-universal" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/eventstream-serde-config-resolver@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-2.1.1.tgz#0b84d6f8be0836af7b92455c69f7427e4f01e7a2" + integrity sha512-EqNqXYp3+dk//NmW3NAgQr9bEQ7fsu/CcxQmTiq07JlaIcne/CBWpMZETyXm9w5LXkhduBsdXdlMscfDUDn2fA== + dependencies: + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/eventstream-serde-node@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-2.1.1.tgz#2e1afa27f9c7eb524c1c53621049c5e4e3cea6a5" + integrity sha512-LF882q/aFidFNDX7uROAGxq3H0B7rjyPkV6QDn6/KDQ+CG7AFkRccjxRf1xqajq/Pe4bMGGr+VKAaoF6lELIQw== + dependencies: + "@smithy/eventstream-serde-universal" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/eventstream-serde-universal@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-2.1.1.tgz#0f5eec9ad033017973a67bafb5549782499488d2" + integrity sha512-LR0mMT+XIYTxk4k2fIxEA1BPtW3685QlqufUEUAX1AJcfFfxNDKEvuCRZbO8ntJb10DrIFVJR9vb0MhDCi0sAQ== + dependencies: + "@smithy/eventstream-codec" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/fetch-http-handler@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-2.4.1.tgz#b4d73bbc1449f61234077d58c705b843a8587bf0" + integrity sha512-VYGLinPsFqH68lxfRhjQaSkjXM7JysUOJDTNjHBuN/ykyRb2f1gyavN9+VhhPTWCy32L4yZ2fdhpCs/nStEicg== + dependencies: + "@smithy/protocol-http" "^3.1.1" + "@smithy/querystring-builder" "^2.1.1" + "@smithy/types" "^2.9.1" + "@smithy/util-base64" "^2.1.1" + tslib "^2.5.0" + +"@smithy/hash-blob-browser@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/hash-blob-browser/-/hash-blob-browser-2.1.1.tgz#f4571d4e2fbc2cc1869c443850e5409bf541ba25" + integrity sha512-jizu1+2PAUjiGIfRtlPEU8Yo6zn+d78ti/ZHDesdf1SUn2BuZW433JlPoCOLH3dBoEEvTgLvQ8tUGSoTTALA+A== + dependencies: + "@smithy/chunked-blob-reader" "^2.1.1" + "@smithy/chunked-blob-reader-native" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/hash-node@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-2.1.1.tgz#0f8a22d97565ca948724f72267e4d3a2f33740a8" + integrity sha512-Qhoq0N8f2OtCnvUpCf+g1vSyhYQrZjhSwvJ9qvR8BUGOtTXiyv2x1OD2e6jVGmlpC4E4ax1USHoyGfV9JFsACg== + dependencies: + "@smithy/types" "^2.9.1" + "@smithy/util-buffer-from" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" + tslib "^2.5.0" + +"@smithy/hash-stream-node@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/hash-stream-node/-/hash-stream-node-2.1.1.tgz#d1885a3bf159872cbb8c9d9f1aefc596ea6cf5db" + integrity sha512-VgDaKcfCy0iHcmtAZgZ3Yw9g37Gkn2JsQiMtFQXUh8Wmo3GfNgDwLOtdhJ272pOT7DStzpe9cNr+eV5Au8KfQA== + dependencies: + "@smithy/types" "^2.9.1" + "@smithy/util-utf8" "^2.1.1" + tslib "^2.5.0" + +"@smithy/invalid-dependency@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-2.1.1.tgz#bd69fa24dd35e9bc65a160bd86becdf1399e4463" + integrity sha512-7WTgnKw+VPg8fxu2v9AlNOQ5yaz6RA54zOVB4f6vQuR0xFKd+RzlCpt0WidYTsye7F+FYDIaS/RnJW4pxjNInw== + dependencies: + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/is-array-buffer@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.1.1.tgz#07b4c77ae67ed58a84400c76edd482271f9f957b" + integrity sha512-xozSQrcUinPpNPNPds4S7z/FakDTh1MZWtRP/2vQtYB/u3HYrX2UXuZs+VhaKBd6Vc7g2XPr2ZtwGBNDN6fNKQ== + dependencies: + tslib "^2.5.0" + +"@smithy/md5-js@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-2.1.1.tgz#f414982bc6ab275b80ec517d2e44a779c374ff9c" + integrity sha512-L3MbIYBIdLlT+MWTYrdVSv/dow1+6iZ1Ad7xS0OHxTTs17d753ZcpOV4Ro7M7tRAVWML/sg2IAp/zzCb6aAttg== + dependencies: + "@smithy/types" "^2.9.1" + "@smithy/util-utf8" "^2.1.1" + tslib "^2.5.0" + +"@smithy/middleware-content-length@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-2.1.1.tgz#df767de12d594bc5622009fb0fc8343522697d8c" + integrity sha512-rSr9ezUl9qMgiJR0UVtVOGEZElMdGFyl8FzWEF5iEKTlcWxGr2wTqGfDwtH3LAB7h+FPkxqv4ZU4cpuCN9Kf/g== + dependencies: + "@smithy/protocol-http" "^3.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/middleware-endpoint@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-2.4.1.tgz#9e500df4d944741808e92018ccd2e948b598a49f" + integrity sha512-XPZTb1E2Oav60Ven3n2PFx+rX9EDsU/jSTA8VDamt7FXks67ekjPY/XrmmPDQaFJOTUHJNKjd8+kZxVO5Ael4Q== + dependencies: + "@smithy/middleware-serde" "^2.1.1" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/url-parser" "^2.1.1" + "@smithy/util-middleware" "^2.1.1" + tslib "^2.5.0" + +"@smithy/middleware-retry@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-2.1.1.tgz#ddc749dd927f136714f76ca5a52dcfb0993ee162" + integrity sha512-eMIHOBTXro6JZ+WWzZWd/8fS8ht5nS5KDQjzhNMHNRcG5FkNTqcKpYhw7TETMYzbLfhO5FYghHy1vqDWM4FLDA== + dependencies: + "@smithy/node-config-provider" "^2.2.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/service-error-classification" "^2.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/util-middleware" "^2.1.1" + "@smithy/util-retry" "^2.1.1" + tslib "^2.5.0" + uuid "^8.3.2" + +"@smithy/middleware-serde@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-2.1.1.tgz#2c5750f76e276a5249720f6c3c24fac29abbee16" + integrity sha512-D8Gq0aQBeE1pxf3cjWVkRr2W54t+cdM2zx78tNrVhqrDykRA7asq8yVJij1u5NDtKzKqzBSPYh7iW0svUKg76g== + dependencies: + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/middleware-stack@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-2.1.1.tgz#67f992dc36e8a6861f881f80a81c1c30956a0396" + integrity sha512-KPJhRlhsl8CjgGXK/DoDcrFGfAqoqvuwlbxy+uOO4g2Azn1dhH+GVfC3RAp+6PoL5PWPb+vt6Z23FP+Mr6qeCw== + dependencies: + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/node-config-provider@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-2.2.1.tgz#c440c7948d58d72f0e212aa1967aa12f0729defd" + integrity sha512-epzK3x1xNxA9oJgHQ5nz+2j6DsJKdHfieb+YgJ7ATWxzNcB7Hc+Uya2TUck5MicOPhDV8HZImND7ZOecVr+OWg== + dependencies: + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/node-http-handler@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-2.3.1.tgz#77d23279ff0a12cbe7cde93c5e7c0e86ad56dd20" + integrity sha512-gLA8qK2nL9J0Rk/WEZSvgin4AppvuCYRYg61dcUo/uKxvMZsMInL5I5ZdJTogOvdfVug3N2dgI5ffcUfS4S9PA== + dependencies: + "@smithy/abort-controller" "^2.1.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/querystring-builder" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/property-provider@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-2.1.1.tgz#0f7ffc5e43829eaca5b2b5aae8554807a52b30f3" + integrity sha512-FX7JhhD/o5HwSwg6GLK9zxrMUrGnb3PzNBrcthqHKBc3dH0UfgEAU24xnJ8F0uow5mj17UeBEOI6o3CF2k7Mhw== + dependencies: + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/protocol-http@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-3.1.1.tgz#eee522d0ed964a72b735d64925e07bcfb7a7806f" + integrity sha512-6ZRTSsaXuSL9++qEwH851hJjUA0OgXdQFCs+VDw4tGH256jQ3TjYY/i34N4vd24RV3nrjNsgd1yhb57uMoKbzQ== + dependencies: + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/querystring-builder@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-2.1.1.tgz#b9693448ad3f8e0767d84cf5cae29f35514591fb" + integrity sha512-C/ko/CeEa8jdYE4gt6nHO5XDrlSJ3vdCG0ZAc6nD5ZIE7LBp0jCx4qoqp7eoutBu7VrGMXERSRoPqwi1WjCPbg== + dependencies: + "@smithy/types" "^2.9.1" + "@smithy/util-uri-escape" "^2.1.1" + tslib "^2.5.0" + +"@smithy/querystring-parser@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-2.1.1.tgz#a4282a66cc56844317dbff824e573f469bbfc032" + integrity sha512-H4+6jKGVhG1W4CIxfBaSsbm98lOO88tpDWmZLgkJpt8Zkk/+uG0FmmqMuCAc3HNM2ZDV+JbErxr0l5BcuIf/XQ== + dependencies: + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/service-error-classification@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-2.1.1.tgz#dd24e1ec529ae9ec8e87d8b15f0fc8f7e17f3d02" + integrity sha512-txEdZxPUgM1PwGvDvHzqhXisrc5LlRWYCf2yyHfvITWioAKat7srQvpjMAvgzf0t6t7j8yHrryXU9xt7RZqFpw== + dependencies: + "@smithy/types" "^2.9.1" + +"@smithy/shared-ini-file-loader@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.3.1.tgz#a2e28b4d85f8a8262a84403fa2b74a086b3a7703" + integrity sha512-2E2kh24igmIznHLB6H05Na4OgIEilRu0oQpYXo3LCNRrawHAcfDKq9004zJs+sAMt2X5AbY87CUCJ7IpqpSgdw== + dependencies: + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/signature-v4@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-2.1.1.tgz#6080171e3d694f40d3f553bbc236c5c433efd4d2" + integrity sha512-Hb7xub0NHuvvQD3YwDSdanBmYukoEkhqBjqoxo+bSdC0ryV9cTfgmNjuAQhTPYB6yeU7hTR+sPRiFMlxqv6kmg== + dependencies: + "@smithy/eventstream-codec" "^2.1.1" + "@smithy/is-array-buffer" "^2.1.1" + "@smithy/types" "^2.9.1" + "@smithy/util-hex-encoding" "^2.1.1" + "@smithy/util-middleware" "^2.1.1" + "@smithy/util-uri-escape" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" + tslib "^2.5.0" + +"@smithy/smithy-client@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-2.3.1.tgz#0c3a4a0d3935c7ad2240cc23181f276705212b1f" + integrity sha512-YsTdU8xVD64r2pLEwmltrNvZV6XIAC50LN6ivDopdt+YiF/jGH6PY9zUOu0CXD/d8GMB8gbhnpPsdrjAXHS9QA== + dependencies: + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/middleware-stack" "^2.1.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/types" "^2.9.1" + "@smithy/util-stream" "^2.1.1" + tslib "^2.5.0" + +"@smithy/types@^2.9.1": + version "2.9.1" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-2.9.1.tgz#ed04d4144eed3b8bd26d20fc85aae8d6e357ebb9" + integrity sha512-vjXlKNXyprDYDuJ7UW5iobdmyDm6g8dDG+BFUncAg/3XJaN45Gy5RWWWUVgrzIK7S4R1KWgIX5LeJcfvSI24bw== + dependencies: + tslib "^2.5.0" + +"@smithy/url-parser@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-2.1.1.tgz#a30de227b6734650d740b6dff74d488b874e85e3" + integrity sha512-qC9Bv8f/vvFIEkHsiNrUKYNl8uKQnn4BdhXl7VzQRP774AwIjiSMMwkbT+L7Fk8W8rzYVifzJNYxv1HwvfBo3Q== + dependencies: + "@smithy/querystring-parser" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/util-base64@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-2.1.1.tgz#af729085cc9d92ebd54a5d2c5d0aa5a0c31f83bf" + integrity sha512-UfHVpY7qfF/MrgndI5PexSKVTxSZIdz9InghTFa49QOvuu9I52zLPLUHXvHpNuMb1iD2vmc6R+zbv/bdMipR/g== + dependencies: + "@smithy/util-buffer-from" "^2.1.1" + tslib "^2.5.0" + +"@smithy/util-body-length-browser@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-2.1.1.tgz#1fc77072768013ae646415eedb9833cd252d055d" + integrity sha512-ekOGBLvs1VS2d1zM2ER4JEeBWAvIOUKeaFch29UjjJsxmZ/f0L3K3x0dEETgh3Q9bkZNHgT+rkdl/J/VUqSRag== + dependencies: + tslib "^2.5.0" + +"@smithy/util-body-length-node@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-2.2.1.tgz#a6f5c9911f1c3e23efb340d5ce7a590b62f2056e" + integrity sha512-/ggJG+ta3IDtpNVq4ktmEUtOkH1LW64RHB5B0hcr5ZaWBmo96UX2cIOVbjCqqDickTXqBWZ4ZO0APuaPrD7Abg== + dependencies: + tslib "^2.5.0" + +"@smithy/util-buffer-from@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.1.1.tgz#f9346bf8b23c5ba6f6bdb61dd9db779441ba8d08" + integrity sha512-clhNjbyfqIv9Md2Mg6FffGVrJxw7bgK7s3Iax36xnfVj6cg0fUG7I4RH0XgXJF8bxi+saY5HR21g2UPKSxVCXg== + dependencies: + "@smithy/is-array-buffer" "^2.1.1" + tslib "^2.5.0" + +"@smithy/util-config-provider@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-2.2.1.tgz#aea0a80236d6cedaee60473802899cff4a8cc0ba" + integrity sha512-50VL/tx9oYYcjJn/qKqNy7sCtpD0+s8XEBamIFo4mFFTclKMNp+rsnymD796uybjiIquB7VCB/DeafduL0y2kw== + dependencies: + tslib "^2.5.0" + +"@smithy/util-defaults-mode-browser@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.1.1.tgz#be9ac82acee6ec4821b610e7187b0e147f0ba8ff" + integrity sha512-lqLz/9aWRO6mosnXkArtRuQqqZBhNpgI65YDpww4rVQBuUT7qzKbDLG5AmnQTCiU4rOquaZO/Kt0J7q9Uic7MA== + dependencies: + "@smithy/property-provider" "^2.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + bowser "^2.11.0" + tslib "^2.5.0" + +"@smithy/util-defaults-mode-node@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.1.1.tgz#0910ee00aac3e8a08aac3e6ae8794e52f3efef02" + integrity sha512-tYVrc+w+jSBfBd267KDnvSGOh4NMz+wVH7v4CClDbkdPfnjvImBZsOURncT5jsFwR9KCuDyPoSZq4Pa6+eCUrA== + dependencies: + "@smithy/config-resolver" "^2.1.1" + "@smithy/credential-provider-imds" "^2.2.1" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/property-provider" "^2.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/util-endpoints@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-1.1.1.tgz#45426dba6fb42282a0ad955600b2b3ba050d118f" + integrity sha512-sI4d9rjoaekSGEtq3xSb2nMjHMx8QXcz2cexnVyRWsy4yQ9z3kbDpX+7fN0jnbdOp0b3KSTZJZ2Yb92JWSanLw== + dependencies: + "@smithy/node-config-provider" "^2.2.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/util-hex-encoding@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-2.1.1.tgz#978252b9fb242e0a59bae4ead491210688e0d15f" + integrity sha512-3UNdP2pkYUUBGEXzQI9ODTDK+Tcu1BlCyDBaRHwyxhA+8xLP8agEKQq4MGmpjqb4VQAjq9TwlCQX0kP6XDKYLg== + dependencies: + tslib "^2.5.0" + +"@smithy/util-middleware@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-2.1.1.tgz#903ba19bb17704f4b476fb9ade9bf9eb0174bc3d" + integrity sha512-mKNrk8oz5zqkNcbcgAAepeJbmfUW6ogrT2Z2gDbIUzVzNAHKJQTYmH9jcy0jbWb+m7ubrvXKb6uMjkSgAqqsFA== + dependencies: + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/util-retry@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-2.1.1.tgz#f2d3566b6e5b841028c7240c852007d4037e49b2" + integrity sha512-Mg+xxWPTeSPrthpC5WAamJ6PW4Kbo01Fm7lWM1jmGRvmrRdsd3192Gz2fBXAMURyXpaNxyZf6Hr/nQ4q70oVEA== + dependencies: + "@smithy/service-error-classification" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@smithy/util-stream@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-2.1.1.tgz#3ae0e88c3a1a45899e29c1655d2e5a3865b6c0a6" + integrity sha512-J7SMIpUYvU4DQN55KmBtvaMc7NM3CZ2iWICdcgaovtLzseVhAqFRYqloT3mh0esrFw+3VEK6nQFteFsTqZSECQ== + dependencies: + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/util-base64" "^2.1.1" + "@smithy/util-buffer-from" "^2.1.1" + "@smithy/util-hex-encoding" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" + tslib "^2.5.0" + +"@smithy/util-uri-escape@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-2.1.1.tgz#7eedc93b73ecda68f12fb9cf92e9fa0fbbed4d83" + integrity sha512-saVzI1h6iRBUVSqtnlOnc9ssU09ypo7n+shdQ8hBTZno/9rZ3AuRYvoHInV57VF7Qn7B+pFJG7qTzFiHxWlWBw== + dependencies: + tslib "^2.5.0" + +"@smithy/util-utf8@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.1.1.tgz#690018dd28f47f014114497735e51417ea5900a6" + integrity sha512-BqTpzYEcUMDwAKr7/mVRUtHDhs6ZoXDi9NypMvMfOr/+u1NW7JgqodPDECiiLboEm6bobcPcECxzjtQh865e9A== + dependencies: + "@smithy/util-buffer-from" "^2.1.1" + tslib "^2.5.0" + +"@smithy/util-waiter@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-2.1.1.tgz#292d4d09cda7df38aba6ea2abd7d948e3f11bf2d" + integrity sha512-kYy6BLJJNif+uqNENtJqWdXcpqo1LS+nj1AfXcDhOpqpSHJSAkVySLyZV9fkmuVO21lzGoxjvd1imGGJHph/IA== + dependencies: + "@smithy/abort-controller" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + +"@solana/buffer-layout-utils@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz#b45a6cab3293a2eb7597cceb474f229889d875ca" + integrity sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g== + dependencies: + "@solana/buffer-layout" "^4.0.0" + "@solana/web3.js" "^1.32.0" + bigint-buffer "^1.1.5" + bignumber.js "^9.0.1" + +"@solana/buffer-layout@^4.0.0", "@solana/buffer-layout@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15" + integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA== + dependencies: + buffer "~6.0.3" + +"@solana/codecs-core@2.0.0-experimental.8618508": + version "2.0.0-experimental.8618508" + resolved "https://registry.yarnpkg.com/@solana/codecs-core/-/codecs-core-2.0.0-experimental.8618508.tgz#4f6709dd50e671267f3bea7d09209bc6471b7ad0" + integrity sha512-JCz7mKjVKtfZxkuDtwMAUgA7YvJcA2BwpZaA1NOLcted4OMC4Prwa3DUe3f3181ixPYaRyptbF0Ikq2MbDkYEA== + +"@solana/codecs-data-structures@2.0.0-experimental.8618508": + version "2.0.0-experimental.8618508" + resolved "https://registry.yarnpkg.com/@solana/codecs-data-structures/-/codecs-data-structures-2.0.0-experimental.8618508.tgz#c16a704ac0f743a2e0bf73ada42d830b3402d848" + integrity sha512-sLpjL9sqzaDdkloBPV61Rht1tgaKq98BCtIKRuyscIrmVPu3wu0Bavk2n/QekmUzaTsj7K1pVSniM0YqCdnEBw== + dependencies: + "@solana/codecs-core" "2.0.0-experimental.8618508" + "@solana/codecs-numbers" "2.0.0-experimental.8618508" + +"@solana/codecs-numbers@2.0.0-experimental.8618508": + version "2.0.0-experimental.8618508" + resolved "https://registry.yarnpkg.com/@solana/codecs-numbers/-/codecs-numbers-2.0.0-experimental.8618508.tgz#d84f9ed0521b22e19125eefc7d51e217fcaeb3e4" + integrity sha512-EXQKfzFr3CkKKNzKSZPOOOzchXsFe90TVONWsSnVkonO9z+nGKALE0/L9uBmIFGgdzhhU9QQVFvxBMclIDJo2Q== + dependencies: + "@solana/codecs-core" "2.0.0-experimental.8618508" + +"@solana/codecs-strings@2.0.0-experimental.8618508": + version "2.0.0-experimental.8618508" + resolved "https://registry.yarnpkg.com/@solana/codecs-strings/-/codecs-strings-2.0.0-experimental.8618508.tgz#72457b884d9be80b59b263bcce73892b081e9402" + integrity sha512-b2yhinr1+oe+JDmnnsV0641KQqqDG8AQ16Z/x7GVWO+AWHMpRlHWVXOq8U1yhPMA4VXxl7i+D+C6ql0VGFp0GA== + dependencies: + "@solana/codecs-core" "2.0.0-experimental.8618508" + "@solana/codecs-numbers" "2.0.0-experimental.8618508" + +"@solana/options@2.0.0-experimental.8618508": + version "2.0.0-experimental.8618508" + resolved "https://registry.yarnpkg.com/@solana/options/-/options-2.0.0-experimental.8618508.tgz#95385340e85f9e8a81b2bfba089404a61c8e9520" + integrity sha512-fy/nIRAMC3QHvnKi63KEd86Xr/zFBVxNW4nEpVEU2OT0gCEKwHY4Z55YHf7XujhyuM3PNpiBKg/YYw5QlRU4vg== + dependencies: + "@solana/codecs-core" "2.0.0-experimental.8618508" + "@solana/codecs-numbers" "2.0.0-experimental.8618508" + +"@solana/spl-token-metadata@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@solana/spl-token-metadata/-/spl-token-metadata-0.1.2.tgz#876e13432bd2960bd3cac16b9b0af63e69e37719" + integrity sha512-hJYnAJNkDrtkE2Q41YZhCpeOGU/0JgRFXbtrtOuGGeKc3pkEUHB9DDoxZAxx+XRno13GozUleyBi0qypz4c3bw== + dependencies: + "@solana/codecs-core" "2.0.0-experimental.8618508" + "@solana/codecs-data-structures" "2.0.0-experimental.8618508" + "@solana/codecs-numbers" "2.0.0-experimental.8618508" + "@solana/codecs-strings" "2.0.0-experimental.8618508" + "@solana/options" "2.0.0-experimental.8618508" + "@solana/spl-type-length-value" "0.1.0" + +"@solana/spl-token@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.2.0.tgz#329bb6babb5de0f9c40035ddb1657f01a8347acd" + integrity sha512-RWcn31OXtdqIxmkzQfB2R+WpsJOVS6rKuvpxJFjvik2LyODd+WN58ZP3Rpjpro03fscGAkzlFuP3r42doRJgyQ== + dependencies: + "@solana/buffer-layout" "^4.0.0" + "@solana/buffer-layout-utils" "^0.2.0" + "@solana/web3.js" "^1.32.0" + start-server-and-test "^1.14.0" + +"@solana/spl-token@^0.3.5", "@solana/spl-token@^0.3.6": + version "0.3.11" + resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.11.tgz#cdc10f9472b29b39c8983c92592cadd06627fb9a" + integrity sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ== + dependencies: + "@solana/buffer-layout" "^4.0.0" + "@solana/buffer-layout-utils" "^0.2.0" + "@solana/spl-token-metadata" "^0.1.2" + buffer "^6.0.3" + +"@solana/spl-type-length-value@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@solana/spl-type-length-value/-/spl-type-length-value-0.1.0.tgz#b5930cf6c6d8f50c7ff2a70463728a4637a2f26b" + integrity sha512-JBMGB0oR4lPttOZ5XiUGyvylwLQjt1CPJa6qQ5oM+MBCndfjz2TKKkw0eATlLLcYmq1jBVsNlJ2cD6ns2GR7lA== + dependencies: + buffer "^6.0.3" + +"@solana/wallet-adapter-base@^0.9.2": + version "0.9.23" + resolved "https://registry.yarnpkg.com/@solana/wallet-adapter-base/-/wallet-adapter-base-0.9.23.tgz#3b17c28afd44e173f44f658bf9700fd637e12a11" + integrity sha512-apqMuYwFp1jFi55NxDfvXUX2x1T0Zh07MxhZ/nCCTGys5raSfYUh82zen2BLv8BSDj/JxZ2P/s7jrQZGrX8uAw== + dependencies: + "@solana/wallet-standard-features" "^1.1.0" + "@wallet-standard/base" "^1.0.1" + "@wallet-standard/features" "^1.0.3" + eventemitter3 "^4.0.7" + +"@solana/wallet-standard-features@^1.1.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@solana/wallet-standard-features/-/wallet-standard-features-1.2.0.tgz#be8b3824abf5ebcfeaa7298445bf53f76a27c935" + integrity sha512-tUd9srDLkRpe1BYg7we+c4UhRQkq+XQWswsr/L1xfGmoRDF47BPSXf4zE7ZU2GRBGvxtGt7lwJVAufQyQYhxTQ== + dependencies: + "@wallet-standard/base" "^1.0.1" + "@wallet-standard/features" "^1.0.3" + +"@solana/web3.js@^1.31.0", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.35.1", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.44.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.66.2", "@solana/web3.js@^1.89.1": + version "1.89.1" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.89.1.tgz#52df6820f2d088c4558aa359af40580a03d10ec9" + integrity sha512-t9TTLtPQxtQB3SAf/5E8xPXfVDsC6WGOsgKY02l2cbe0HLymT7ynE8Hu48Lk5qynHCquj6nhISfEHcjMkYpu/A== + dependencies: + "@babel/runtime" "^7.23.4" + "@noble/curves" "^1.2.0" + "@noble/hashes" "^1.3.2" + "@solana/buffer-layout" "^4.0.1" + agentkeepalive "^4.5.0" + bigint-buffer "^1.1.5" + bn.js "^5.2.1" + borsh "^0.7.0" + bs58 "^4.0.1" + buffer "6.0.3" + fast-stable-stringify "^1.0.0" + jayson "^4.1.0" + node-fetch "^2.7.0" + rpc-websockets "^7.5.1" + superstruct "^0.14.2" + +"@solflare-wallet/utl-sdk@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@solflare-wallet/utl-sdk/-/utl-sdk-1.4.0.tgz#0a6d5e66d0566c20e835be7b2a92f6491f10f5be" + integrity sha512-0E3s+fXs5XMGBNrXGB4XSH4+sPgLanbBEVyz227KJyxSIgiRdQMcM2Yv/KdnMHNmhYoR/aPpH6TH115SIJqM0A== + dependencies: + "@metaplex-foundation/js" "0.11.7" + axios "^0.27.2" + eventemitter3 "^5.0.0" + lodash "^4.17.21" + +"@supercharge/promise-pool@^2.1.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@supercharge/promise-pool/-/promise-pool-2.4.0.tgz#6050eea8c2d7f92ddd4ddc582ee328b15c034ad3" + integrity sha512-O9CMipBlq5OObdt1uKJGIzm9cdjpPWfj+a+Zw9EgWKxaMNHKC7EU7X9taj3H0EGQNLOSq2jAcOa3EzxlfHsD6w== + +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + +"@szmarczak/http-timer@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" + integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== + dependencies: + defer-to-connect "^2.0.1" + +"@tronweb3/google-protobuf@^3.21.2": + version "3.21.2" + resolved "https://registry.yarnpkg.com/@tronweb3/google-protobuf/-/google-protobuf-3.21.2.tgz#0964cf83ed7826d31c3cb4e4ecf07655681631c9" + integrity sha512-IVcT2GfWX3K6tHUVhs14NP5uzKhQt4KeDya1g9ACxuZsUzsaoGUIGzceK2Ltu7xp1YV94AaHOf4yxLAivlvEkQ== + +"@types/babel__core@^7.1.14": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.1.tgz#916ecea274b0c776fec721e333e55762d3a9614b" + integrity sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" + integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.1" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" + integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.1.tgz#dd6f1d2411ae677dcb2db008c962598be31d6acf" + integrity sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg== + dependencies: + "@babel/types" "^7.20.7" + +"@types/bn.js@^4.11.3": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" + integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== + dependencies: + "@types/node" "*" + +"@types/cacheable-request@^6.0.1", "@types/cacheable-request@^6.0.2": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" + integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "^3.1.4" + "@types/node" "*" + "@types/responselike" "^1.0.0" + +"@types/connect@^3.4.33": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.3": + version "3.7.4" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" + integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.0.tgz#55818eabb376e2272f77fbf5c96c43137c3c1e53" + integrity sha512-gsF+c/0XOguWgaOgvFs+xnnRqt9GwgTvIks36WpE6ueeI4KCEHHd8K/CKHqhOqrJKsYH8m27kRzQEvWXAwXUTw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" + integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== + +"@types/glob@^7.1.1": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/graceful-fs@^4.1.3": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== + dependencies: + "@types/node" "*" + +"@types/http-cache-semantics@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" + integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^29.2.3": + version "29.5.3" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.3.tgz#7a35dc0044ffb8b56325c6802a4781a626b05777" + integrity sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.12" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" + integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/keyv@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== + dependencies: + "@types/node" "*" + +"@types/lodash.clonedeep@^4.5.7": + version "4.5.7" + resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.7.tgz#0e119f582ed6f9e6b373c04a644651763214f197" + integrity sha512-ccNqkPptFIXrpVqUECi60/DFxjNKsfoQxSQsgcBJCX/fuX1wgyQieojkcWH/KpE3xzLoWN/2k+ZeGqIN3paSvw== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.195" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632" + integrity sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg== + +"@types/minimatch@*": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + +"@types/node@*": + version "20.4.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.4.tgz#c79c7cc22c9d0e97a7944954c9e663bcbd92b0cb" + integrity sha512-CukZhumInROvLq3+b5gLev+vgpsIqC2D0deQr/yS1WnxvmYLlJXZpaQrQiseMY+6xusl79E04UjWoqyr+t1/Ew== + +"@types/node@11.11.6": + version "11.11.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a" + integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ== + +"@types/node@18.15.13": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== + +"@types/node@^12.12.54", "@types/node@^12.12.6": + version "12.20.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + dependencies: + "@types/node" "*" + +"@types/prettier@^2.1.5": + version "2.7.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" + integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== + +"@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + +"@types/secp256k1@^4.0.1": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c" + integrity sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w== + dependencies: + "@types/node" "*" + +"@types/semver@^7.3.12": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" + integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== + +"@types/stack-utils@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== + +"@types/ws@^7.4.4": + version "7.4.7" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" + integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== + dependencies: + "@types/node" "*" + +"@types/ws@^8.5.3": + version "8.5.5" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" + integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== + dependencies: + "@types/node" "*" + +"@types/yargs-parser@*": + version "21.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" + integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + +"@types/yargs@^17.0.8": + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^5.61.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.36.2": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== + dependencies: + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== + dependencies: + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + +"@uniswap/lib@^4.0.1-alpha": + version "4.0.1-alpha" + resolved "https://registry.yarnpkg.com/@uniswap/lib/-/lib-4.0.1-alpha.tgz#2881008e55f075344675b3bca93f020b028fbd02" + integrity sha512-f6UIliwBbRsgVLxIaBANF6w09tYqc6Y/qXdsrbEmXHyFA7ILiKrIwRFXe1yOg8M3cksgVsO9N7yuL2DdCGQKBA== + +"@uniswap/v2-core@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@uniswap/v2-core/-/v2-core-1.0.1.tgz#af8f508bf183204779938969e2e54043e147d425" + integrity sha512-MtybtkUPSyysqLY2U210NBDeCHX+ltHt3oADGdjqoThZaFRDKwM6k1Nb3F0A3hk5hwuQvytFWhrWHOEq6nVJ8Q== + +"@uniswap/v3-core@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.0.tgz#6c24adacc4c25dceee0ba3ca142b35adbd7e359d" + integrity sha512-kSC4djMGKMHj7sLMYVnn61k9nu+lHjMIxgg9CDQT+s2QYLoA56GbSK9Oxr+qJXzzygbkrmuY6cwgP6cW2JXPFA== + +"@uniswap/v3-periphery@^1.0.1": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@uniswap/v3-periphery/-/v3-periphery-1.4.3.tgz#a6da4632dbd46b139cc13a410e4ec09ad22bd19f" + integrity sha512-80c+wtVzl5JJT8UQskxVYYG3oZb4pkhY0zDe0ab/RX4+8f9+W5d8wI4BT0wLB0wFQTSnbW+QdBSpkHA/vRyGBA== + dependencies: + "@openzeppelin/contracts" "3.4.2-solc-0.7" + "@uniswap/lib" "^4.0.1-alpha" + "@uniswap/v2-core" "1.0.1" + "@uniswap/v3-core" "1.0.0" + base64-sol "1.0.1" + +"@uniswap/v3-staker@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@uniswap/v3-staker/-/v3-staker-1.0.0.tgz#9a6915ec980852479dfc903f50baf822ff8fa66e" + integrity sha512-JV0Qc46Px5alvg6YWd+UIaGH9lDuYG/Js7ngxPit1SPaIP30AlVer1UYB7BRYeUVVxE+byUyIeN5jeQ7LLDjIw== + dependencies: + "@openzeppelin/contracts" "3.4.1-solc-0.7-2" + "@uniswap/v3-core" "1.0.0" + "@uniswap/v3-periphery" "^1.0.1" + +"@viaprotocol/router-sdk@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@viaprotocol/router-sdk/-/router-sdk-1.0.7.tgz#d480b995db3156184f4d2faadcdaa94639904afa" + integrity sha512-eVzvmIFg1zC6Sf8wKLRfIYw0PYvCg0aLS23mgebl9MgaWtuuNKOb6qvo3qRMyaHa8GTYXJzf/evIWaPpnX3hrg== + dependencies: + "@types/ws" "^8.5.3" + axios "^0.26.1" + isomorphic-ws "^5.0.0" + ws "^8.8.0" + +"@wagmi/chains@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@wagmi/chains/-/chains-1.6.0.tgz#eb992ad28dbaaab729b5bcab3e5b461e8a035656" + integrity sha512-5FRlVxse5P4ZaHG3GTvxwVANSmYJas1eQrTBHhjxVtqXoorm0aLmCHbhmN8Xo1yu09PaWKlleEvfE98yH4AgIw== + +"@wallet-standard/base@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@wallet-standard/base/-/base-1.0.1.tgz#860dd94d47c9e3c5c43b79d91c6afdbd7a36264e" + integrity sha512-1To3ekMfzhYxe0Yhkpri+Fedq0SYcfrOfJi3vbLjMwF2qiKPjTGLwZkf2C9ftdQmxES+hmxhBzTwF4KgcOwf8w== + +"@wallet-standard/features@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@wallet-standard/features/-/features-1.0.3.tgz#c992876c5e4f7a0672f8869c4146c87e0dfe48c8" + integrity sha512-m8475I6W5LTatTZuUz5JJNK42wFRgkJTB0I9tkruMwfqBF2UN2eomkYNVf9RbrsROelCRzSFmugqjKZBFaubsA== + dependencies: + "@wallet-standard/base" "^1.0.1" + +"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" + integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" + integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" + integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" + integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-opt" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/wast-printer" "1.11.6" + +"@webassemblyjs/wasm-gen@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" + integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" + integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + +"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" + integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" + integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" + integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== + +"@webpack-cli/info@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" + integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== + dependencies: + envinfo "^7.7.3" + +"@webpack-cli/serve@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" + integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +Base64@~0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/Base64/-/Base64-0.2.1.tgz#ba3a4230708e186705065e66babdd4c35cf60028" + integrity sha512-reGEWshDmTDQDsCec/HduOO9Wyj6yMOupMfhIf3ugN1TDlK2NQW4DDJSqNNtp380SNcvRfXtO8HSCQot0d0SMw== + +JSONStream@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +abitype@0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.9.3.tgz#294d25288ee683d72baf4e1fed757034e3c8c277" + integrity sha512-dz4qCQLurx97FQhnb/EIYTk/ldQ+oafEDUqC0VVIeQS1Q48/YWt/9YNfMmp9SLFqN41ktxny3c8aYxHjmFIB/w== + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +abortcontroller-polyfill@^1.7.3: + version "1.7.5" + resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed" + integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ== + +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^8.0.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== + +aes-js@4.0.0-beta.5: + version "4.0.0-beta.5" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873" + integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q== + +aes-js@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" + integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== + +agentkeepalive@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" + integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== + dependencies: + humanize-ms "^1.2.1" + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.0, ajv@^8.9.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +algo-msgpack-with-bigint@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/algo-msgpack-with-bigint/-/algo-msgpack-with-bigint-2.1.1.tgz#38bb717220525b3ff42232eefdcd9efb9ad405d6" + integrity sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ== + +algosdk@^1.13.1: + version "1.24.1" + resolved "https://registry.yarnpkg.com/algosdk/-/algosdk-1.24.1.tgz#afc4102457ae0c38a32de6b84f4d713aedfc9e89" + integrity sha512-9moZxdqeJ6GdE4N6fA/GlUP4LrbLZMYcYkt141J4Ss68OfEgH9qW0wBuZ3ZOKEx/xjc5bg7mLP2Gjg7nwrkmww== + dependencies: + algo-msgpack-with-bigint "^2.1.1" + buffer "^6.0.2" + cross-fetch "^3.1.5" + hi-base32 "^0.5.1" + js-sha256 "^0.9.0" + js-sha3 "^0.8.0" + js-sha512 "^0.8.0" + json-bigint "^1.0.0" + tweetnacl "^1.0.3" + vlq "^2.0.4" + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-sequence-parser@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz#4d790f31236ac20366b23b3916b789e1bde39aed" + integrity sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +ansicolors@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" + integrity sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg== + +anymatch@^3.0.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arbundles@^0.6.20: + version "0.6.23" + resolved "https://registry.yarnpkg.com/arbundles/-/arbundles-0.6.23.tgz#c00cda953df67fa65d4297486237cc8e0c072c47" + integrity sha512-+gr93F3fivN+6dhiImT6BQNaXz4oECPn2GYjCZjS2yEoq7hM78FRvVp6kQyjEdhnuBFQr/q4oS/nkjnQlHdj9Q== + dependencies: + "@noble/ed25519" "^1.6.1" + "@randlabs/myalgo-connect" "^1.1.2" + "@solana/wallet-adapter-base" "^0.9.2" + algosdk "^1.13.1" + arweave "^1.11.4" + arweave-stream-tx "^1.1.0" + avsc "https://github.com/Irys-xyz/avsc#csp-fixes" + axios "^0.21.3" + base64url "^3.0.1" + bs58 "^4.0.1" + ethers "^5.5.1" + keccak "^3.0.2" + multistream "^4.1.0" + process "^0.11.10" + secp256k1 "^4.0.2" + tmp-promise "^3.0.2" + +arconnect@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/arconnect/-/arconnect-0.4.2.tgz#83de7638fb46183e82d7ec7efb5594c5f7cdc806" + integrity sha512-Jkpd4QL3TVqnd3U683gzXmZUVqBUy17DdJDuL/3D9rkysLgX6ymJ2e+sR+xyZF5Rh42CBqDXWNMmCjBXeP7Gbw== + dependencies: + arweave "^1.10.13" + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== + +array-back@^3.0.1, array-back@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" + integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== + +array-back@^4.0.1, array-back@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" + integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== + +array-back@^6.2.0, array-back@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-6.2.2.tgz#f567d99e9af88a6d3d2f9dfcc21db6f9ba9fd157" + integrity sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw== + +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-includes@^3.1.6: + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + is-string "^1.0.7" + +array-union@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== + dependencies: + array-uniq "^1.0.1" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== + +array.prototype.flat@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +arraybuffer.prototype.slice@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz#9b5ea3868a6eebc30273da577eb888381c0044bb" + integrity sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + +arweave-stream-tx@^1.1.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/arweave-stream-tx/-/arweave-stream-tx-1.2.2.tgz#2d5c66554301baacd02586a152fbb198b422112f" + integrity sha512-bNt9rj0hbAEzoUZEF2s6WJbIz8nasZlZpxIw03Xm8fzb9gRiiZlZGW3lxQLjfc9Z0VRUWDzwtqoYeEoB/JDToQ== + dependencies: + exponential-backoff "^3.1.0" + +arweave@^1.10.13, arweave@^1.11.4: + version "1.14.4" + resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.14.4.tgz#5ba22136aa0e7fd9495258a3931fb770c9d6bf21" + integrity sha512-tmqU9fug8XAmFETYwgUhLaD3WKav5DaM4p1vgJpEj/Px2ORPPMikwnSySlFymmL2qgRh2ZBcZsg11+RXPPGLsA== + dependencies: + arconnect "^0.4.2" + asn1.js "^5.4.1" + base64-js "^1.5.1" + bignumber.js "^9.0.2" + +asn1.js@^5.2.0, asn1.js@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== + +assert@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32" + integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== + dependencies: + es6-object-assign "^1.1.0" + is-nan "^1.2.1" + object-is "^1.0.1" + util "^0.12.0" + +assert@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd" + integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw== + dependencies: + call-bind "^1.0.2" + is-nan "^1.3.2" + object-is "^1.1.5" + object.assign "^4.1.4" + util "^0.12.5" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async-retry@^1.3.1, async-retry@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280" + integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== + dependencies: + retry "0.13.1" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +"avsc@https://github.com/Irys-xyz/avsc#csp-fixes": + version "5.4.7" + resolved "https://github.com/Irys-xyz/avsc#a730cc8018b79e114b6a3381bbb57760a24c6cef" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== + +aws4@^1.8.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" + integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== + +axios@^0.21.3: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + +axios@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" + integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g== + dependencies: + follow-redirects "^1.14.7" + +axios@^0.26.1: + version "0.26.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" + integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== + dependencies: + follow-redirects "^1.14.8" + +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + +axios@^1.6.2: + version "1.6.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" + integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== + dependencies: + follow-redirects "^1.15.4" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +babel-jest@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.6.1.tgz#a7141ad1ed5ec50238f3cd36127636823111233a" + integrity sha512-qu+3bdPEQC6KZSPz+4Fyjbga5OODNcp49j6GKzG1EKbkfyJBxEYGVUmVGpwCSeGouG52R4EgYMLb6p9YeEEQ4A== + dependencies: + "@jest/transform" "^29.6.1" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.5.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" + integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== + dependencies: + babel-plugin-jest-hoist "^29.5.0" + babel-preset-current-node-syntax "^1.0.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-x@^3.0.2, base-x@^3.0.8: + version "3.0.9" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" + integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + dependencies: + safe-buffer "^5.0.1" + +base-x@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a" + integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw== + +base58-js@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/base58-js/-/base58-js-1.0.5.tgz#00697bff954aa85007fa45ce76b699b7960768cd" + integrity sha512-LkkAPP8Zu+c0SVNRTRVDyMfKVORThX+rCViget00xdgLRrKkClCTz1T7cIrpr69ShwV5XJuuoZvMvJ43yURwkA== + +base64-js@^1.3.1, base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +base64-sol@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/base64-sol/-/base64-sol-1.0.1.tgz#91317aa341f0bc763811783c5729f1c2574600f6" + integrity sha512-ld3cCNMeXt4uJXmLZBHFGMvVpK9KsLVEhPpFRXnvSVAqABKbuNZg/+dsq3NuM+wxFLb/UrVkz7m1ciWmkMfTbg== + +base64url@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + +bech32@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + +bech32@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" + integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +bigint-buffer@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" + integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA== + dependencies: + bindings "^1.3.0" + +bignumber.js@9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.0.tgz#8d340146107fe3a6cb8d40699643c302e8773b62" + integrity sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A== + +bignumber.js@^9.0.0, bignumber.js@^9.0.1, bignumber.js@^9.0.2: + version "9.1.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" + integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== + +bindings@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bip39-light@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/bip39-light/-/bip39-light-1.0.7.tgz#06a72f251b89389a136d3f177f29b03342adc5ba" + integrity sha512-WDTmLRQUsiioBdTs9BmSEmkJza+8xfJmptsNJjxnoq3EydSa/ZBXT6rm66KoT3PJIRYMnhSKNR7S9YL1l7R40Q== + dependencies: + create-hash "^1.1.0" + pbkdf2 "^3.0.9" + +bip39@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.2.tgz#2baf42ff3071fc9ddd5103de92e8f80d9257ee32" + integrity sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ== + dependencies: + "@types/node" "11.11.6" + create-hash "^1.1.0" + pbkdf2 "^3.0.9" + randombytes "^2.0.1" + +bip39@^3.0.4: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.1.0.tgz#c55a418deaf48826a6ceb34ac55b3ee1577e18a3" + integrity sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A== + dependencies: + "@noble/hashes" "^1.2.0" + +bitcoin-address-validation@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/bitcoin-address-validation/-/bitcoin-address-validation-2.2.1.tgz#049b5bc54687e4a28a5b7f599968986c5a9f98cb" + integrity sha512-f6LXNpvRKlTbHWb37N9tHoAbYGbshzM8FPWvCtloh++hxZ0/dmkokvKNVLz6HkG82zVwo8w6Sq4JmfO2timzyg== + dependencies: + base58-js "^1.0.0" + bech32 "^2.0.0" + sha256-uint8array "^0.10.3" + +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +blakejs@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" + integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== + +bluebird@3.7.2, bluebird@^3.5.0: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA== + +bn.js@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +body-parser@^1.16.0: + version "1.20.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +borsh@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.6.0.tgz#a7c9eeca6a31ca9e0607cb49f329cb659eb791e1" + integrity sha512-sl5k89ViqsThXQpYa9XDtz1sBl3l1lI313cFUY1HKr+wvMILnb+58xpkqTNrYbelh99dY7K8usxoCusQmqix9Q== + dependencies: + bn.js "^5.2.0" + bs58 "^4.0.0" + text-encoding-utf-8 "^1.0.2" + +borsh@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a" + integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA== + dependencies: + bn.js "^5.2.0" + bs58 "^4.0.0" + text-encoding-utf-8 "^1.0.2" + +bowser@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" + integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserslist@^4.14.5, browserslist@^4.21.9: + version "4.21.9" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" + integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== + dependencies: + caniuse-lite "^1.0.30001503" + electron-to-chromium "^1.4.431" + node-releases "^2.0.12" + update-browserslist-db "^1.0.11" + +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + +bs58@^4.0.0, bs58@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + +bs58@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279" + integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ== + dependencies: + base-x "^4.0.0" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + integrity sha512-3dthu5CYiVB1DEJp61FtApNnNndTckcqe4pFcLdvHtrpG+kcyekCJKg4MRiDcFW7A6AODnXB9U4dwQiCW5kzJQ== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer@6.0.3, buffer@^6.0.2, buffer@^6.0.3, buffer@~6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bufferutil@^4.0.1: + version "4.0.7" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad" + integrity sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw== + dependencies: + node-gyp-build "^4.3.0" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-lookup@^6.0.4: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz#0330a543471c61faa4e9035db583aad753b36385" + integrity sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww== + +cacheable-request@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" + integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +call-me-maybe@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz#03f964f19522ba643b1b0693acb9152fe2074baa" + integrity sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ== + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +cancelable-promise@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/cancelable-promise/-/cancelable-promise-4.3.1.tgz#957b62382435e74dfb2a038517f53205e0c631c6" + integrity sha512-A/8PwLk/T7IJDfUdQ68NR24QHa8rIlnN/stiJEBo6dmVUkD4K14LswG0w3VwdeK/o7qOwRUR1k2MhK5Rpy2m7A== + +caniuse-lite@^1.0.30001503: + version "1.0.30001517" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz#90fabae294215c3495807eb24fc809e11dc2f0a8" + integrity sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA== + +capability@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/capability/-/capability-0.2.5.tgz#51ad87353f1936ffd77f2f21c74633a4dea88801" + integrity sha512-rsJZYVCgXd08sPqwmaIqjAd5SUTfonV0z/gDJ8D6cN8wQphky1kkAYEqQ+hmDxTw7UihvBfjUVUSY+DBEe44jg== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + +cbridge-revert-manager@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/cbridge-revert-manager/-/cbridge-revert-manager-1.1.0.tgz#ad4790b6b446e25199f98cb9f31ff00404b1f44e" + integrity sha512-MIj9ldDfm3d/i5emjf4ajxK6OSbjydu2ONouyLf2ZG4UdhXbHyC2Zqz0cAwcnoCLQDAu8ZTjEnRw97mtUtslUg== + dependencies: + ethers "^5.7.2" + google-protobuf "^3.21.2" + +chalk@^2.0.0, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +check-more-types@2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== + +chownr@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +ci-info@^3.2.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + +cids@^0.7.1: + version "0.7.5" + resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" + integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== + dependencies: + buffer "^5.5.0" + class-is "^1.1.0" + multibase "~0.6.0" + multicodec "^1.0.0" + multihashes "~0.4.15" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +cjs-module-lexer@^1.0.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + +class-is@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" + integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +collect-v8-coverage@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +command-line-args@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" + integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== + dependencies: + array-back "^3.1.0" + find-replace "^3.0.0" + lodash.camelcase "^4.3.0" + typical "^4.0.0" + +command-line-usage@^6.1.1: + version "6.1.3" + resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.3.tgz#428fa5acde6a838779dfa30e44686f4b6761d957" + integrity sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw== + dependencies: + array-back "^4.0.2" + chalk "^2.4.2" + table-layout "^1.0.2" + typical "^5.2.0" + +commander@^2.20.0, commander@^2.20.3: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^7.0.0, commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^8.2.0, commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +compression-webpack-plugin@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-9.2.0.tgz#57fd539d17c5907eebdeb4e83dcfe2d7eceb9ef6" + integrity sha512-R/Oi+2+UHotGfu72fJiRoVpuRifZT0tTC6UqFD/DUo+mv8dbOow9rVOuTvDv5nPPm3GZhHL/fKkwxwIHnJ8Nyw== + dependencies: + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +confusing-browser-globals@^1.0.10: + version "1.0.11" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" + integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-hash@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" + integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== + dependencies: + cids "^0.7.1" + multicodec "^0.5.5" + multihashes "^0.4.15" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== + +cors@^2.8.1: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +crc32@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/crc32/-/crc32-0.2.2.tgz#7ad220d6ffdcd119f9fc127a7772cacea390a4ba" + integrity sha512-PFZEGbDUeoNbL2GHIEpJRQGheXReDody/9axKTxhXtQqIL443wnNigtVZO9iuCIMPApKZRv7k2xr8euXHqNxQQ== + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@1.1.7, create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +create-ts-index@^1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/create-ts-index/-/create-ts-index-1.14.0.tgz#afb32548a420b0ffebb111d0f0329a485d7e9ad6" + integrity sha512-3VuFF5HeZvkbk9qa8IpSAETYLFxI1n0yiymY2ctr2txJgzYxJP68rw6p9nQ68KlGQLuUBUFfCcBGO/Dk5FdfUQ== + dependencies: + chalk "^4.1.2" + dayjs "^1.10.7" + debug "^4.3.3" + deepmerge "^4.2.2" + fast-glob "^3.2.7" + fp-ts "^2.11.5" + glob "^7.2.0" + json5 "^2.2.0" + merge "^2.1.1" + minimatch "^3.0.4" + my-easy-fp "^0.9.0" + tslib "^2.3.1" + yargs "^17.3.0" + +cross-fetch@^3.1.4, cross-fetch@^3.1.5: + version "3.1.8" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" + integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== + dependencies: + node-fetch "^2.6.12" + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-browserify@^3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +csv-generate@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/csv-generate/-/csv-generate-4.3.1.tgz#3ff7c481e07c7dc70fb2a95d50c6a7092a836a80" + integrity sha512-7YeeJq+44/I/O5N2sr2qBMcHZXhpfe38eh7DOFxyMtYO+Pir7kIfgFkW5MPksqKqqR6+/wX7UGoZm1Ot11151w== + +csv-parse@^5.5.3: + version "5.5.3" + resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-5.5.3.tgz#0261050761ee46cd0e46421854bf9bf4de1317bf" + integrity sha512-v0KW6C0qlZzoGjk6u5tLmVfyZxNgPGXZsWTXshpAgKVGmGXzaVWGdlCFxNx5iuzcXT/oJN1HHM9DZKwtAtYa+A== + +csv-stringify@^6.4.5: + version "6.4.5" + resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-6.4.5.tgz#99808b40ec30b86cc0e1c7d707c1d6ecbc4e1a00" + integrity sha512-SPu1Vnh8U5EnzpNOi1NDBL5jU5Rx7DVHr15DNg9LXDTAbQlAVAmEbVt16wZvEW9Fu9Qt4Ji8kmeCJ2B1+4rFTQ== + +csv@^6.0.5: + version "6.3.6" + resolved "https://registry.yarnpkg.com/csv/-/csv-6.3.6.tgz#f29e171b7e557a1c77c32c9d5ac82d14f659d1da" + integrity sha512-jsEsX2HhGp7xiwrJu5srQavKsh+HUJcCi78Ar3m4jlmFKRoTkkMy7ZZPP+LnQChmaztW+uj44oyfMb59daAs/Q== + dependencies: + csv-generate "^4.3.1" + csv-parse "^5.5.3" + csv-stringify "^6.4.5" + stream-transform "^3.3.0" + +current-module-paths@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/current-module-paths/-/current-module-paths-1.1.1.tgz#f0ee3298cd1a6839503b46796aac9e19add3f9e6" + integrity sha512-8Ga5T8oMXBaSsHq9Gj+bddX7kHSaJKsl2vaAd3ep51eQLkr4W18eFEmEZM5bLo1zrz8tt3jE1U8QK9QGhaLR4g== + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== + dependencies: + assert-plus "^1.0.0" + +dayjs@^1.10.7: + version "1.11.9" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a" + integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA== + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +decimal.js-light@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" + integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== + +decode-uri-component@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== + dependencies: + mimic-response "^1.0.0" + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== + +deep-extend@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +defaults@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== + dependencies: + clone "^1.0.2" + +defer-to-connect@^2.0.0, defer-to-connect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delay@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" + integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +depd@2.0.0, depd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +des.js@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" + integrity sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +duplexer@^0.1.2, duplexer@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.431: + version "1.4.468" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.468.tgz#3cbf64ad67d9f12bfe69fefe5eb1935ec4f6ab7a" + integrity sha512-6M1qyhaJOt7rQtNti1lBA0GwclPH+oKCmsra/hkcWs5INLxfXXD/dtdnaKUYQu/pjOBP/8Osoe4mAcNvvzoFag== + +elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0, enhanced-resolve@^5.7.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +envinfo@^7.7.3: + version "7.10.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.10.0.tgz#55146e3909cc5fe63c22da63fb15b05aeac35b13" + integrity sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +error-polyfill@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/error-polyfill/-/error-polyfill-0.1.3.tgz#df848b61ad8834f7a5db69a70b9913df86721d15" + integrity sha512-XHJk60ufE+TG/ydwp4lilOog549iiQF2OAPhkk9DdiYWMrltz5yhDz/xnKuenNwP7gy3dsibssO5QpVhkrSzzg== + dependencies: + capability "^0.2.5" + o3 "^1.0.3" + u3 "^0.1.1" + +es-abstract@^1.19.0, es-abstract@^1.20.4: + version "1.22.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" + integrity sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.1" + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.2.1" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.10" + is-weakref "^1.0.2" + object-inspect "^1.12.3" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.0" + safe-array-concat "^1.0.0" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.7" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.10" + +es-module-lexer@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.0.tgz#6be9c9e0b4543a60cd166ff6f8b4e9dae0b0c16f" + integrity sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA== + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.62" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" + integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + next-tick "^1.1.0" + +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-object-assign@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" + integrity sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw== + +es6-promise@^4.0.3, es6-promise@^4.2.8: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ== + dependencies: + es6-promise "^4.0.3" + +es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-airbnb-base@^15.0.0: + version "15.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz#6b09add90ac79c2f8d723a2580e07f3925afd236" + integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig== + dependencies: + confusing-browser-globals "^1.0.10" + object.assign "^4.1.2" + object.entries "^1.1.5" + semver "^6.3.0" + +eslint-config-airbnb-typescript@^17.0.0: + version "17.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.1.0.tgz#fda960eee4a510f092a9a1c139035ac588937ddc" + integrity sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig== + dependencies: + eslint-config-airbnb-base "^15.0.0" + +eslint-config-prettier@^8.3.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" + integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== + +eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== + dependencies: + debug "^3.2.7" + is-core-module "^2.11.0" + resolve "^1.22.1" + +eslint-module-utils@^2.7.4: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== + dependencies: + debug "^3.2.7" + +eslint-plugin-import@^2.22.1: + version "2.27.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" + integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" + has "^1.0.3" + is-core-module "^2.11.0" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" + tsconfig-paths "^3.14.1" + +eslint-plugin-prettier@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz#e9ddb200efb6f3d05ffe83b1665a716af4a387e5" + integrity sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-plugin-simple-import-sort@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-8.0.0.tgz#9d9a2372b0606e999ea841b10458a370a6ccc160" + integrity sha512-bXgJQ+lqhtQBCuWY/FUWdB27j4+lqcvXv5rUARkzbeWLwea+S5eBZEQrhnO+WgX3ZoJHVj0cn943iyXwByHHQw== + +eslint-plugin-unused-imports@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-1.1.5.tgz#a2b992ef0faf6c6c75c3815cc47bde76739513c2" + integrity sha512-TeV8l8zkLQrq9LBeYFCQmYVIXMjfHgdRQLw7dEZp4ZB3PeR10Y5Uif11heCsHRmhdRIYMoewr1d9ouUHLbLHew== + dependencies: + eslint-rule-composer "^0.3.0" + +eslint-rule-composer@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9" + integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg== + +eslint-scope@5.1.1, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.1.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.1.tgz#936821d3462675f25a18ac5fd88a67cc15b393bd" + integrity sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" + integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== + +eslint@8.22.0: + version "8.22.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.22.0.tgz#78fcb044196dfa7eef30a9d65944f6f980402c48" + integrity sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA== + dependencies: + "@eslint/eslintrc" "^1.3.0" + "@humanwhocodes/config-array" "^0.10.4" + "@humanwhocodes/gitignore-to-minimatch" "^1.0.2" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.3" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + functional-red-black-tree "^1.0.1" + glob-parent "^6.0.1" + globals "^13.15.0" + globby "^11.1.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^9.3.3, espree@^9.4.0: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eth-ens-namehash@2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + integrity sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw== + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + +eth-lib@0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" + integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.1.26: + version "0.1.29" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" + integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +ethereum-bloom-filters@^1.0.6: + version "1.0.10" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz#3ca07f4aed698e75bd134584850260246a5fed8a" + integrity sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA== + dependencies: + js-sha3 "^0.8.0" + +ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-cryptography@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz#18fa7108622e56481157a5cb7c01c0c6a672eb67" + integrity sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug== + dependencies: + "@noble/curves" "1.1.0" + "@noble/hashes" "1.3.1" + "@scure/bip32" "1.3.1" + "@scure/bip39" "1.2.1" + +ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.2, ethereumjs-util@^7.1.5: + version "7.1.5" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" + integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + +ethereumjs-wallet@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-1.0.2.tgz#2c000504b4c71e8f3782dabe1113d192522e99b6" + integrity sha512-CCWV4RESJgRdHIvFciVQFnCHfqyhXWchTPlkfp28Qc53ufs+doi5I/cV2+xeK9+qEo25XCWfP9MiL+WEPAZfdA== + dependencies: + aes-js "^3.1.2" + bs58check "^2.1.2" + ethereum-cryptography "^0.1.3" + ethereumjs-util "^7.1.2" + randombytes "^2.1.0" + scrypt-js "^3.0.1" + utf8 "^3.0.0" + uuid "^8.3.2" + +ethers@^5.1.0, ethers@^5.5.1, ethers@^5.6.8, ethers@^5.7.2: + version "5.7.2" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" + integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== + dependencies: + "@ethersproject/abi" "5.7.0" + "@ethersproject/abstract-provider" "5.7.0" + "@ethersproject/abstract-signer" "5.7.0" + "@ethersproject/address" "5.7.0" + "@ethersproject/base64" "5.7.0" + "@ethersproject/basex" "5.7.0" + "@ethersproject/bignumber" "5.7.0" + "@ethersproject/bytes" "5.7.0" + "@ethersproject/constants" "5.7.0" + "@ethersproject/contracts" "5.7.0" + "@ethersproject/hash" "5.7.0" + "@ethersproject/hdnode" "5.7.0" + "@ethersproject/json-wallets" "5.7.0" + "@ethersproject/keccak256" "5.7.0" + "@ethersproject/logger" "5.7.0" + "@ethersproject/networks" "5.7.1" + "@ethersproject/pbkdf2" "5.7.0" + "@ethersproject/properties" "5.7.0" + "@ethersproject/providers" "5.7.2" + "@ethersproject/random" "5.7.0" + "@ethersproject/rlp" "5.7.0" + "@ethersproject/sha2" "5.7.0" + "@ethersproject/signing-key" "5.7.0" + "@ethersproject/solidity" "5.7.0" + "@ethersproject/strings" "5.7.0" + "@ethersproject/transactions" "5.7.0" + "@ethersproject/units" "5.7.0" + "@ethersproject/wallet" "5.7.0" + "@ethersproject/web" "5.7.1" + "@ethersproject/wordlists" "5.7.0" + +ethers@^6.6.0: + version "6.9.1" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.9.1.tgz#4d50c77b46b6661e00f5cc6292e6bcd933fe4cba" + integrity sha512-kuV8fGd4/8Gj7wkurbsuUsm1DCG6N5gKGYdw3fnWG/7QGknhy1xtHD7kbkCWQAcbAYmzLCLqCPedS3FYncFkKQ== + dependencies: + "@adraffy/ens-normalize" "1.10.0" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@types/node" "18.15.13" + aes-js "4.0.0-beta.5" + tslib "2.4.0" + ws "8.5.0" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw== + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +ethjs-util@0.1.6, ethjs-util@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g== + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +eventemitter3@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" + integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== + +eventemitter3@^3.1.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== + +eventemitter3@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +eventemitter3@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@5.1.1, execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^29.0.0, expect@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.1.tgz#64dd1c8f75e2c0b209418f2b8d36a07921adfdf1" + integrity sha512-XEdDLonERCU1n9uR56/Stx9OqojaLAQtZf9PrCHH9Hl8YXiEIka3H4NXJ3NOIBmQJTg7+j7buh34PMHfJujc8g== + dependencies: + "@jest/expect-utils" "^29.6.1" + "@types/node" "*" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + +exponential-backoff@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" + integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== + +express@^4.14.0: + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.1.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extract-files@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a" + integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + +eyes@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" + integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2, fast-diff@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-glob@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-glob@^3.2.7, fast-glob@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fast-stable-stringify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313" + integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag== + +fast-xml-parser@4.2.5: + version "4.2.5" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz#a6747a09296a6cb34f2ae634019bf1738f3b421f" + integrity sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g== + dependencies: + strnum "^1.0.5" + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-set@^5.1.2: + version "5.1.3" + resolved "https://registry.yarnpkg.com/file-set/-/file-set-5.1.3.tgz#44dde6a8ae52d69813ee22beccd7cfe28bc655d2" + integrity sha512-mQ6dqz+z59on3B50IGF3ujNGbZmY1TAeLHpNfhLEeNM6Lky31w3RUlbCyqZWQs0DuZJQU4R2qDuVd9ojyzadcg== + dependencies: + array-back "^6.2.2" + glob "^7.2.0" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-replace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" + integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== + dependencies: + array-back "^3.0.1" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + +follow-redirects@^1.14.0, follow-redirects@^1.14.7, follow-redirects@^1.15.4: + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== + +follow-redirects@^1.14.8, follow-redirects@^1.14.9: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== + +form-data-encoder@1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.1.tgz#ac80660e4f87ee0d3d3c3638b7da8278ddb8ec96" + integrity sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg== + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fp-ts@^2.11.5: + version "2.16.0" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.16.0.tgz#64e03314dfc1c7ce5e975d3496ac14bc3eb7f92e" + integrity sha512-bLq+KgbiXdTEoT1zcARrWEpa5z6A/8b7PcDW7Gef3NSisQ+VS7ll2Xbf1E+xsgik0rWub/8u0qP/iTTjj+PhxQ== + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== + +functions-have-names@^1.2.2, functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0, get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== + dependencies: + assert-plus "^1.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA== + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig== + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.1.3, glob@^7.1.4, glob@^7.2.0: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-dirs@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" + integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== + dependencies: + ini "2.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +global@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.15.0, globals@^13.19.0: + version "13.20.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globby@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" + integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^1.0.2" + dir-glob "^2.2.2" + fast-glob "^2.2.6" + glob "^7.1.3" + ignore "^4.0.3" + pify "^4.0.1" + slash "^2.0.0" + +google-protobuf@^3.21.2: + version "3.21.2" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.21.2.tgz#4580a2bea8bbb291ee579d1fefb14d6fa3070ea4" + integrity sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA== + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +got@12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-12.1.0.tgz#099f3815305c682be4fd6b0ee0726d8e4c6b0af4" + integrity sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig== + dependencies: + "@sindresorhus/is" "^4.6.0" + "@szmarczak/http-timer" "^5.0.1" + "@types/cacheable-request" "^6.0.2" + "@types/responselike" "^1.0.0" + cacheable-lookup "^6.0.4" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + form-data-encoder "1.7.1" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^2.0.0" + +got@^11.8.5: + version "11.8.6" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" + integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +graphql-request@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-5.2.0.tgz#a05fb54a517d91bb2d7aefa17ade4523dc5ebdca" + integrity sha512-pLhKIvnMyBERL0dtFI3medKqWOz/RhHdcgbZ+hMMIb32mEPa5MJSzS4AuXxfI4sRAu6JVVk5tvXuGfCWl9JYWQ== + dependencies: + "@graphql-typed-document-node/core" "^3.1.1" + cross-fetch "^3.1.5" + extract-files "^9.0.0" + form-data "^3.0.0" + +graphql-request@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-6.1.0.tgz#f4eb2107967af3c7a5907eb3131c671eac89be4f" + integrity sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw== + dependencies: + "@graphql-typed-document-node/core" "^3.2.0" + cross-fetch "^3.1.5" + +graphql@^16.6.0: + version "16.7.1" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.7.1.tgz#11475b74a7bff2aefd4691df52a0eca0abd9b642" + integrity sha512-DRYR9tf+UGU0KOsMcKAlXeFfX89UiiIZ0dRU3mR0yJfu6OjZqUcp68NnFLnqQU5RexygFoDy1EW+ccOYcPfmHg== + +grpc-web@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/grpc-web/-/grpc-web-1.4.2.tgz#86995f76471ce6b2119106ec26f909b7b69e7d43" + integrity sha512-gUxWq42l5ldaRplcKb4Pw5O4XBONWZgz3vxIIXnfIeJj8Jc3wYiq2O4c9xzx/NGbbPEej4rhI62C9eTENwLGNw== + +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== + dependencies: + duplexer "^0.1.2" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hi-base32@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/hi-base32/-/hi-base32-0.5.1.tgz#1279f2ddae2673219ea5870c2121d2a33132857e" + integrity sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA== + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-browserify@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/http-browserify/-/http-browserify-1.7.0.tgz#33795ade72df88acfbfd36773cefeda764735b20" + integrity sha512-Irf/LJXmE3cBzU1eaR4+NEX6bmVLqt1wkmDiA7kBwH7zmb0D8kBAXsDmQ88hhj/qv9iEZKlyGx/hrMcFi8sOHw== + dependencies: + Base64 "~0.2.0" + inherits "~2.0.1" + +http-cache-semantics@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@^1.7.2: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.1" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg== + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + +http2-wrapper@^2.1.10: + version "2.2.0" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.0.tgz#b80ad199d216b7d3680195077bd7b9060fa9d7f3" + integrity sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.2.0" + +http@^0.0.1-security: + version "0.0.1-security" + resolved "https://registry.yarnpkg.com/http/-/http-0.0.1-security.tgz#3aac09129d12dc2747bbce4157afde20ad1f7995" + integrity sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g== + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== + dependencies: + ms "^2.0.0" + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +idna-uts46-hx@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" + integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== + dependencies: + punycode "2.1.0" + +ieee754@^1.1.13, ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^4.0.3: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.2.0: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + +ini@^1.3.5: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +injectpromise@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/injectpromise/-/injectpromise-1.0.0.tgz#c621f7df2bbfc1164d714f1fb229adec2079da39" + integrity sha512-qNq5wy4qX4uWHcVFOEU+RqZkoVG65FhvGkyDWbuBxILMjK6A1LFf5A1mgXZkD4nRx5FCorD81X/XvPKp/zVfPA== + +inquirer@^8.2.0: + version "8.2.6" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" + integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.5.5" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + wrap-ansi "^6.0.1" + +internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + dependencies: + get-intrinsic "^1.2.0" + has "^1.0.3" + side-channel "^1.0.4" + +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A== + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.11.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg== + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-function@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw== + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +is-nan@^1.2.1, is-nan@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.10, is-typed-array@^1.1.3, is-typed-array@^1.1.9: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +isarray@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isnumber@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isnumber/-/isnumber-1.0.0.tgz#0e3f9759b581d99dd85086f0ec2a74909cfadd01" + integrity sha512-JLiSz/zsZcGFXPrB4I/AGBvtStkt+8QmksyZBZnVXnnK9XdTEyz0tX8CRYljtwYDuIuZzih6DpHQdi+3Q6zHPw== + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +isomorphic-ws@5.0.0, isomorphic-ws@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" + integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== + +isomorphic-ws@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.5" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +iziswap-sdk@1.4.12: + version "1.4.12" + resolved "https://registry.yarnpkg.com/iziswap-sdk/-/iziswap-sdk-1.4.12.tgz#658425fd588f019d8e5a6f0f4fe466439116299b" + integrity sha512-CkWI5QJstjqSemoShpCTjgi+aaL0SZ6asLnVNPmVy6wuL4HAlaDef3CmZKcj/yP6b8QeaqrltE29C//uUT/8tA== + dependencies: + bignumber.js "^9.0.2" + http "^0.0.1-security" + jsbi "^4.3.0" + memoize-one "^6.0.0" + web3 "^1.7.3" + +jayson@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/jayson/-/jayson-4.1.0.tgz#60dc946a85197317f2b1439d672a8b0a99cea2f9" + integrity sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A== + dependencies: + "@types/connect" "^3.4.33" + "@types/node" "^12.12.54" + "@types/ws" "^7.4.4" + JSONStream "^1.3.5" + commander "^2.20.3" + delay "^5.0.0" + es6-promisify "^5.0.0" + eyes "^0.1.8" + isomorphic-ws "^4.0.1" + json-stringify-safe "^5.0.1" + uuid "^8.3.2" + ws "^7.4.5" + +jest-changed-files@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" + integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== + dependencies: + execa "^5.0.0" + p-limit "^3.1.0" + +jest-circus@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.6.1.tgz#861dab37e71a89907d1c0fabc54a0019738ed824" + integrity sha512-tPbYLEiBU4MYAL2XoZme/bgfUeotpDBd81lgHLCbDZZFaGmECk0b+/xejPFtmiBP87GgP/y4jplcRpbH+fgCzQ== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/expect" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + is-generator-fn "^2.0.0" + jest-each "^29.6.1" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-runtime "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" + p-limit "^3.1.0" + pretty-format "^29.6.1" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-cli@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.6.1.tgz#99d9afa7449538221c71f358f0fdd3e9c6e89f72" + integrity sha512-607dSgTA4ODIN6go9w6xY3EYkyPFGicx51a69H7yfvt7lN53xNswEVLovq+E77VsTRi5fWprLH0yl4DJgE8Ing== + dependencies: + "@jest/core" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" + prompts "^2.0.1" + yargs "^17.3.1" + +jest-config@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.6.1.tgz#d785344509065d53a238224c6cdc0ed8e2f2f0dd" + integrity sha512-XdjYV2fy2xYixUiV2Wc54t3Z4oxYPAELUzWnV6+mcbq0rh742X2p52pii5A3oeRzYjLnQxCsZmp0qpI6klE2cQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.6.1" + "@jest/types" "^29.6.1" + babel-jest "^29.6.1" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.6.1" + jest-environment-node "^29.6.1" + jest-get-type "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.6.1" + jest-runner "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.6.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.1.tgz#13df6db0a89ee6ad93c747c75c85c70ba941e545" + integrity sha512-FsNCvinvl8oVxpNLttNQX7FAq7vR+gMDGj90tiP7siWw1UdakWUGqrylpsYrpvj908IYckm5Y0Q7azNAozU1Kg== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.6.1" + +jest-docblock@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" + integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== + dependencies: + detect-newline "^3.0.0" + +jest-each@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.6.1.tgz#975058e5b8f55c6780beab8b6ab214921815c89c" + integrity sha512-n5eoj5eiTHpKQCAVcNTT7DRqeUmJ01hsAL0Q1SMiBHcBcvTKDELixQOGMCpqhbIuTcfC4kMfSnpmDqRgRJcLNQ== + dependencies: + "@jest/types" "^29.6.1" + chalk "^4.0.0" + jest-get-type "^29.4.3" + jest-util "^29.6.1" + pretty-format "^29.6.1" + +jest-environment-node@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.6.1.tgz#08a122dece39e58bc388da815a2166c58b4abec6" + integrity sha512-ZNIfAiE+foBog24W+2caIldl4Irh8Lx1PUhg/GZ0odM1d/h2qORAsejiFc7zb+SEmYPn1yDZzEDSU5PmDkmVLQ== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/fake-timers" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + jest-mock "^29.6.1" + jest-util "^29.6.1" + +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== + +jest-haste-map@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.6.1.tgz#62655c7a1c1b349a3206441330fb2dbdb4b63803" + integrity sha512-0m7f9PZXxOCk1gRACiVgX85knUKPKLPg4oRCjLoqIm9brTHXaorMA0JpmtmVkQiT8nmXyIVoZd/nnH1cfC33ig== + dependencies: + "@jest/types" "^29.6.1" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.4.3" + jest-util "^29.6.1" + jest-worker "^29.6.1" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + +jest-leak-detector@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.6.1.tgz#66a902c81318e66e694df7d096a95466cb962f8e" + integrity sha512-OrxMNyZirpOEwkF3UHnIkAiZbtkBWiye+hhBweCHkVbCgyEy71Mwbb5zgeTNYWJBi1qgDVfPC1IwO9dVEeTLwQ== + dependencies: + jest-get-type "^29.4.3" + pretty-format "^29.6.1" + +jest-matcher-utils@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.1.tgz#6c60075d84655d6300c5d5128f46531848160b53" + integrity sha512-SLaztw9d2mfQQKHmJXKM0HCbl2PPVld/t9Xa6P9sgiExijviSp7TnZZpw2Fpt+OI3nwUO/slJbOfzfUMKKC5QA== + dependencies: + chalk "^4.0.0" + jest-diff "^29.6.1" + jest-get-type "^29.4.3" + pretty-format "^29.6.1" + +jest-message-util@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.1.tgz#d0b21d87f117e1b9e165e24f245befd2ff34ff8d" + integrity sha512-KoAW2zAmNSd3Gk88uJ56qXUWbFk787QKmjjJVOjtGFmmGSZgDBrlIL4AfQw1xyMYPNVD7dNInfIbur9B2rd/wQ== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.1" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.6.1" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock-promise@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jest-mock-promise/-/jest-mock-promise-2.0.2.tgz#560d2617d8d92c2e9be5ea03a8ede6557eb5ee34" + integrity sha512-JD5zcuoagmSJ+Gb05iD8o+Ze/viWLzI7QnrVY3Q8FyDVGUH5aXxZON+u7S9ouAt59sLNR97dSMWGezfTkkb5hg== + +jest-mock@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.6.1.tgz#049ee26aea8cbf54c764af649070910607316517" + integrity sha512-brovyV9HBkjXAEdRooaTQK42n8usKoSRR3gihzUpYeV/vwqgSoNfrksO7UfSACnPmxasO/8TmHM3w9Hp3G1dgw== + dependencies: + "@jest/types" "^29.6.1" + "@types/node" "*" + jest-util "^29.6.1" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== + +jest-resolve-dependencies@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.1.tgz#b85b06670f987a62515bbf625d54a499e3d708f5" + integrity sha512-BbFvxLXtcldaFOhNMXmHRWx1nXQO5LoXiKSGQcA1LxxirYceZT6ch8KTE1bK3X31TNG/JbkI7OkS/ABexVahiw== + dependencies: + jest-regex-util "^29.4.3" + jest-snapshot "^29.6.1" + +jest-resolve@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.6.1.tgz#4c3324b993a85e300add2f8609f51b80ddea39ee" + integrity sha512-AeRkyS8g37UyJiP9w3mmI/VXU/q8l/IH52vj/cDAyScDcemRbSBhfX/NMYIGilQgSVwsjxrCHf3XJu4f+lxCMg== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.1" + jest-pnp-resolver "^1.2.2" + jest-util "^29.6.1" + jest-validate "^29.6.1" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + +jest-runner@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.6.1.tgz#54557087e7972d345540d622ab5bfc3d8f34688c" + integrity sha512-tw0wb2Q9yhjAQ2w8rHRDxteryyIck7gIzQE4Reu3JuOBpGp96xWgF0nY8MDdejzrLCZKDcp8JlZrBN/EtkQvPQ== + dependencies: + "@jest/console" "^29.6.1" + "@jest/environment" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.4.3" + jest-environment-node "^29.6.1" + jest-haste-map "^29.6.1" + jest-leak-detector "^29.6.1" + jest-message-util "^29.6.1" + jest-resolve "^29.6.1" + jest-runtime "^29.6.1" + jest-util "^29.6.1" + jest-watcher "^29.6.1" + jest-worker "^29.6.1" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.6.1.tgz#8a0fc9274ef277f3d70ba19d238e64334958a0dc" + integrity sha512-D6/AYOA+Lhs5e5il8+5pSLemjtJezUr+8zx+Sn8xlmOux3XOqx4d8l/2udBea8CRPqqrzhsKUsN/gBDE/IcaPQ== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/fake-timers" "^29.6.1" + "@jest/globals" "^29.6.1" + "@jest/source-map" "^29.6.0" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.1" + jest-message-util "^29.6.1" + jest-mock "^29.6.1" + jest-regex-util "^29.4.3" + jest-resolve "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-snapshot@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.6.1.tgz#0d083cb7de716d5d5cdbe80d598ed2fbafac0239" + integrity sha512-G4UQE1QQ6OaCgfY+A0uR1W2AY0tGXUPQpoUClhWHq1Xdnx1H6JOrC2nH5lqnOEqaDgbHFgIwZ7bNq24HpB180A== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.6.1" + graceful-fs "^4.2.9" + jest-diff "^29.6.1" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + natural-compare "^1.4.0" + pretty-format "^29.6.1" + semver "^7.5.3" + +jest-util@^29.0.0, jest-util@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.1.tgz#c9e29a87a6edbf1e39e6dee2b4689b8a146679cb" + integrity sha512-NRFCcjc+/uO3ijUVyNOQJluf8PtGCe/W6cix36+M3cTFgiYqFOOW5MgN4JOOcvbUhcKTYVd1CvHz/LWi8d16Mg== + dependencies: + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.6.1.tgz#765e684af6e2c86dce950aebefbbcd4546d69f7b" + integrity sha512-r3Ds69/0KCN4vx4sYAbGL1EVpZ7MSS0vLmd3gV78O+NAx3PDQQukRU5hNHPXlyqCgFY8XUk7EuTMLugh0KzahA== + dependencies: + "@jest/types" "^29.6.1" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.4.3" + leven "^3.1.0" + pretty-format "^29.6.1" + +jest-watcher@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.6.1.tgz#7c0c43ddd52418af134c551c92c9ea31e5ec942e" + integrity sha512-d4wpjWTS7HEZPaaj8m36QiaP856JthRZkrgcIY/7ISoUWPIillrXM23WPboZVLbiwZBt4/qn2Jke84Sla6JhFA== + dependencies: + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.6.1" + string-length "^4.0.1" + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.6.1.tgz#64b015f0e985ef3a8ad049b61fe92b3db74a5319" + integrity sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA== + dependencies: + "@types/node" "*" + jest-util "^29.6.1" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^29.3.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.6.1.tgz#74be1cb719c3abe439f2d94aeb18e6540a5b02ad" + integrity sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw== + dependencies: + "@jest/core" "^29.6.1" + "@jest/types" "^29.6.1" + import-local "^3.0.2" + jest-cli "^29.6.1" + +joi@^17.7.0: + version "17.12.1" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.12.1.tgz#3347ecf4cd3301962d42191c021b165eef1f395b" + integrity sha512-vtxmq+Lsc5SlfqotnfVjlViWfOL9nt/avKNbKYizwf6gsCfq9NYY/ceYRMFD8XDdrjJ9abJyScWmhmIiy+XRtQ== + dependencies: + "@hapi/hoek" "^9.3.0" + "@hapi/topo" "^5.1.0" + "@sideway/address" "^4.1.5" + "@sideway/formula" "^3.0.1" + "@sideway/pinpoint" "^2.0.0" + +js-sha256@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" + integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== + +js-sha3@0.8.0, js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +js-sha3@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g== + +js-sha512@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha512/-/js-sha512-0.8.0.tgz#dd22db8d02756faccf19f218e3ed61ec8249f7d4" + integrity sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsbi@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-4.3.0.tgz#b54ee074fb6fcbc00619559305c8f7e912b04741" + integrity sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g== + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-bigint@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" + integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== + dependencies: + bignumber.js "^9.0.0" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +json5@^2.2.0, json5@^2.2.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonc-parser@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +jsprim@^1.2.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + +keccak@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.3.tgz#4bc35ad917be1ef54ff246f904c2bbbf9ac61276" + integrity sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +keccak@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" + integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +keyv@^4.0.0: + version "4.5.3" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.3.tgz#00873d2b046df737963157bd04f294ca818c9c25" + integrity sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug== + dependencies: + json-buffer "3.0.1" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +lazy-ass@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +load-module@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/load-module/-/load-module-4.2.1.tgz#3b596a4ddee52c59106d38160aa631a0ded68867" + integrity sha512-Sbfg6R4LjvyThJpqUoADHMjyoI2+cL4msbCQeZ9kkY/CqP/TT2938eftKm7x4I2gd4/A+DEe6nePkbfWYbXwSw== + dependencies: + array-back "^6.2.0" + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + +lodash.memoize@4.x: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lunr@^2.3.9: + version "2.3.9" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" + integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== + +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-error@1.x: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== + +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g== + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== + dependencies: + object-visit "^1.0.0" + +marked@^4.2.12: + version "4.3.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" + integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memoize-one@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" + integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +merge@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/merge/-/merge-2.1.1.tgz#59ef4bf7e0b3e879186436e8481c06a6c162ca98" + integrity sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.0, micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@^2.1.27, mime-types@^2.1.34, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ== + dependencies: + dom-walk "^0.1.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^7.1.3: + version "7.4.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb" + integrity sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.7, minimist@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^2.6.0, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha512-Hepn5kb1lJPtVW84RFT40YG1OddBNTOVUZR2bzQUHc+Z03en8/3uX0+060JDhcEzyO08HmipsN9DcnFMxhIL9w== + dependencies: + mkdirp "*" + +mkdirp@*: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" + integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== + +mkdirp@^0.5.5: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mnemonist@^0.38.3: + version "0.38.5" + resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.5.tgz#4adc7f4200491237fe0fa689ac0b86539685cade" + integrity sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg== + dependencies: + obliterator "^2.0.0" + +mock-fs@^4.1.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" + integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== + +mrmime@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" + integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.0.0, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multibase@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" + integrity sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multibase@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" + integrity sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multicodec@^0.5.5: + version "0.5.7" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" + integrity sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA== + dependencies: + varint "^5.0.0" + +multicodec@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" + integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== + dependencies: + buffer "^5.6.0" + varint "^5.0.0" + +multihashes@^0.4.15, multihashes@~0.4.15: + version "0.4.21" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" + integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== + dependencies: + buffer "^5.5.0" + multibase "^0.7.0" + varint "^5.0.0" + +multistream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/multistream/-/multistream-4.1.0.tgz#7bf00dfd119556fbc153cff3de4c6d477909f5a8" + integrity sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw== + dependencies: + once "^1.4.0" + readable-stream "^3.6.0" + +mustache@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" + integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +my-easy-fp@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/my-easy-fp/-/my-easy-fp-0.9.0.tgz#cd785f2aa4c4a2bb091c6b49c74500b2094f654f" + integrity sha512-uqUYAMPNiRBIzmy6vust9I62HnKIdhHdalDS7kMfMoEv9BYVln0G7DCCZFdV/XiwZno6AFeHyQMSPFqgOntArQ== + dependencies: + debug "^4.1.1" + tslib "1.11.1" + +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + integrity sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +near-api-js@^0.44.2: + version "0.44.2" + resolved "https://registry.yarnpkg.com/near-api-js/-/near-api-js-0.44.2.tgz#e451f68f2c56bd885c7b918db5818a3e6e9423d0" + integrity sha512-eMnc4V+geggapEUa3nU2p8HSHn/njtloI4P2mceHQWO8vDE1NGpnAw8FuTBrLmXSgIv9m6oocgFc9t3VNf5zwg== + dependencies: + bn.js "5.2.0" + borsh "^0.6.0" + bs58 "^4.0.0" + depd "^2.0.0" + error-polyfill "^0.1.3" + http-errors "^1.7.2" + js-sha256 "^0.9.0" + mustache "^4.0.0" + node-fetch "^2.6.1" + text-encoding-utf-8 "^1.0.2" + tweetnacl "^1.0.1" + +near-hd-key@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/near-hd-key/-/near-hd-key-1.2.1.tgz#f508ff15436cf8a439b543220f3cc72188a46756" + integrity sha512-SIrthcL5Wc0sps+2e1xGj3zceEa68TgNZDLuCx0daxmfTP7sFTB3/mtE2pYhlFsCxWoMn+JfID5E1NlzvvbRJg== + dependencies: + bip39 "3.0.2" + create-hmac "1.1.7" + tweetnacl "1.0.3" + +near-seed-phrase@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/near-seed-phrase/-/near-seed-phrase-0.2.0.tgz#fb7cf89682112b1160ab68abb50dc821f49be18a" + integrity sha512-NpmrnejpY1AdlRpDZ0schJQJtfBaoUheRfiYtQpcq9TkwPgqKZCRULV5L3hHmLc0ep7KRtikbPQ9R2ztN/3cyQ== + dependencies: + bip39-light "^1.0.7" + bs58 "^4.0.1" + near-hd-key "^1.2.1" + tweetnacl "^1.0.2" + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-fetch@^2.6.1, node-fetch@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +node-fetch@^2.6.12: + version "2.6.12" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" + integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== + dependencies: + whatwg-url "^5.0.0" + +node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" + integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + +node-releases@^2.0.12: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig== + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +o3@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/o3/-/o3-1.0.3.tgz#192ce877a882dfa6751f0412a865fafb2da1dac0" + integrity sha512-f+4n+vC6s4ysy7YO7O2gslWZBUu8Qj2i2OUJOvjRxQva7jVjYjB29jrr9NCjmxZQR0gzrOcv1RnqoYOeMs5VRQ== + dependencies: + capability "^0.2.5" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.12.3, object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +object-is@^1.0.1, object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.2, object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" + integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== + dependencies: + isobject "^3.0.1" + +object.values@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +obliterator@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" + integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== + +oboe@2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.5.tgz#5554284c543a2266d7a38f17e073821fbde393cd" + integrity sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA== + dependencies: + http-https "^1.0.0" + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0, onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +opener@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + +optionator@^0.9.1: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + +p-cancelable@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" + integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2, p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-headers@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.5.tgz#069793f9356a54008571eb7f9761153e6c770da9" + integrity sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA== + +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== + dependencies: + through "~2.3" + +pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.0.9: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pirates@^4.0.4: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.2.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +pretty-format@^29.0.0, pretty-format@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.1.tgz#ec838c288850b7c4f9090b867c2d4f4edbfb0f3e" + integrity sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog== + dependencies: + "@jest/schemas" "^29.6.0" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +printj@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/printj/-/printj-1.3.1.tgz#9af6b1d55647a1587ac44f4c1654a4b95b8e12cb" + integrity sha512-GA3TdL8szPK4AQ2YnOe/b+Y1jUFwmmGMMK/qbY7VcE3Z7FU8JstbKiKRzO6CIiAKPhTO8m01NoQ0V5f3jc4OGg== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +prompts@^2.0.1, prompts@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +ps-tree@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" + integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== + dependencies: + event-stream "=3.3.4" + +psl@^1.1.28: + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + integrity sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA== + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +punycode@^2.1.0, punycode@^2.1.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +pure-rand@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" + integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +qs@^6.11.0: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + +qs@~6.5.2: + version "6.5.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + +readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +rechoir@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" + integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== + dependencies: + resolve "^1.9.0" + +reduce-flatten@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" + integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== + +regenerator-runtime@^0.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexp.prototype.flags@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" + +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +renamer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/renamer/-/renamer-4.0.0.tgz#12e96979f2fec0f0ce31fa1c6384d668bc80f648" + integrity sha512-yurufcXxbJfFBVAUoByNyDVH811zTZ/MrKo6gUH8pHGeAmdK7J5egj2lSNe57HuVIvnVzSalzeVGu8pi8UHGxg== + dependencies: + array-back "^6.2.0" + chalk "^4.1.2" + command-line-args "^5.2.0" + command-line-usage "^6.1.1" + current-module-paths "^1.1.0" + fast-diff "^1.2.0" + file-set "^5.1.2" + global-dirs "^3.0.0" + load-module "^4.2.1" + printj "^1.3.0" + stream-read-all "^3.0.1" + typical "^7.1.1" + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + +request@^2.79.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve-alpn@^1.0.0, resolve-alpn@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== + +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== + +resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.9.0: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== + dependencies: + lowercase-keys "^2.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry@0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.2.3, rlp@^2.2.4: + version "2.2.7" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" + integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== + dependencies: + bn.js "^5.2.0" + +rpc-websockets@^7.5.1: + version "7.9.0" + resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.9.0.tgz#a3938e16d6f134a3999fdfac422a503731bf8973" + integrity sha512-DwKewQz1IUA5wfLvgM8wDpPRcr+nWSxuFxx5CbrI2z/MyyZ4nXLM86TvIA+cI1ZAdqC8JIBR1mZR55dzaLU+Hw== + dependencies: + "@babel/runtime" "^7.17.2" + eventemitter3 "^4.0.7" + uuid "^8.3.2" + ws "^8.5.0" + optionalDependencies: + bufferutil "^4.0.1" + utf-8-validate "^5.0.2" + +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +rxjs@7.8.1, rxjs@^7.5.5, rxjs@^7.8.0: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + +safe-array-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.0.tgz#2064223cba3c08d2ee05148eedbc563cd6d84060" + integrity sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +scrypt-js@3.0.1, scrypt-js@^3.0.0, scrypt-js@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +secp256k1@^4.0.1, secp256k1@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" + integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== + dependencies: + elliptic "^6.5.4" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +semver@^5.6.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.4, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +sha256-uint8array@^0.10.3: + version "0.10.5" + resolved "https://registry.yarnpkg.com/sha256-uint8array/-/sha256-uint8array-0.10.5.tgz#a8ca295fda3bd444cb32c0fe784819bd614fe6e2" + integrity sha512-KjYP8M6y8VvV62lnSSZwzutuwqphVOOVQamP+GmmClZcYhbq1HSIw/M2tlKgHndiaIwe3tFG5X38YYsQfUdItw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shiki@^0.14.1: + version "0.14.3" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.3.tgz#d1a93c463942bdafb9866d74d619a4347d0bbf64" + integrity sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g== + dependencies: + ansi-sequence-parser "^1.1.0" + jsonc-parser "^3.2.0" + vscode-oniguruma "^1.7.0" + vscode-textmate "^8.0.0" + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^2.7.0: + version "2.8.2" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.2.tgz#5708fb0919d440657326cd5fe7d2599d07705019" + integrity sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +sirv@^1.0.7: + version "1.0.19" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" + integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== + dependencies: + "@polka/url" "^1.0.0-next.20" + mrmime "^1.0.0" + totalist "^1.0.0" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA== + dependencies: + through "2" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +sshpk@^1.7.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" + integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +start-server-and-test@^1.14.0: + version "1.15.5" + resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.15.5.tgz#5c9103bd87c06678fc62658fbe97d09501714011" + integrity sha512-o3EmkX0++GV+qsvIJ/OKWm3w91fD8uS/bPQVPrh/7loaxkpXSuAIHdnmN/P/regQK9eNAK76aBJcHt+OSTk+nA== + dependencies: + arg "^5.0.2" + bluebird "3.7.2" + check-more-types "2.24.0" + debug "4.3.4" + execa "5.1.1" + lazy-ass "1.6.0" + ps-tree "1.2.0" + wait-on "7.0.1" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +stats-lite@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/stats-lite/-/stats-lite-2.2.0.tgz#278a5571fa1d2e8b1691295dccc0235282393bbf" + integrity sha512-/Kz55rgUIv2KP2MKphwYT/NCuSfAlbbMRv2ZWw7wyXayu230zdtzhxxuXXcvsc6EmmhS8bSJl3uS1wmMHFumbA== + dependencies: + isnumber "~1.0.0" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.5.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +stream-browserify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" + integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== + dependencies: + inherits "~2.0.4" + readable-stream "^3.5.0" + +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw== + dependencies: + duplexer "~0.1.1" + +stream-read-all@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/stream-read-all/-/stream-read-all-3.0.1.tgz#60762ae45e61d93ba0978cda7f3913790052ad96" + integrity sha512-EWZT9XOceBPlVJRrYcykW8jyRSZYbkb/0ZK36uLEmoWVO5gxBOnntNTseNzfREsqxqdfEGQrD8SXQ3QWbBmq8A== + +stream-transform@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stream-transform/-/stream-transform-3.3.0.tgz#b9d51b173acd94f195a941cb8856897f0a2af536" + integrity sha512-pG1NeDdmErNYKtvTpFayrEueAmL0xVU5wd22V7InGnatl4Ocq3HY7dcXIKj629kXvYQvglCC7CeDIGAlx1RNGA== + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ== + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string.prototype.trim@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A== + dependencies: + is-hex-prefixed "1.0.0" + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + +superstruct@^0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b" + integrity sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +swarm-js@^0.1.40: + version "0.1.42" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.42.tgz#497995c62df6696f6e22372f457120e43e727979" + integrity sha512-BV7c/dVlA3R6ya1lMlSSNPLYrntt0LUq4YMgy3iwpCIc6rZnS5W2wUoctarZ5pXlpKtxDDf9hNziEkcfrxdhqQ== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^11.8.5" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request "^1.0.1" + +table-layout@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" + integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== + dependencies: + array-back "^4.0.1" + deep-extend "~0.6.0" + typical "^5.2.0" + wordwrapjs "^4.0.0" + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tar@^4.0.2: + version "4.4.19" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== + dependencies: + chownr "^1.1.4" + fs-minipass "^1.2.7" + minipass "^2.9.0" + minizlib "^1.3.3" + mkdirp "^0.5.5" + safe-buffer "^5.2.1" + yallist "^3.1.1" + +terser-webpack-plugin@^5.3.0, terser-webpack-plugin@^5.3.7: + version "5.3.9" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" + integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.17" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.16.8" + +terser@^5.16.8: + version "5.19.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.19.2.tgz#bdb8017a9a4a8de4663a7983f45c506534f9234e" + integrity sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +text-encoding-utf-8@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" + integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3, through@~2.3.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA== + +tiny-invariant@^1.1.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" + integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== + +tiny-warning@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + +tmp-promise@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.3.tgz#60a1a1cc98c988674fcbfd23b6e3367bdeac4ce7" + integrity sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ== + dependencies: + tmp "^0.2.0" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmp@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toformat@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/toformat/-/toformat-2.0.0.tgz#7a043fd2dfbe9021a4e36e508835ba32056739d8" + integrity sha512-03SWBVop6nU8bpyZCx7SodpYznbZF5R4ljwNLBcTQzKOD9xuihRo/psX58llS1BMFhhAI08H3luot5GoXJz2pQ== + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +totalist@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" + integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +tronweb@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/tronweb/-/tronweb-5.3.2.tgz#393b0fa0290e2c5aa7a3b3b82956f53ca65a764f" + integrity sha512-iPcIjMCxb6H7FXMntAj47F3L+7jSideyQ7ZOvRj9MeZBh46SHevMrDDR57HzakUa/tT8VvlPFHtqFK4hzTLkXw== + dependencies: + "@babel/runtime" "^7.0.0" + "@ethersproject/abi" "^5.7.0" + "@tronweb3/google-protobuf" "^3.21.2" + axios "^1.6.2" + bignumber.js "^9.0.1" + ethereum-cryptography "^2.0.0" + ethers "^6.6.0" + eventemitter3 "^3.1.0" + injectpromise "^1.0.0" + lodash "^4.17.21" + querystring-es3 "^0.2.1" + semver "^5.6.0" + validator "^13.7.0" + +ts-essentials@^9.0.0: + version "9.3.2" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-9.3.2.tgz#5f4ae6d24e20d042a033316c0592dbb51d1b273f" + integrity sha512-JxKJzuWqH1MmH4ZFHtJzGEhkfN3QvVR3C3w+4BIoWeoY68UVVoA2Np/Bca9z0IPSErVCWhv439aT0We4Dks8kQ== + +ts-jest@^29.0.3: + version "29.1.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" + integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "4.x" + make-error "1.x" + semver "^7.5.3" + yargs-parser "^21.0.1" + +ts-loader@^9.3.0: + version "9.4.4" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.4.4.tgz#6ceaf4d58dcc6979f84125335904920884b7cee4" + integrity sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + +ts-patch@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/ts-patch/-/ts-patch-3.0.2.tgz#cbdf88e4dfb596e4dab8f2c8269361d33270a0ba" + integrity sha512-iTg8euqiNsNM1VDfOsVIsP0bM4kAVXU38n7TGQSkky7YQX/syh6sDPIRkvSS0HjT8ZOr0pq1h+5Le6jdB3hiJQ== + dependencies: + chalk "^4.1.2" + global-prefix "^3.0.0" + minimist "^1.2.8" + resolve "^1.22.2" + semver "^7.3.8" + strip-ansi "^6.0.1" + +tsconfig-paths-webpack-plugin@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-3.5.2.tgz#01aafff59130c04a8c4ebc96a3045c43c376449a" + integrity sha512-EhnfjHbzm5IYI9YPNVIxx1moxMI4bpHD2e0zTXeDNQcwjjRaGepP7IhTHJkyDBG0CAOoxRfe7jCG630Ou+C6Pw== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.7.0" + tsconfig-paths "^3.9.0" + +tsconfig-paths@^3.14.1, tsconfig-paths@^3.9.0: + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tscpaths@^0.0.9: + version "0.0.9" + resolved "https://registry.yarnpkg.com/tscpaths/-/tscpaths-0.0.9.tgz#c77abfde6820920f10c64f83c27753b9505814ab" + integrity sha512-tz4qimSJTCjYtHVsoY7pvxLcxhmhgmwzm7fyMEiL3/kPFFVyUuZOwuwcWwjkAsIrSUKJK22A7fNuJUwxzQ+H+w== + dependencies: + commander "^2.20.0" + globby "^9.2.0" + +tslib@1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" + integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== + +tslib@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + +tslib@^1.11.1, tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.1.0, tslib@^2.3.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" + integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== + +tslib@^2.5.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +tweetnacl-util@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== + +tweetnacl@1.0.3, tweetnacl@^1.0.1, tweetnacl@^1.0.2, tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" + integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== + +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedoc@^0.23.21: + version "0.23.28" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.23.28.tgz#3ce9c36ef1c273fa849d2dea18651855100d3ccd" + integrity sha512-9x1+hZWTHEQcGoP7qFmlo4unUoVJLB0H/8vfO/7wqTnZxg4kPuji9y3uRzEu0ZKez63OJAUmiGhUrtukC6Uj3w== + dependencies: + lunr "^2.3.9" + marked "^4.2.12" + minimatch "^7.1.3" + shiki "^0.14.1" + +typescript-transform-paths@^3.4.6: + version "3.4.6" + resolved "https://registry.yarnpkg.com/typescript-transform-paths/-/typescript-transform-paths-3.4.6.tgz#28e6b24eb17a34116484a4b7af7323b8bb756db6" + integrity sha512-qdgpCk9oRHkIBhznxaHAapCFapJt5e4FbFik7Y4qdqtp6VyC3smAIPoDEIkjZ2eiF7x5+QxUPYNwJAtw0thsTw== + dependencies: + minimatch "^3.0.4" + +typescript@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== + +typical@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" + integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== + +typical@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" + integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== + +typical@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/typical/-/typical-7.1.1.tgz#ba177ab7ab103b78534463ffa4c0c9754523ac1f" + integrity sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA== + +u3@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/u3/-/u3-0.1.1.tgz#5f52044f42ee76cd8de33148829e14528494b73b" + integrity sha512-+J5D5ir763y+Am/QY6hXNRlwljIeRMZMGs0cT6qqZVVzzT3X3nFPXVyPOFRMOR4kupB0T8JnCdpWdp6Q/iXn3w== + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +update-browserslist-db@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== + +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + integrity sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg== + +url@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.1.tgz#26f90f615427eca1b9f4d6a28288c147e2302a32" + integrity sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA== + dependencies: + punycode "^1.4.1" + qs "^6.11.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +utf-8-validate@^5.0.2: + version "5.0.10" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2" + integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ== + dependencies: + node-gyp-build "^4.3.0" + +utf8@3.0.0, utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util@^0.12.0, util@^0.12.5: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + +v8-compile-cache@^2.0.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== + +v8-to-istanbul@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + +validator@^13.7.0: + version "13.9.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" + integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA== + +varint@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +viem@^1.2.9, viem@^1.3.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/viem/-/viem-1.4.1.tgz#490f39b3f371bf58910b1b338c237e19066805cf" + integrity sha512-MtaoBHDSJDqa+QyXKG5d+S6EQSebRO0tzw6anSP4zC7AbC614vMeg9Y8LbkmEkWCw8swFYkort+H9l7GkWB0uA== + dependencies: + "@adraffy/ens-normalize" "1.9.0" + "@noble/curves" "1.0.0" + "@noble/hashes" "1.3.0" + "@scure/bip32" "1.3.0" + "@scure/bip39" "1.2.0" + "@wagmi/chains" "1.6.0" + abitype "0.9.3" + isomorphic-ws "5.0.0" + ws "8.12.0" + +vlq@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-2.0.4.tgz#6057b85729245b9829e3cc7755f95b228d4fe041" + integrity sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA== + +vscode-oniguruma@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" + integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== + +vscode-textmate@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d" + integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== + +wait-on@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-7.0.1.tgz#5cff9f8427e94f4deacbc2762e6b0a489b19eae9" + integrity sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog== + dependencies: + axios "^0.27.2" + joi "^17.7.0" + lodash "^4.17.21" + minimist "^1.2.7" + rxjs "^7.8.0" + +walker@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + +web3-bzz@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.10.0.tgz#ac74bc71cdf294c7080a79091079192f05c5baed" + integrity sha512-o9IR59io3pDUsXTsps5pO5hW1D5zBmg46iNc2t4j2DkaYHNdDLwk2IP9ukoM2wg47QILfPEJYzhTfkS/CcX0KA== + dependencies: + "@types/node" "^12.12.6" + got "12.1.0" + swarm-js "^0.1.40" + +web3-core-helpers@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.10.0.tgz#1016534c51a5df77ed4f94d1fcce31de4af37fad" + integrity sha512-pIxAzFDS5vnbXvfvLSpaA1tfRykAe9adw43YCKsEYQwH0gCLL0kMLkaCX3q+Q8EVmAh+e1jWL/nl9U0de1+++g== + dependencies: + web3-eth-iban "1.10.0" + web3-utils "1.10.0" + +web3-core-method@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.10.0.tgz#82668197fa086e8cc8066742e35a9d72535e3412" + integrity sha512-4R700jTLAMKDMhQ+nsVfIXvH6IGJlJzGisIfMKWAIswH31h5AZz7uDUW2YctI+HrYd+5uOAlS4OJeeT9bIpvkA== + dependencies: + "@ethersproject/transactions" "^5.6.2" + web3-core-helpers "1.10.0" + web3-core-promievent "1.10.0" + web3-core-subscriptions "1.10.0" + web3-utils "1.10.0" + +web3-core-promievent@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.10.0.tgz#cbb5b3a76b888df45ed3a8d4d8d4f54ccb66a37b" + integrity sha512-68N7k5LWL5R38xRaKFrTFT2pm2jBNFaM4GioS00YjAKXRQ3KjmhijOMG3TICz6Aa5+6GDWYelDNx21YAeZ4YTg== + dependencies: + eventemitter3 "4.0.4" + +web3-core-requestmanager@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.10.0.tgz#4b34f6e05837e67c70ff6f6993652afc0d54c340" + integrity sha512-3z/JKE++Os62APml4dvBM+GAuId4h3L9ckUrj7ebEtS2AR0ixyQPbrBodgL91Sv7j7cQ3Y+hllaluqjguxvSaQ== + dependencies: + util "^0.12.5" + web3-core-helpers "1.10.0" + web3-providers-http "1.10.0" + web3-providers-ipc "1.10.0" + web3-providers-ws "1.10.0" + +web3-core-subscriptions@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.10.0.tgz#b534592ee1611788fc0cb0b95963b9b9b6eacb7c" + integrity sha512-HGm1PbDqsxejI075gxBc5OSkwymilRWZufIy9zEpnWKNmfbuv5FfHgW1/chtJP6aP3Uq2vHkvTDl3smQBb8l+g== + dependencies: + eventemitter3 "4.0.4" + web3-core-helpers "1.10.0" + +web3-core@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.10.0.tgz#9aa07c5deb478cf356c5d3b5b35afafa5fa8e633" + integrity sha512-fWySwqy2hn3TL89w5TM8wXF1Z2Q6frQTKHWmP0ppRQorEK8NcHJRfeMiv/mQlSKoTS1F6n/nv2uyZsixFycjYQ== + dependencies: + "@types/bn.js" "^5.1.1" + "@types/node" "^12.12.6" + bignumber.js "^9.0.0" + web3-core-helpers "1.10.0" + web3-core-method "1.10.0" + web3-core-requestmanager "1.10.0" + web3-utils "1.10.0" + +web3-eth-abi@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.10.0.tgz#53a7a2c95a571e205e27fd9e664df4919483cce1" + integrity sha512-cwS+qRBWpJ43aI9L3JS88QYPfFcSJJ3XapxOQ4j40v6mk7ATpA8CVK1vGTzpihNlOfMVRBkR95oAj7oL6aiDOg== + dependencies: + "@ethersproject/abi" "^5.6.3" + web3-utils "1.10.0" + +web3-eth-accounts@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.10.0.tgz#2942beca0a4291455f32cf09de10457a19a48117" + integrity sha512-wiq39Uc3mOI8rw24wE2n15hboLE0E9BsQLdlmsL4Zua9diDS6B5abXG0XhFcoNsXIGMWXVZz4TOq3u4EdpXF/Q== + dependencies: + "@ethereumjs/common" "2.5.0" + "@ethereumjs/tx" "3.3.2" + eth-lib "0.2.8" + ethereumjs-util "^7.1.5" + scrypt-js "^3.0.1" + uuid "^9.0.0" + web3-core "1.10.0" + web3-core-helpers "1.10.0" + web3-core-method "1.10.0" + web3-utils "1.10.0" + +web3-eth-contract@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.10.0.tgz#8e68c7654576773ec3c91903f08e49d0242c503a" + integrity sha512-MIC5FOzP/+2evDksQQ/dpcXhSqa/2hFNytdl/x61IeWxhh6vlFeSjq0YVTAyIzdjwnL7nEmZpjfI6y6/Ufhy7w== + dependencies: + "@types/bn.js" "^5.1.1" + web3-core "1.10.0" + web3-core-helpers "1.10.0" + web3-core-method "1.10.0" + web3-core-promievent "1.10.0" + web3-core-subscriptions "1.10.0" + web3-eth-abi "1.10.0" + web3-utils "1.10.0" + +web3-eth-ens@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.10.0.tgz#96a676524e0b580c87913f557a13ed810cf91cd9" + integrity sha512-3hpGgzX3qjgxNAmqdrC2YUQMTfnZbs4GeLEmy8aCWziVwogbuqQZ+Gzdfrym45eOZodk+lmXyLuAdqkNlvkc1g== + dependencies: + content-hash "^2.5.2" + eth-ens-namehash "2.0.8" + web3-core "1.10.0" + web3-core-helpers "1.10.0" + web3-core-promievent "1.10.0" + web3-eth-abi "1.10.0" + web3-eth-contract "1.10.0" + web3-utils "1.10.0" + +web3-eth-iban@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.10.0.tgz#5a46646401965b0f09a4f58e7248c8a8cd22538a" + integrity sha512-0l+SP3IGhInw7Q20LY3IVafYEuufo4Dn75jAHT7c2aDJsIolvf2Lc6ugHkBajlwUneGfbRQs/ccYPQ9JeMUbrg== + dependencies: + bn.js "^5.2.1" + web3-utils "1.10.0" + +web3-eth-personal@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.10.0.tgz#94d525f7a29050a0c2a12032df150ac5ea633071" + integrity sha512-anseKn98w/d703eWq52uNuZi7GhQeVjTC5/svrBWEKob0WZ5kPdo+EZoFN0sp5a5ubbrk/E0xSl1/M5yORMtpg== + dependencies: + "@types/node" "^12.12.6" + web3-core "1.10.0" + web3-core-helpers "1.10.0" + web3-core-method "1.10.0" + web3-net "1.10.0" + web3-utils "1.10.0" + +web3-eth@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.10.0.tgz#38b905e2759697c9624ab080cfcf4e6c60b3a6cf" + integrity sha512-Z5vT6slNMLPKuwRyKGbqeGYC87OAy8bOblaqRTgg94CXcn/mmqU7iPIlG4506YdcdK3x6cfEDG7B6w+jRxypKA== + dependencies: + web3-core "1.10.0" + web3-core-helpers "1.10.0" + web3-core-method "1.10.0" + web3-core-subscriptions "1.10.0" + web3-eth-abi "1.10.0" + web3-eth-accounts "1.10.0" + web3-eth-contract "1.10.0" + web3-eth-ens "1.10.0" + web3-eth-iban "1.10.0" + web3-eth-personal "1.10.0" + web3-net "1.10.0" + web3-utils "1.10.0" + +web3-net@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.10.0.tgz#be53e7f5dafd55e7c9013d49c505448b92c9c97b" + integrity sha512-NLH/N3IshYWASpxk4/18Ge6n60GEvWBVeM8inx2dmZJVmRI6SJIlUxbL8jySgiTn3MMZlhbdvrGo8fpUW7a1GA== + dependencies: + web3-core "1.10.0" + web3-core-method "1.10.0" + web3-utils "1.10.0" + +web3-providers-http@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.10.0.tgz#864fa48675e7918c9a4374e5f664b32c09d0151b" + integrity sha512-eNr965YB8a9mLiNrkjAWNAPXgmQWfpBfkkn7tpEFlghfww0u3I0tktMZiaToJVcL2+Xq+81cxbkpeWJ5XQDwOA== + dependencies: + abortcontroller-polyfill "^1.7.3" + cross-fetch "^3.1.4" + es6-promise "^4.2.8" + web3-core-helpers "1.10.0" + +web3-providers-ipc@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.10.0.tgz#9747c7a6aee96a51488e32fa7c636c3460b39889" + integrity sha512-OfXG1aWN8L1OUqppshzq8YISkWrYHaATW9H8eh0p89TlWMc1KZOL9vttBuaBEi96D/n0eYDn2trzt22bqHWfXA== + dependencies: + oboe "2.1.5" + web3-core-helpers "1.10.0" + +web3-providers-ws@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.10.0.tgz#cb0b87b94c4df965cdf486af3a8cd26daf3975e5" + integrity sha512-sK0fNcglW36yD5xjnjtSGBnEtf59cbw4vZzJ+CmOWIKGIR96mP5l684g0WD0Eo+f4NQc2anWWXG74lRc9OVMCQ== + dependencies: + eventemitter3 "4.0.4" + web3-core-helpers "1.10.0" + websocket "^1.0.32" + +web3-shh@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.10.0.tgz#c2979b87e0f67a7fef2ce9ee853bd7bfbe9b79a8" + integrity sha512-uNUUuNsO2AjX41GJARV9zJibs11eq6HtOe6Wr0FtRUcj8SN6nHeYIzwstAvJ4fXA53gRqFMTxdntHEt9aXVjpg== + dependencies: + web3-core "1.10.0" + web3-core-method "1.10.0" + web3-core-subscriptions "1.10.0" + web3-net "1.10.0" + +web3-utils@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.0.tgz#ca4c1b431a765c14ac7f773e92e0fd9377ccf578" + integrity sha512-kSaCM0uMcZTNUSmn5vMEhlo02RObGNRRCkdX0V9UTAU0+lrvn0HSaudyCo6CQzuXUsnuY2ERJGCGPfeWmv19Rg== + dependencies: + bn.js "^5.2.1" + ethereum-bloom-filters "^1.0.6" + ethereumjs-util "^7.1.0" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + +web3@^1.7.3, web3@^1.8.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.10.0.tgz#2fde0009f59aa756c93e07ea2a7f3ab971091274" + integrity sha512-YfKY9wSkGcM8seO+daR89oVTcbu18NsVfvOngzqMYGUU0pPSQmE57qQDvQzUeoIOHAnXEBNzrhjQJmm8ER0rng== + dependencies: + web3-bzz "1.10.0" + web3-core "1.10.0" + web3-eth "1.10.0" + web3-eth-personal "1.10.0" + web3-net "1.10.0" + web3-shh "1.10.0" + web3-utils "1.10.0" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +webpack-bundle-analyzer@^4.5.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.0.tgz#fc093c4ab174fd3dcbd1c30b763f56d10141209d" + integrity sha512-+bXGmO1LyiNx0i9enBu3H8mv42sj/BJWhZNFwjz92tVnBa9J3JMGo2an2IXlEleoDOPn/Hofl5hr/xCpObUDtw== + dependencies: + "@discoveryjs/json-ext" "0.5.7" + acorn "^8.0.4" + acorn-walk "^8.0.0" + chalk "^4.1.0" + commander "^7.2.0" + gzip-size "^6.0.0" + lodash "^4.17.20" + opener "^1.5.2" + sirv "^1.0.7" + ws "^7.3.1" + +webpack-cli@^4.9.1: + version "4.10.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" + integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^1.2.0" + "@webpack-cli/info" "^1.5.0" + "@webpack-cli/serve" "^1.7.0" + colorette "^2.0.14" + commander "^7.0.0" + cross-spawn "^7.0.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^2.2.0" + rechoir "^0.7.0" + webpack-merge "^5.7.3" + +webpack-merge@^5.7.3: + version "5.9.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.9.0.tgz#dc160a1c4cf512ceca515cc231669e9ddb133826" + integrity sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.65.0: + version "5.88.2" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.2.tgz#f62b4b842f1c6ff580f3fcb2ed4f0b579f4c210e" + integrity sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" + acorn "^8.7.1" + acorn-import-assertions "^1.9.0" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.15.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.7" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +websocket@^1.0.32: + version "1.0.34" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" + integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-typed-array@^1.1.10, which-typed-array@^1.1.11, which-typed-array@^1.1.2: + version "1.1.11" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" + integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +wordwrapjs@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" + integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== + dependencies: + reduce-flatten "^2.0.0" + typical "^5.2.0" + +wrap-ansi@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + +ws@7.4.6: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + +ws@8.12.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8" + integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig== + +ws@8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" + integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +ws@^7.3.1, ws@^7.4.5: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +ws@^8.5.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== + +ws@^8.8.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + +xhr-request-promise@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" + integrity sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg== + dependencies: + xhr-request "^1.1.0" + +xhr-request@^1.0.1, xhr-request@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr@^2.0.4, xhr@^2.3.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" + integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== + dependencies: + global "~4.4.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug== + +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@^21.0.1, yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.0.1, yargs@^17.3.0, yargs@^17.3.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zod@^3.21.4: + version "3.21.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db" + integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==