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