├── .flake8
├── .gitignore
├── README.md
├── signing-providers
├── .prettierignore
├── .eslintignore
├── .prettierrc
├── src
│ ├── index.js
│ ├── helpers.js
│ ├── config.devnet.js
│ ├── config.js
│ ├── auth.js
│ ├── iframe-communication.js
│ ├── extension.js
│ ├── webview.js
│ ├── cross-window.js
│ ├── metamask.js
│ ├── wallet-connect-v2.js
│ ├── iframe-wallet.js
│ ├── hw.js
│ └── web-wallet.js
├── .eslintrc.js
├── dummy-certificate.pem
├── package.json
├── README.md
├── iframe-wallet.html
├── dummy-certificate-key.pem
├── webpack.config.js
├── webview-child.html
└── index.html
├── wallet
├── README.md
├── package.json
├── index.js
└── basic.js
├── contracts
├── adder.wasm
├── counter.wasm
├── counter.abi.json
├── adder.abi.json
├── example.abi.json
└── multisig-full.abi.json
├── codec
├── package.json
├── README.md
├── decodeCustomType.js
└── decodeEvent.js
├── testwallets
├── alice.pem
└── alice.json
└── .github
└── workflows
└── package-lock-checks.yml
/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | ignore = E501
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/node_modules/**
2 | **/out/**
3 | codec/package-lock.json
4 | .idea
5 | settings.json
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mx-sdk-js-examples
2 |
3 | Examples of using sdk-js and its satellite packages.
4 |
--------------------------------------------------------------------------------
/signing-providers/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 | build/
4 | out/
5 | .yalc/
6 | *.d.ts
--------------------------------------------------------------------------------
/wallet/README.md:
--------------------------------------------------------------------------------
1 | # Examples: using wallet components
2 |
3 | ```bash
4 | node ./index.js
5 | ```
6 |
--------------------------------------------------------------------------------
/contracts/adder.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/multiversx/mx-sdk-js-examples/HEAD/contracts/adder.wasm
--------------------------------------------------------------------------------
/contracts/counter.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/multiversx/mx-sdk-js-examples/HEAD/contracts/counter.wasm
--------------------------------------------------------------------------------
/signing-providers/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 | build/
4 | out/
5 | .yalc/
6 | *.d.ts
7 | .prettierignore
--------------------------------------------------------------------------------
/signing-providers/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": false,
3 | "trailingComma": "all",
4 | "tabWidth": 4,
5 | "printWidth": 120
6 | }
7 |
--------------------------------------------------------------------------------
/codec/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "dependencies": {
4 | "@multiversx/sdk-core": "15.0.0",
5 | "minimist": "1.2.8"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/wallet/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples",
3 | "version": "0.0.0",
4 | "description": "Examples",
5 | "scripts": {},
6 | "author": "MultiversX",
7 | "dependencies": {
8 | "@multiversx/sdk-core": "15.0.0",
9 | "axios": "^1.11.0"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/signing-providers/src/index.js:
--------------------------------------------------------------------------------
1 | export * from "./extension";
2 | export * from "./hw";
3 | export * from "./metamask";
4 | export * from "./wallet-connect-v2";
5 | export * from "./web-wallet";
6 | export * from "./webview";
7 | export * from "./cross-window";
8 | export * from "./iframe-communication";
9 | export * from "./iframe-wallet";
10 |
--------------------------------------------------------------------------------
/testwallets/alice.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY for erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th-----
2 | NDEzZjQyNTc1ZjdmMjZmYWQzMzE3YTc3ODc3MTIxMmZkYjgwMjQ1ODUwOTgxZTQ4
3 | YjU4YTRmMjVlMzQ0ZThmOTAxMzk0NzJlZmY2ODg2NzcxYTk4MmYzMDgzZGE1ZDQy
4 | MWYyNGMyOTE4MWU2Mzg4ODIyOGRjODFjYTYwZDY5ZTE=
5 | -----END PRIVATE KEY for erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th-----
--------------------------------------------------------------------------------
/wallet/index.js:
--------------------------------------------------------------------------------
1 | const { exampleDeriveAccountsFromMnemonic, exampleSignAndBroadcastTransaction, exampleSignMessage, exampleVerifyTransactionSignature, exampleVerifyMessage } = require("./basic");
2 |
3 | (async () => {
4 | exampleDeriveAccountsFromMnemonic();
5 | await exampleSignAndBroadcastTransaction();
6 | await exampleSignMessage();
7 | await exampleVerifyMessage();
8 | await exampleVerifyTransactionSignature();
9 | })();
10 |
--------------------------------------------------------------------------------
/codec/README.md:
--------------------------------------------------------------------------------
1 | # Codec utilities (examples)
2 |
3 | Decode a transaction event:
4 |
5 | ```
6 | node ./decodeEvent.js --abi=../contracts/example.abi.json --event=deposit --api=https://testnet-api.multiversx.com --tx=532087e5021c9ab8be8a4db5ad843cfe0610761f6334d9693b3765992fd05f67
7 | ```
8 |
9 | Decode a value of a custom type (a structure):
10 |
11 | ```
12 | node ./decodeCustomType.js --abi=../contracts/example.abi.json --type=DepositEvent --data=00000000000003db000000
13 | ```
14 |
--------------------------------------------------------------------------------
/signing-providers/src/helpers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Displays a message and an optional outcome using console.log and alert.
3 | *
4 | * @param {string} message - The message to be displayed.
5 | * @param {Object} [outcome] - The optional outcome object to be displayed.
6 | * @returns {void}
7 | */
8 | export const displayOutcome = (message, outcome) => {
9 | if (!outcome) {
10 | console.log(message);
11 | alert(message);
12 | return;
13 | }
14 |
15 | console.log(message, outcome);
16 | alert(`${message}\n${JSON.stringify(outcome, null, 4)}`);
17 | };
18 |
--------------------------------------------------------------------------------
/signing-providers/src/config.devnet.js:
--------------------------------------------------------------------------------
1 | import { WALLET_PROVIDER_DEVNET } from "@multiversx/sdk-web-wallet-provider";
2 |
3 | export const CHAIN_ID = "D";
4 | export const NETWORK_NAME = "Devnet";
5 | export const WALLET_PROVIDER_URL = WALLET_PROVIDER_DEVNET;
6 | export const API_URL = "https://devnet-api.multiversx.com";
7 | export const PROXY_URL = "https://devnet-gateway.multiversx.com";
8 | export const WALLET_URL = "https://devnet-wallet.multiversx.com";
9 |
10 | // Generate your own WalletConnect 2 ProjectId here: https://cloud.walletconnect.com/app
11 | export const WALLET_CONNECT_PROJECT_ID = "9b1a9564f91cb659ffe21b73d5c4e2d8";
12 | export const WALLET_CONNECT_RELAY_URL = "wss://relay.walletconnect.com";
13 | export const METAMASK_SNAP_WALLET_ADDRESS = "https://devnet-snap-wallet.multiversx.com";
14 |
--------------------------------------------------------------------------------
/signing-providers/src/config.js:
--------------------------------------------------------------------------------
1 | import { WALLET_PROVIDER_TESTNET } from "@multiversx/sdk-web-wallet-provider";
2 |
3 | export const CHAIN_ID = "T";
4 | export const NETWORK_NAME = "Testnet";
5 | export const WALLET_PROVIDER_URL = WALLET_PROVIDER_TESTNET;
6 | export const API_URL = "https://testnet-api.multiversx.com";
7 | export const PROXY_URL = "https://testnet-gateway.multiversx.com";
8 | export const WALLET_URL = "https://testnet-wallet.multiversx.com";
9 |
10 | // Generate your own WalletConnect 2 ProjectId here: https://cloud.walletconnect.com/app
11 | export const WALLET_CONNECT_PROJECT_ID = "9b1a9564f91cb659ffe21b73d5c4e2d8";
12 | export const WALLET_CONNECT_RELAY_URL = "wss://relay.walletconnect.com";
13 | export const METAMASK_SNAP_WALLET_ADDRESS = "https://testnet-snap-wallet.multiversx.com";
14 |
--------------------------------------------------------------------------------
/contracts/counter.abi.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "counter",
3 | "constructor": {
4 | "inputs": [],
5 | "outputs": []
6 | },
7 | "endpoints": [
8 | {
9 | "name": "increment",
10 | "inputs": [],
11 | "outputs": [
12 | {
13 | "type": "i64"
14 | }
15 | ]
16 | },
17 | {
18 | "name": "decrement",
19 | "inputs": [],
20 | "outputs": [
21 | {
22 | "type": "i64"
23 | }
24 | ]
25 | },
26 | {
27 | "name": "get",
28 | "inputs": [],
29 | "outputs": [
30 | {
31 | "type": "i64"
32 | }
33 | ]
34 | }
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/testwallets/alice.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 4,
3 | "id": "0dc10c02-b59b-4bac-9710-6b2cfa4284ba",
4 | "address": "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1",
5 | "bech32": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th",
6 | "crypto": {
7 | "ciphertext": "4c41ef6fdfd52c39b1585a875eb3c86d30a315642d0e35bb8205b6372c1882f135441099b11ff76345a6f3a930b5665aaf9f7325a32c8ccd60081c797aa2d538",
8 | "cipherparams": {
9 | "iv": "033182afaa1ebaafcde9ccc68a5eac31"
10 | },
11 | "cipher": "aes-128-ctr",
12 | "kdf": "scrypt",
13 | "kdfparams": {
14 | "dklen": 32,
15 | "salt": "4903bd0e7880baa04fc4f886518ac5c672cdc745a6bd13dcec2b6c12e9bffe8d",
16 | "n": 4096,
17 | "r": 8,
18 | "p": 1
19 | },
20 | "mac": "5b4a6f14ab74ba7ca23db6847e28447f0e6a7724ba9664cf425df707a84f5a8b"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.github/workflows/package-lock-checks.yml:
--------------------------------------------------------------------------------
1 | name: Check package-lock.json
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | compare-package-lock:
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v2
12 |
13 | - name: Use Node.js LTS
14 | uses: actions/setup-node@v1
15 | with:
16 | node-version: '16.14.2'
17 |
18 | - name: Check package-lock.json for "signing-providers"
19 | run: |
20 | cd signing-providers
21 | cp package-lock.json package-lock-copy.json
22 | rm -f package-lock.json
23 | npm install
24 | cmp package-lock.json package-lock-copy.json
25 |
26 | - name: Check package-lock.json for "wallet"
27 | run: |
28 | cd wallet
29 | cp package-lock.json package-lock-copy.json
30 | rm -f package-lock.json
31 | npm install
32 | cmp package-lock.json package-lock-copy.json
33 |
34 | # TODO: replicate the step above for other folders, as necessary
35 |
--------------------------------------------------------------------------------
/signing-providers/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parserOptions: {
3 | sourceType: "module",
4 | ecmaVersion: 2020,
5 | },
6 | extends: ["eslint:recommended", "plugin:prettier/recommended"],
7 | plugins: ["prettier"],
8 | root: true,
9 | env: {
10 | node: true,
11 | browser: true,
12 | },
13 | ignorePatterns: [
14 | ".eslintrc.js",
15 | "node_modules",
16 | "out",
17 | "out-tests",
18 | "out-browser",
19 | "out-browser-tests",
20 | "cookbook",
21 | ],
22 | rules: {
23 | "no-empty-function": "off",
24 | "no-use-before-define": "off",
25 | quotes: "off",
26 | "no-empty-pattern": "off",
27 | "no-var": "warn",
28 | "no-unused-vars": [
29 | "warn",
30 | {
31 | argsIgnorePattern: "^_",
32 | varsIgnorePattern: "^_",
33 | },
34 | ],
35 | "prefer-const": "off",
36 | "prettier/prettier": "error",
37 | },
38 | };
39 |
--------------------------------------------------------------------------------
/contracts/adder.abi.json:
--------------------------------------------------------------------------------
1 | {
2 | "docs": [
3 | "One of the simplest smart contracts possible,",
4 | "it holds a single variable in storage, which anyone can increment."
5 | ],
6 | "name": "Adder",
7 | "constructor": {
8 | "inputs": [
9 | {
10 | "name": "initial_value",
11 | "type": "BigUint"
12 | }
13 | ],
14 | "outputs": []
15 | },
16 | "endpoints": [
17 | {
18 | "name": "getSum",
19 | "mutability": "readonly",
20 | "inputs": [],
21 | "outputs": [
22 | {
23 | "type": "BigUint"
24 | }
25 | ]
26 | },
27 | {
28 | "docs": [
29 | "Add desired amount to the storage variable."
30 | ],
31 | "name": "add",
32 | "mutability": "mutable",
33 | "inputs": [
34 | {
35 | "name": "value",
36 | "type": "BigUint"
37 | }
38 | ],
39 | "outputs": []
40 | }
41 | ],
42 | "events": [],
43 | "hasCallback": false,
44 | "types": {}
45 | }
46 |
--------------------------------------------------------------------------------
/signing-providers/src/auth.js:
--------------------------------------------------------------------------------
1 | import { NativeAuthClient } from "@multiversx/sdk-native-auth-client";
2 | import { API_URL, NETWORK_NAME } from "./config";
3 |
4 | export async function createNativeAuthInitialPart() {
5 | const client = new NativeAuthClient({
6 | apiUrl: API_URL,
7 | expirySeconds: 7200,
8 | });
9 |
10 | const initialPart = await client.initialize();
11 | console.log("createNativeAuthInitialPart()", initialPart);
12 |
13 | return initialPart;
14 | }
15 |
16 | export function packNativeAuthToken(address, initialPart, signature) {
17 | console.log("packNativeAuthToken()");
18 | console.log("address", address);
19 | console.log("initialPart", initialPart);
20 | console.log("signature", signature);
21 |
22 | const client = new NativeAuthClient();
23 | const accessToken = client.getToken(address, initialPart, signature);
24 | return accessToken;
25 | }
26 |
27 | export function verifyNativeAuthToken(nativeAuthToken) {
28 | const message = `
29 | Native auth token:
30 | ${nativeAuthToken}
31 |
32 | Normally, you would now send this token to your server, which would then validate it.
33 |
34 | Go and check it on:
35 | https://utils.multiversx.com/auth (switch to ${NETWORK_NAME})`;
36 |
37 | console.log(message);
38 | alert(message);
39 | }
40 |
--------------------------------------------------------------------------------
/signing-providers/dummy-certificate.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDxTCCAq2gAwIBAgIURipd1gg/wb/awjtz1q09AeYhAkcwDQYJKoZIhvcNAQEL
3 | BQAwcjELMAkGA1UEBhMCUk8xDTALBgNVBAgMBFRlc3QxDTALBgNVBAcMBFRlc3Qx
4 | DTALBgNVBAoMBFRlc3QxDTALBgNVBAsMBFRlc3QxEjAQBgNVBAMMCTEyNy4wLjAu
5 | MTETMBEGCSqGSIb3DQEJARYEVGVzdDAeFw0yMjAzMjgxNDU5NTdaFw0zMjAzMjUx
6 | NDU5NTdaMHIxCzAJBgNVBAYTAlJPMQ0wCwYDVQQIDARUZXN0MQ0wCwYDVQQHDARU
7 | ZXN0MQ0wCwYDVQQKDARUZXN0MQ0wCwYDVQQLDARUZXN0MRIwEAYDVQQDDAkxMjcu
8 | MC4wLjExEzARBgkqhkiG9w0BCQEWBFRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
9 | DwAwggEKAoIBAQDH2ieSlD9B+FoJdAD1M3SJMNIxfeM2wyw3KWewaxJxd20qL/FX
10 | iBM2v5gRQZnxUdfjXALgz5DCa5zF/eKnDHGepPA2hc7fNLofXvB1qpO9M/Y1Cjvv
11 | 5kABIw1BtNMVaMRmzzkQW1aCMn9mF3qIm8+db7iMN6pNlvvXA1Q44UgZFu1G7lhn
12 | /HSQedb90ScoDOZ2HZnLIdI2sVoby9f6+va48Noqe2XdveA0GGE4Poch1c1l0ljf
13 | frd2mmnQRTg/bXgX5KQ+zEJg9TMTugz+WNrP49fW9s0Sr3/3q8gKG0D5xJrTUh0b
14 | lYBecj9r37wUUMK67IIsHZuAw6kf2GoLf2KVAgMBAAGjUzBRMB0GA1UdDgQWBBQv
15 | xzKASX+752Cpd/Ja7gjZ9lTIcTAfBgNVHSMEGDAWgBQvxzKASX+752Cpd/Ja7gjZ
16 | 9lTIcTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBzcWWvnWtb
17 | v7VlDqxoJIsveaGfKl8vlrvYR/FBskxDEQRjp6ptoDiDDbbTc6fJ9x4WdX003Cvv
18 | mkG1+SnjrpKTm8Sr5DlYPZW61yG/77Is7mLiQUN65NUhud2soTn5ZFst6aZSY0dA
19 | QtubNXoshwKSru7xVwzOBqiwf0kdBNnDxD4p2s88f99B26l6Ze4rw1xrRMgBaA2I
20 | WU0/jqOCkJc6khL0MMjvRGiilTAt4ra+IUf2hAwf6TORbH27CBWACPwL74rNb3Yh
21 | M2SSUK0wxego+3Jvh8JAiViVetPfkWI01Nu64VVB+CJOydTFotPGCSJkc/BF4yJq
22 | ZlzZ1OQJvjZX
23 | -----END CERTIFICATE-----
24 |
--------------------------------------------------------------------------------
/signing-providers/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples",
3 | "version": "0.0.1",
4 | "description": "Examples",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "build": "webpack",
8 | "lint": "eslint . --ext .js",
9 | "lint:fix": "eslint . --ext .js --fix"
10 | },
11 | "author": "MultiversX",
12 | "dependencies": {
13 | "@multiversx/sdk-core": "15.0.0",
14 | "@multiversx/sdk-dapp-utils": "3.0.1",
15 | "@multiversx/sdk-extension-provider": "5.1.1",
16 | "@multiversx/sdk-hw-provider": "8.1.1",
17 | "@multiversx/sdk-native-auth-client": "2.0.1",
18 | "@multiversx/sdk-wallet-connect-provider": "6.1.2",
19 | "@multiversx/sdk-web-wallet-cross-window-provider": "3.2.1",
20 | "@multiversx/sdk-web-wallet-iframe-provider": "3.1.1",
21 | "@multiversx/sdk-web-wallet-provider": "5.1.1",
22 | "@multiversx/sdk-webview-provider": "3.2.1",
23 | "protobufjs": "^7.4.0",
24 | "punycode": "2.3.0",
25 | "qrcode": "1.5.1",
26 | "qs": "6.10.3"
27 | },
28 | "devDependencies": {
29 | "crypto-browserify": "3.12.0",
30 | "eslint": "^8.56.0",
31 | "eslint-config-prettier": "^10.1.5",
32 | "eslint-plugin-prettier": "^5.5.0",
33 | "path-browserify": "1.0.1",
34 | "prettier": "^3.5.3",
35 | "stream-browserify": "3.0.0",
36 | "webpack": "5.94.0",
37 | "webpack-cli": "4.8.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/signing-providers/README.md:
--------------------------------------------------------------------------------
1 | # Examples: using the signing providers
2 |
3 | This example (a simple, frontend-only web application) depicts the usage of different signing providers for dApps, such as:
4 |
5 | - [(Web) Wallet provider](https://github.com/multiversx/mx-sdk-js-web-wallet-provider)
6 | - [(Web) Wallet Cross-Window provider](@multiversx/sdk-web-wallet-cross-window-provider)
7 | - [xAlias](https://github.com/multiversx/mx-sdk-js-web-wallet-provider) - from the perspective of a dApp, this one follows the interface of the Web Wallet provider (above)
8 | - [DeFi Wallet provider](https://github.com/multiversx/mx-sdk-js-extension-provider)
9 | - [Wallet Connect (xPortal) provider](https://github.com/multiversx/mx-sdk-js-wallet-connect-provider)
10 | - [Hardware Wallet (Ledger) provider](https://github.com/multiversx/mx-sdk-js-hw-provider)
11 |
12 | ## Prerequisites
13 |
14 | Make sure you have the package `http-server` installed globally.
15 |
16 | ```bash
17 | npm install --global http-server
18 | ```
19 |
20 | Note that some providers (such as `hw-provider`) have to be used in pages served via HTTPS in order to work properly (a dummy certificate is included here).
21 |
22 | Furthermore, make sure you install the browser extension `MultiversX DeFi Wallet` in advance.
23 |
24 | ## Running the examples
25 |
26 | When you are ready, build the examples:
27 |
28 | ```bash
29 | npm run build
30 | ```
31 |
32 | Start the server (with a HTTPS binding):
33 |
34 | ```bash
35 | http-server -c-1 -S -C ./dummy-certificate.pem -K ./dummy-certificate-key.pem --port=8080
36 | ```
37 |
38 | Afterwards, navigate to [https://localhost:8080/index.html](https://localhost:8080/index.html).
39 |
--------------------------------------------------------------------------------
/signing-providers/iframe-wallet.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Iframe Wallet
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
33 |
36 |
37 |
38 |
39 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/signing-providers/dummy-certificate-key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDH2ieSlD9B+FoJ
3 | dAD1M3SJMNIxfeM2wyw3KWewaxJxd20qL/FXiBM2v5gRQZnxUdfjXALgz5DCa5zF
4 | /eKnDHGepPA2hc7fNLofXvB1qpO9M/Y1Cjvv5kABIw1BtNMVaMRmzzkQW1aCMn9m
5 | F3qIm8+db7iMN6pNlvvXA1Q44UgZFu1G7lhn/HSQedb90ScoDOZ2HZnLIdI2sVob
6 | y9f6+va48Noqe2XdveA0GGE4Poch1c1l0ljffrd2mmnQRTg/bXgX5KQ+zEJg9TMT
7 | ugz+WNrP49fW9s0Sr3/3q8gKG0D5xJrTUh0blYBecj9r37wUUMK67IIsHZuAw6kf
8 | 2GoLf2KVAgMBAAECggEAJwYdJg0WkQ4qnp/tM/P5NHS5Bnr7bA0OTDMkkRlHP6q/
9 | QTadXKcwgUdGLVBu++UsT7P+x+Ef9ibHNQ4PPOk8Ims4kJzuOT11fnyuXXuSX6aO
10 | 0+qMq5p9MvuiMgtaEFslxqF+FgiPytqLb+bzwUsTbj2Lfq277myl/mUjA/xRdLxh
11 | /7yly7YBAoffsUsRu65ek7k/itV7heVCqpgbioJ4KjGpLruh3G8EEID7FfFlafRY
12 | phU9f+xWG9QtZTEBsWDgJhljeFZ64wOHpG3Wokpq17sYWoobykkRN19kgu6ag8ok
13 | 3LC2mkw8Hf56ZdZt2B+RDOQCez1x5O3N/GbO8obKAQKBgQDkiOYmtFPAbvnZHCdN
14 | JMYHFSMw6/hB+QZWho0oc+qAdzX4xE9rvAXOb7/oilyZf1DJdwILCm34NVH4gaVP
15 | BhGD24usTEKMq2w/JhLsLPYYlKyvtNKBCFpg0S7xoyfPT6xfUYfsnvN+50V7R5cG
16 | WyHeJytGKnkkc5UVRzuORWfagQKBgQDf3s6HrL3LTjuXWqgXNk6HefKP/888MZHL
17 | /iyJ6Av8xmeSyvWITcB2Hr7xka+weTb3Co/6MH140LDc0zAMEISsTKP+p4qV5Ewv
18 | e60wOAx+7r0gYPy9vTef7oW4zwh1we1UUO1FklI3/mUIEkNlHQnSGouTCrS/BVib
19 | 7IWBi8N2FQKBgQCXbmciymacomyIAnHAWlelpcn1xsZv4LTkbK/oWDbQ/S0UM/B0
20 | cNhgHAhL7DLDu2sqs+L0seqAh8RTKIUDQgAlITsB5l5Km+RUS8RKHtjLHOj4XJcH
21 | zSMl+DZlAzmD00Viu8GXdxPdyR1vPNbD7WsZq1avXcF79+KXgOXjtfXFAQKBgQCa
22 | kVploKYWNfi9ArHl/O/xaAhK1iN+evcgMmkL+nQ0XcRrPMiUCKLmq0nIvn7gSIDj
23 | xp4r7sji3qwOe53D5q/DytK742+zGEJl0m18SmaOSUW5kl1On9NFEEmxlPhRXckA
24 | GzQHggRUdsfI79sqeAcs0nkl2BF9hjJszbxL+nTFFQKBgQDVl/toy30trOx9vyB+
25 | yA76hyF654Drjjnbhbi1UDkKoVo95H1q8Arnu/VzgEhW/GcfIUAkUAuRlxNK08Et
26 | LjINeDA2blVI9emwGSLRSeuMkpOLTexs5PqaB64A36IPn2YZWRsQ760z0PqlZwPf
27 | G7Or7/WAMMKnmsuXYA2ZOc6w7A==
28 | -----END PRIVATE KEY-----
29 |
--------------------------------------------------------------------------------
/signing-providers/webpack.config.js:
--------------------------------------------------------------------------------
1 | //@ts-check
2 | "use strict";
3 |
4 | //@ts-check
5 | /** @typedef {import('webpack').Configuration} WebpackConfig **/
6 |
7 | const path = require("path");
8 |
9 | const webpack = require("webpack");
10 |
11 | /** @type WebpackConfig */
12 | const config = {
13 | mode: "none",
14 | entry: {
15 | app: "./src/index.js",
16 | },
17 | output: {
18 | filename: "[name].js",
19 | path: path.join(__dirname, "./out"),
20 | libraryTarget: "umd",
21 | },
22 | resolve: {
23 | mainFields: ["browser", "module", "main"],
24 | extensions: [".ts", ".js"], // support ts-files and js-files
25 | alias: {
26 | // provides alternate implementation for node module and source files
27 | },
28 | fallback: {
29 | // Webpack 5 no longer polyfills Node.js core modules automatically.
30 | // see https://webpack.js.org/configuration/resolve/#resolvefallback
31 | // for the list of Node.js core module polyfills.
32 | assert: require.resolve("assert"),
33 | buffer: require.resolve("buffer"),
34 | stream: require.resolve("stream-browserify"),
35 | crypto: require.resolve("crypto-browserify"),
36 | path: require.resolve("path-browserify"),
37 | punycode: require.resolve("punycode"),
38 | fs: false,
39 | },
40 | },
41 | module: {
42 | rules: [],
43 | },
44 | plugins: [
45 | new webpack.ProvidePlugin({
46 | Buffer: ["buffer", "Buffer"],
47 | }),
48 | ],
49 | externals: {},
50 | performance: {
51 | hints: false,
52 | },
53 | devtool: "eval-source-map", // create a source map that points to the original source file
54 | };
55 |
56 | module.exports = [config];
57 |
--------------------------------------------------------------------------------
/codec/decodeCustomType.js:
--------------------------------------------------------------------------------
1 | import { AbiRegistry, BinaryCodec } from "@multiversx/sdk-core";
2 | import * as fs from "fs";
3 | import minimist from "minimist";
4 | import { homedir } from "os";
5 |
6 | async function main() {
7 | const argv = minimist(process.argv.slice(2));
8 | const abiPath = asUserPath(argv.abi);
9 | const type = argv.type;
10 | const encodedData = argv.data;
11 |
12 | if (!abiPath) {
13 | throw new Error("Missing parameter 'abi'! E.g. --abi=~/example.abi.json");
14 | }
15 | if (!fs.existsSync(abiPath)) {
16 | throw new Error(`File not found: ${abiPath}`);
17 | }
18 | if (!type) {
19 | throw new Error("Missing parameter 'type'! E.g. --type=DepositEvent");
20 | }
21 | if (!encodedData) {
22 | throw new Error("Missing parameter 'data'! E.g. --data=00000000000003db000000");
23 | }
24 |
25 | const abiContent = fs.readFileSync(abiPath, { encoding: "utf8" });
26 | const abiObj = JSON.parse(abiContent);
27 | const abiRegistry = AbiRegistry.create(abiObj);
28 | const data = Buffer.from(encodedData, "hex");
29 |
30 | const customType = abiRegistry.customTypes.find((e) => e.getName() == type);
31 | if (!customType) {
32 | throw new Error(`Custom type not found: ${type}`);
33 | }
34 |
35 | const codec = new BinaryCodec();
36 | const decoded = codec.decodeTopLevel(data, customType);
37 | const decodedValue = decoded.valueOf();
38 |
39 | console.log(JSON.stringify(decodedValue, null, 4));
40 | }
41 |
42 | function asUserPath(userPath) {
43 | return (userPath || "").toString().replace("~", homedir);
44 | }
45 |
46 | async function doMain() {
47 | try {
48 | await main();
49 | } catch (error) {
50 | console.error("Error:", error.message);
51 | process.exit(1);
52 | }
53 | }
54 |
55 | await doMain();
56 |
--------------------------------------------------------------------------------
/signing-providers/webview-child.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Examples
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
35 |
38 |
39 |
40 |
41 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/codec/decodeEvent.js:
--------------------------------------------------------------------------------
1 | import { AbiRegistry, ApiNetworkProvider, ResultsParser } from "@multiversx/sdk-core";
2 | import * as fs from "fs";
3 | import minimist from "minimist";
4 | import { homedir } from "os";
5 |
6 | async function main() {
7 | const argv = minimist(process.argv.slice(2));
8 | const abiPath = asUserPath(argv.abi);
9 | const eventIdentifier = argv.event;
10 | const apiUrl = argv.api;
11 | const transactionHash = argv.tx;
12 |
13 | if (!abiPath) {
14 | throw new Error("Missing parameter 'abi'! E.g. --abi=~/example.abi.json");
15 | }
16 | if (!fs.existsSync(abiPath)) {
17 | throw new Error(`File not found: ${abiPath}`);
18 | }
19 | if (!eventIdentifier) {
20 | throw new Error("Missing parameter 'event'! E.g. --event=deposit");
21 | }
22 | if (!apiUrl) {
23 | throw new Error("Missing parameter 'api'! E.g. --api=https://testnet-api.multiversx.com");
24 | }
25 | if (!transactionHash) {
26 | throw new Error("Missing parameter 'tx'! E.g. --tx=532087e5021c9ab8be8a4db5ad843cfe0610761f6334d9693b3765992fd05f67");
27 | }
28 |
29 | const abiContent = fs.readFileSync(abiPath, { encoding: "utf8" });
30 | const abiObj = JSON.parse(abiContent);
31 | const abiRegistry = AbiRegistry.create(abiObj);
32 |
33 | const eventDefinition = abiRegistry.getEvent(eventIdentifier);
34 | const resultsParser = new ResultsParser();
35 |
36 | const provider = new ApiNetworkProvider(apiUrl, { clientName: "multiversx-sdk-js-examples" });
37 | const transaction = await provider.getTransaction(transactionHash);
38 | const event = findTransactionEvent(transaction, eventIdentifier);
39 | if (!event) {
40 | throw new Error(`Event not found: ${eventIdentifier}`);
41 | }
42 |
43 | const outcome = resultsParser.parseEvent(event, eventDefinition);
44 | console.log(JSON.stringify(outcome, null, 4));
45 | }
46 |
47 | function asUserPath(userPath) {
48 | return (userPath || "").toString().replace("~", homedir);
49 | }
50 |
51 | function findTransactionEvent(transaction, eventIdentifier) {
52 | const eventInTransaction = transaction.logs.findFirstOrNoneEvent(eventIdentifier);
53 | if (eventInTransaction) {
54 | return eventInTransaction;
55 | }
56 |
57 | for (const contractResult of transaction.contractResults.items) {
58 | const eventInContractResult = contractResult.logs.findFirstOrNoneEvent(eventIdentifier);
59 | if (eventInContractResult) {
60 | return eventInContractResult;
61 | }
62 | }
63 |
64 | return null;
65 | }
66 |
67 | async function doMain() {
68 | try {
69 | await main();
70 | } catch (error) {
71 | console.error("Error:", error.message);
72 | process.exit(1);
73 | }
74 | }
75 |
76 | await doMain();
77 |
--------------------------------------------------------------------------------
/signing-providers/src/iframe-communication.js:
--------------------------------------------------------------------------------
1 | import { IframeProvider } from "@multiversx/sdk-web-wallet-iframe-provider/out";
2 | import { IframeLoginTypes } from "@multiversx/sdk-web-wallet-iframe-provider/out/constants";
3 | import { CHAIN_ID } from "./config";
4 | import { displayOutcome } from "./helpers";
5 | import { Address, Message, Transaction } from "@multiversx/sdk-core";
6 |
7 | // IMPORTANT: The iframe wallet must be served over HTTPS on different domain
8 | // example: http-server -c-1 -S -C ./dummy-certificate.pem -K ./dummy-certificate-key.pem --port=3000
9 | const IFRAME_WALLET_URL = `https://192.168.50.183:3000/iframe-wallet.html`;
10 |
11 | export class IframeCommunication {
12 | constructor() {
13 | this.provider = IframeProvider.getInstance();
14 | this.address = "";
15 | }
16 |
17 | async init() {
18 | // TODO: change to iframe login type
19 | this.provider.setLoginType(IframeLoginTypes.metamask);
20 | this.provider.setWalletUrl(IFRAME_WALLET_URL);
21 | const isInitialized = await this.provider.init();
22 | return isInitialized;
23 | }
24 |
25 | async login() {
26 | await this.init();
27 | const account = await this.provider.login();
28 | this.address = account.address;
29 | alert(`Address: ${account.address}`);
30 | }
31 |
32 | async logout() {
33 | await this.provider.logout();
34 | this.address = "";
35 | }
36 |
37 | async signTransactions() {
38 | const sender = this.address;
39 | const firstTransaction = new Transaction({
40 | nonce: 42,
41 | value: "1",
42 | sender: new Address(sender),
43 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
44 | gasPrice: 1000000000,
45 | gasLimit: 50000,
46 | data: Buffer.from(""),
47 | chainID: CHAIN_ID,
48 | version: 1,
49 | });
50 |
51 | const secondTransaction = new Transaction({
52 | nonce: 43,
53 | value: "100000000",
54 | sender: new Address(sender),
55 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
56 | gasPrice: 1000000000,
57 | gasLimit: 50000,
58 | data: Buffer.from("hello world"),
59 | chainID: CHAIN_ID,
60 | version: 1,
61 | });
62 |
63 | await this.provider.signTransactions([firstTransaction, secondTransaction]);
64 | console.log("First transaction, upon signing:", firstTransaction);
65 | console.log("Second transaction, upon signing:", secondTransaction);
66 |
67 | alert(JSON.stringify([firstTransaction.toSendable(), secondTransaction.toSendable()], null, 4));
68 | }
69 |
70 | async signMessage() {
71 | const message = new Message({
72 | address: new Address(this.address),
73 | data: Buffer.from("hello"),
74 | });
75 | const signedMessage = await this.provider.signMessage(message);
76 | displayOutcome("Message signed. Signature: ", signedMessage);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/signing-providers/src/extension.js:
--------------------------------------------------------------------------------
1 | import { Address, Message, Transaction, TransactionPayload } from "@multiversx/sdk-core";
2 | import { ExtensionProvider } from "@multiversx/sdk-extension-provider";
3 | import { createNativeAuthInitialPart, packNativeAuthToken, verifyNativeAuthToken } from "./auth";
4 | import { CHAIN_ID } from "./config";
5 | import { displayOutcome } from "./helpers";
6 |
7 | export class Extension {
8 | constructor() {
9 | this.provider = ExtensionProvider.getInstance();
10 | }
11 |
12 | async login() {
13 | await this.provider.init();
14 | const account = await this.provider.login();
15 |
16 | alert(`Address: ${account.address}`);
17 | }
18 |
19 | async loginWithToken() {
20 | await this.provider.init();
21 |
22 | const nativeAuthInitialPart = await createNativeAuthInitialPart();
23 | const account = await this.provider.login({ token: nativeAuthInitialPart });
24 |
25 | const address = account.address;
26 | const signature = account.signature;
27 | const nativeAuthToken = packNativeAuthToken(address, nativeAuthInitialPart, signature);
28 |
29 | verifyNativeAuthToken(nativeAuthToken);
30 | }
31 |
32 | async logout() {
33 | await this.provider.init();
34 | await this.provider.logout();
35 | }
36 |
37 | async signTransaction() {
38 | await this.provider.init();
39 |
40 | const sender = await this.provider.getAddress();
41 | const transaction = new Transaction({
42 | nonce: 42,
43 | value: "1",
44 | sender: new Address(sender),
45 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
46 | gasPrice: 1000000000,
47 | gasLimit: 50000,
48 | data: Buffer.from("hello"),
49 | chainID: CHAIN_ID,
50 | version: 1,
51 | });
52 |
53 | await this.provider.signTransaction(transaction);
54 |
55 | alert(JSON.stringify(transaction.toSendable(), null, 4));
56 | }
57 |
58 | async signTransactions() {
59 | await this.provider.init();
60 |
61 | const sender = await this.provider.getAddress();
62 | const firstTransaction = new Transaction({
63 | nonce: 42,
64 | value: "1",
65 | sender: new Address(sender),
66 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
67 | gasPrice: 1000000000,
68 | gasLimit: 50000,
69 | data: Buffer.from("hello"),
70 | chainID: CHAIN_ID,
71 | version: 1,
72 | });
73 |
74 | const secondTransaction = new Transaction({
75 | nonce: 43,
76 | value: "100000000",
77 | sender: new Address(sender),
78 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
79 | gasPrice: 1000000000,
80 | gasLimit: 50000,
81 | data: Buffer.from("hello world"),
82 | chainID: CHAIN_ID,
83 | version: 1,
84 | });
85 |
86 | await this.provider.signTransactions([firstTransaction, secondTransaction]);
87 | console.log("First transaction, upon signing:", firstTransaction);
88 | console.log("Second transaction, upon signing:", secondTransaction);
89 |
90 | alert(JSON.stringify([firstTransaction.toSendable(), secondTransaction.toSendable()], null, 4));
91 | }
92 |
93 | async signMessage() {
94 | await this.provider.init();
95 |
96 | const address = await this.provider.getAddress();
97 |
98 | const message = new Message({
99 | address: new Address(address),
100 | data: Buffer.from("hello"),
101 | });
102 |
103 | const signedMessage = await this.provider.signMessage(message);
104 |
105 | displayOutcome("Message signed. Signature: ", Buffer.from(signedMessage?.signature).toString("hex"));
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/signing-providers/src/webview.js:
--------------------------------------------------------------------------------
1 | import { Address, Message, Transaction } from "@multiversx/sdk-core";
2 | import { CHAIN_ID } from "./config";
3 | import { WebviewProvider } from "@multiversx/sdk-webview-provider/out/WebviewProvider";
4 |
5 | import { displayOutcome } from "./helpers";
6 |
7 | export const addressOfAlice = new Address("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th");
8 |
9 | export class Webview {
10 | constructor() {
11 | this._provider = WebviewProvider.getInstance({
12 | resetStateCallback: () => console.log("Reset state callback called"),
13 | });
14 | }
15 |
16 | async init() {
17 | return await this._provider.init();
18 | }
19 |
20 | async login() {
21 | const response = await this._provider.login();
22 |
23 | console.log("Login response:" + JSON.stringify(response));
24 | alert("Login response:" + JSON.stringify(response));
25 |
26 | return response;
27 | }
28 |
29 | async logout() {
30 | const response = await this._provider.logout();
31 |
32 | console.log("Logout response:" + JSON.stringify(response));
33 | alert("Logout response:" + JSON.stringify(response));
34 |
35 | return response;
36 | }
37 |
38 | async relogin() {
39 | const accessToken = await this._provider.relogin();
40 |
41 | alert("accessToken = " + JSON.stringify(accessToken));
42 | console.log("accessToken = " + JSON.stringify(accessToken));
43 |
44 | if (!accessToken) {
45 | console.error("Unable to re-login. Missing accessToken.");
46 | alert("Unable to re-login. Missing accessToken.");
47 | return null;
48 | }
49 |
50 | return accessToken;
51 | }
52 |
53 | async signTransaction() {
54 | const transaction = new Transaction({
55 | nonce: 42,
56 | value: "1",
57 | sender: addressOfAlice,
58 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
59 | gasPrice: 1000000000,
60 | gasLimit: 50000,
61 | data: Buffer.from("world"),
62 | chainID: CHAIN_ID,
63 | version: 1,
64 | });
65 |
66 | const response = await this._provider.signTransaction(transaction);
67 |
68 | console.log("Sign transaction response:" + JSON.stringify(response));
69 | alert("Sign transaction response:" + JSON.stringify(response));
70 |
71 | return response;
72 | }
73 |
74 | async signTransactions() {
75 | const transaction = new Transaction({
76 | nonce: 42,
77 | value: "1",
78 | sender: addressOfAlice,
79 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
80 | gasPrice: 1000000000,
81 | gasLimit: 50000,
82 | data: Buffer.from("world"),
83 | chainID: CHAIN_ID,
84 | version: 1,
85 | });
86 |
87 | const response = await this._provider.signTransactions([transaction]);
88 |
89 | if (!response) {
90 | this._provider.cancelAction();
91 | return null;
92 | }
93 |
94 | console.log("Sign transactions response:" + JSON.stringify(response));
95 | alert("Sign transactions response:" + JSON.stringify(response));
96 |
97 | return response;
98 | }
99 |
100 | async signMessage() {
101 | const message = new Message({
102 | address: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
103 | data: Buffer.from("hello"),
104 | });
105 |
106 | const signedMessage = await this._provider.signMessage(message);
107 |
108 | displayOutcome("Message signed. Signature: ", Buffer.from(signedMessage?.signature).toString("hex"));
109 | }
110 |
111 | async cancelAction() {
112 | return await this._provider.cancelAction();
113 | }
114 |
115 | async isInitialized() {
116 | return this._provider.isInitialized();
117 | }
118 |
119 | async isConnected() {
120 | return this._provider.isConnected();
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/signing-providers/src/cross-window.js:
--------------------------------------------------------------------------------
1 | import { Address, Message, Transaction } from "@multiversx/sdk-core";
2 | import { CHAIN_ID, WALLET_URL } from "./config";
3 | import { CrossWindowProvider } from "@multiversx/sdk-web-wallet-cross-window-provider";
4 |
5 | import { createNativeAuthInitialPart, packNativeAuthToken, verifyNativeAuthToken } from "./auth";
6 | import { displayOutcome } from "./helpers";
7 |
8 | const callbackUrl = window.location.href;
9 |
10 | export class CrossWindowWallet {
11 | constructor() {
12 | this._provider = CrossWindowProvider.getInstance();
13 | this._address = "";
14 | }
15 |
16 | async init() {
17 | await CrossWindowProvider.getInstance().init();
18 | this._provider = CrossWindowProvider.getInstance().setWalletUrl(WALLET_URL);
19 | }
20 |
21 | async login() {
22 | await this.init();
23 |
24 | const { address } = await this._provider.login({ callbackUrl });
25 | this._address = address;
26 | console.log("Login response:" + JSON.stringify(address));
27 |
28 | return address;
29 | }
30 |
31 | async loginWithToken() {
32 | await this.init();
33 |
34 | const nativeAuthInitialPart = await createNativeAuthInitialPart();
35 | await this._provider.login({ token: nativeAuthInitialPart, callbackUrl });
36 |
37 | const address = this._provider.account.address;
38 | const signature = this._provider.account.signature;
39 | const nativeAuthToken = packNativeAuthToken(address, nativeAuthInitialPart, signature);
40 | this._address = address;
41 |
42 | verifyNativeAuthToken(nativeAuthToken);
43 | }
44 |
45 | async logout() {
46 | const response = await this._provider.logout();
47 |
48 | displayOutcome("Logout response:", response);
49 |
50 | return response;
51 | }
52 |
53 | async signTransaction() {
54 | const transaction = new Transaction({
55 | nonce: 42,
56 | value: "1",
57 | sender: new Address(this._address),
58 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
59 | gasPrice: 1000000000,
60 | gasLimit: 50000,
61 | data: Uint8Array.from(Buffer.from("hello world")),
62 | chainID: CHAIN_ID,
63 | version: 1,
64 | });
65 |
66 | await this._provider.signTransaction(transaction, {
67 | callbackUrl: encodeURIComponent(callbackUrl),
68 | });
69 |
70 | alert(JSON.stringify(transaction.toSendable(), null, 4));
71 | }
72 |
73 | async signTransactions() {
74 | const sender = this._address;
75 | const firstTransaction = new Transaction({
76 | nonce: 42,
77 | value: "1",
78 | sender: new Address(sender),
79 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
80 | gasPrice: 1000000000,
81 | gasLimit: 50000,
82 | data: Uint8Array.from(Buffer.from("hello once")),
83 | chainID: CHAIN_ID,
84 | version: 1,
85 | });
86 |
87 | const secondTransaction = new Transaction({
88 | nonce: 43,
89 | value: "100000000",
90 | sender: new Address(sender),
91 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
92 | gasPrice: 1000000000,
93 | gasLimit: 50000,
94 | data: Uint8Array.from(Buffer.from("hello twice")),
95 | chainID: CHAIN_ID,
96 | version: 1,
97 | });
98 |
99 | const response = await this._provider.signTransactions([firstTransaction, secondTransaction], {
100 | callbackUrl: encodeURIComponent(callbackUrl),
101 | });
102 |
103 | const plainResponse = response.map((r) => r.toPlainObject());
104 | console.log("First transaction, upon signing:", firstTransaction);
105 | console.log("Second transaction, upon signing:", secondTransaction);
106 | console.log("Response:", plainResponse);
107 |
108 | alert(JSON.stringify(plainResponse, null, 4));
109 | }
110 |
111 | async signMessage() {
112 | await this._provider.init();
113 | const address = this._address;
114 |
115 | const message = new Message({
116 | address: new Address(address),
117 | data: Buffer.from("hello"),
118 | });
119 |
120 | const signedMessage = await this._provider.signMessage(message);
121 |
122 | displayOutcome("Message signed. Signature: ", Buffer.from(signedMessage?.signature).toString("hex"));
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/signing-providers/src/metamask.js:
--------------------------------------------------------------------------------
1 | import { Address, Message, Transaction, TransactionPayload } from "@multiversx/sdk-core";
2 | import { IframeProvider } from "@multiversx/sdk-web-wallet-iframe-provider/out";
3 | import { IframeLoginTypes } from "@multiversx/sdk-web-wallet-iframe-provider/out/constants";
4 |
5 | import { createNativeAuthInitialPart, packNativeAuthToken, verifyNativeAuthToken } from "./auth";
6 | import { CHAIN_ID, METAMASK_SNAP_WALLET_ADDRESS } from "./config.devnet";
7 | import { displayOutcome } from "./helpers";
8 |
9 | const callbackUrl = window.location.href;
10 |
11 | export class Metamask {
12 | constructor() {
13 | this._provider = IframeProvider.getInstance();
14 | this._address = "";
15 | }
16 |
17 | async init() {
18 | this._provider.setLoginType(IframeLoginTypes.metamask);
19 | console.log("Metamask snap wallet address:" + METAMASK_SNAP_WALLET_ADDRESS);
20 |
21 | this._provider.setWalletUrl(METAMASK_SNAP_WALLET_ADDRESS);
22 | await this._provider.init();
23 | }
24 |
25 | async login() {
26 | await this.init();
27 |
28 | await this._provider.login({});
29 |
30 | const { address } = this._provider.account;
31 | this._address = address;
32 | console.log("Login response:" + JSON.stringify(address));
33 |
34 | return address;
35 | }
36 |
37 | async loginWithToken() {
38 | await this.init();
39 |
40 | const nativeAuthInitialPart = await createNativeAuthInitialPart();
41 | await this._provider.login({ token: nativeAuthInitialPart });
42 |
43 | const address = this._provider.account.address;
44 | const signature = this._provider.account.signature;
45 | const nativeAuthToken = packNativeAuthToken(address, nativeAuthInitialPart, signature);
46 | this._address = address;
47 |
48 | verifyNativeAuthToken(nativeAuthToken);
49 | }
50 |
51 | async logout() {
52 | const response = await this._provider.logout();
53 |
54 | console.log("Logout response:" + JSON.stringify(response));
55 |
56 | return response;
57 | }
58 |
59 | async signTransaction() {
60 | const transaction = new Transaction({
61 | nonce: 42,
62 | value: "1",
63 | sender: new Address(this._address),
64 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
65 | gasPrice: 1000000000,
66 | gasLimit: 50000,
67 | data: Buffer.from("hello"),
68 | chainID: CHAIN_ID,
69 | version: 1,
70 | });
71 |
72 | await this._provider.signTransaction(transaction, {
73 | callbackUrl: encodeURIComponent(callbackUrl),
74 | });
75 |
76 | alert(JSON.stringify(transaction.toSendable(), null, 4));
77 | }
78 |
79 | async signTransactions() {
80 | const sender = this._address;
81 | const firstTransaction = new Transaction({
82 | nonce: 42,
83 | value: "1",
84 | sender: new Address(sender),
85 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
86 | gasPrice: 1000000000,
87 | gasLimit: 150000,
88 | data: Buffer.from("hello once"),
89 | chainID: CHAIN_ID,
90 | version: 1,
91 | });
92 |
93 | const secondTransaction = new Transaction({
94 | nonce: 43,
95 | value: "100000000",
96 | sender: new Address(sender),
97 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
98 | gasPrice: 1000000000,
99 | gasLimit: 150000,
100 | data: Buffer.from("hello twice"),
101 | chainID: CHAIN_ID,
102 | version: 1,
103 | });
104 |
105 | const response = await this._provider.signTransactions([firstTransaction, secondTransaction], {
106 | callbackUrl: encodeURIComponent(callbackUrl),
107 | });
108 | console.log("First transaction, upon signing:", firstTransaction);
109 | console.log("Second transaction, upon signing:", secondTransaction);
110 |
111 | const plainResponse = response.map((r) => r.toPlainObject());
112 | console.log("Response:", plainResponse);
113 |
114 | alert(JSON.stringify(plainResponse, null, 4));
115 | }
116 |
117 | async signMessage() {
118 | await this._provider.init();
119 |
120 | const message = new Message({
121 | address: new Address(this._address),
122 | data: Buffer.from("hello"),
123 | });
124 |
125 | const signedMessage = await this._provider.signMessage(message);
126 |
127 | displayOutcome("Message signed. Signature: ", Buffer.from(signedMessage?.signature).toString("hex"));
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/contracts/example.abi.json:
--------------------------------------------------------------------------------
1 | {
2 | "endpoints": [
3 | {
4 | "name": "deposit",
5 | "inputs": [
6 | {
7 | "name": "to",
8 | "type": "Address"
9 | },
10 | {
11 | "name": "opt_transfer_data",
12 | "type": "optional",
13 | "multi_arg": true
14 | }
15 | ],
16 | "outputs": []
17 | }
18 | ],
19 | "events": [
20 | {
21 | "identifier": "deposit",
22 | "inputs": [
23 | {
24 | "name": "dest_address",
25 | "type": "Address",
26 | "indexed": true
27 | },
28 | {
29 | "name": "tokens",
30 | "type": "List",
31 | "indexed": true
32 | },
33 | {
34 | "name": "event_data",
35 | "type": "DepositEvent"
36 | }
37 | ]
38 | }
39 | ],
40 | "types": {
41 | "DepositEvent": {
42 | "type": "struct",
43 | "fields": [
44 | {
45 | "name": "tx_nonce",
46 | "type": "u64"
47 | },
48 | {
49 | "name": "opt_function",
50 | "type": "Option"
51 | },
52 | {
53 | "name": "opt_arguments",
54 | "type": "Option>"
55 | },
56 | {
57 | "name": "opt_gas_limit",
58 | "type": "Option"
59 | }
60 | ]
61 | },
62 | "EsdtTokenPayment": {
63 | "type": "struct",
64 | "fields": [
65 | {
66 | "name": "token_identifier",
67 | "type": "TokenIdentifier"
68 | },
69 | {
70 | "name": "token_nonce",
71 | "type": "u64"
72 | },
73 | {
74 | "name": "amount",
75 | "type": "BigUint"
76 | }
77 | ]
78 | },
79 | "TransferData": {
80 | "type": "struct",
81 | "fields": [
82 | {
83 | "name": "gas_limit",
84 | "type": "u64"
85 | },
86 | {
87 | "name": "function",
88 | "type": "bytes"
89 | },
90 | {
91 | "name": "args",
92 | "type": "List"
93 | }
94 | ]
95 | },
96 | "Reward": {
97 | "type": "struct",
98 | "fields": [
99 | {
100 | "name": "reward_type",
101 | "type": "RewardType"
102 | },
103 | {
104 | "name": "reward_token_id",
105 | "type": "EgldOrEsdtTokenIdentifier"
106 | },
107 | {
108 | "name": "value",
109 | "type": "BigUint"
110 | },
111 | {
112 | "name": "description",
113 | "type": "bytes"
114 | },
115 | {
116 | "name": "percentage_chance",
117 | "type": "u64"
118 | },
119 | {
120 | "name": "epochs_cooldown",
121 | "type": "u64"
122 | }
123 | ]
124 | },
125 | "RewardType": {
126 | "type": "enum",
127 | "variants": [
128 | {
129 | "name": "None",
130 | "discriminant": 0
131 | },
132 | {
133 | "name": "ExperiencePoints",
134 | "discriminant": 1
135 | },
136 | {
137 | "name": "MysteryBox",
138 | "discriminant": 2
139 | },
140 | {
141 | "name": "SFT",
142 | "discriminant": 3
143 | },
144 | {
145 | "name": "PercentValue",
146 | "discriminant": 4
147 | },
148 | {
149 | "name": "FixedValue",
150 | "discriminant": 5
151 | },
152 | {
153 | "name": "CustomReward",
154 | "discriminant": 6
155 | }
156 | ]
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/signing-providers/src/wallet-connect-v2.js:
--------------------------------------------------------------------------------
1 | import { Address, Message, Transaction } from "@multiversx/sdk-core";
2 | import { WalletConnectV2Provider } from "@multiversx/sdk-wallet-connect-provider";
3 | import QRCode from "qrcode";
4 | import { createNativeAuthInitialPart, packNativeAuthToken, verifyNativeAuthToken } from "./auth";
5 | import { CHAIN_ID, WALLET_CONNECT_PROJECT_ID, WALLET_CONNECT_RELAY_URL } from "./config";
6 | import { displayOutcome } from "./helpers";
7 |
8 | export class WalletConnectV2 {
9 | constructor() {
10 | this.provider = new WalletConnectV2Provider(
11 | this.prepareCallbacks(),
12 | CHAIN_ID,
13 | WALLET_CONNECT_RELAY_URL,
14 | WALLET_CONNECT_PROJECT_ID,
15 | );
16 | }
17 |
18 | prepareCallbacks() {
19 | const self = this;
20 |
21 | return {
22 | onClientLogin: async function () {
23 | closeModal();
24 | const address = self.provider.getAddress();
25 | alert(`onClientLogin(), address: ${address}`);
26 | },
27 | onClientLogout: function () {
28 | alert("onClientLogout()");
29 | },
30 | onClientEvent: function (event) {
31 | alert("onClientEvent()", event);
32 | },
33 | };
34 | }
35 |
36 | async login() {
37 | await this.provider.init();
38 | const { uri, approval } = await this.provider.connect();
39 |
40 | await openModal(uri);
41 |
42 | try {
43 | await this.provider.login({ approval });
44 | } catch (err) {
45 | console.log(err);
46 | alert("Connection Proposal Refused");
47 | }
48 | }
49 |
50 | async loginWithToken() {
51 | await this.provider.init();
52 | const nativeAuthInitialPart = await createNativeAuthInitialPart();
53 | const { uri, approval } = await this.provider.connect();
54 |
55 | await openModal(uri);
56 |
57 | try {
58 | const account = await this.provider.login({
59 | approval,
60 | token: nativeAuthInitialPart,
61 | });
62 |
63 | const address = account.address;
64 | const signature = account.signature;
65 | const nativeAuthToken = packNativeAuthToken(address, nativeAuthInitialPart, signature);
66 |
67 | verifyNativeAuthToken(nativeAuthToken);
68 | } catch (err) {
69 | console.log(err);
70 | alert("Rejected by user");
71 | }
72 | }
73 |
74 | async logout() {
75 | await this.provider.init();
76 | await this.provider.logout();
77 | }
78 |
79 | async signTransaction() {
80 | await this.provider.init();
81 |
82 | const sender = this.provider.getAddress();
83 | const transaction = new Transaction({
84 | nonce: 42,
85 | value: "1",
86 | sender: new Address(sender),
87 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
88 | gasPrice: 1000000000,
89 | gasLimit: 50000,
90 | data: Buffer.from("hello"),
91 | chainID: CHAIN_ID,
92 | version: 1,
93 | });
94 |
95 | await this.provider.signTransaction(transaction);
96 |
97 | alert(JSON.stringify(transaction.toSendable(), null, 4));
98 | }
99 |
100 | async signTransactions() {
101 | await this.provider.init();
102 |
103 | const sender = this.provider.getAddress();
104 | const firstTransaction = new Transaction({
105 | nonce: 43,
106 | value: "1",
107 | sender: new Address(sender),
108 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
109 | gasPrice: 1000000000,
110 | gasLimit: 50000,
111 | data: Buffer.from("hello"),
112 | chainID: CHAIN_ID,
113 | version: 1,
114 | });
115 |
116 | const secondTransaction = new Transaction({
117 | nonce: 44,
118 | value: "100000000",
119 | sender: new Address(sender),
120 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
121 | gasPrice: 1000000000,
122 | gasLimit: 50000,
123 | data: Buffer.from("hello world"),
124 | chainID: CHAIN_ID,
125 | version: 1,
126 | });
127 |
128 | const transactions = [firstTransaction, secondTransaction];
129 | await this.provider.signTransactions(transactions);
130 |
131 | alert(JSON.stringify([firstTransaction.toSendable(), secondTransaction.toSendable()], null, 4));
132 | }
133 |
134 | async signMessage() {
135 | await this.provider.init();
136 | const address = this.provider.getAddress();
137 |
138 | const message = new Message({
139 | address: new Address(address),
140 | data: Buffer.from("hello"),
141 | });
142 |
143 | const signedMessage = await this.provider.signMessage(message);
144 |
145 | displayOutcome("Message signed. Signature: ", Buffer.from(signedMessage?.signature).toString("hex"));
146 | }
147 | }
148 |
149 | async function openModal(connectorUri) {
150 | const svg = await QRCode.toString(connectorUri, { type: "svg" });
151 |
152 | window.$("#MyWalletConnectV2QRContainer").html(svg);
153 | window.$("#MyWalletConnectV2Modal").modal("show");
154 | }
155 |
156 | function closeModal() {
157 | window.$("#MyWalletConnectV2Modal").modal("hide");
158 | }
159 |
--------------------------------------------------------------------------------
/signing-providers/src/iframe-wallet.js:
--------------------------------------------------------------------------------
1 | import {
2 | WindowProviderRequestEnums,
3 | WindowProviderResponseEnums,
4 | SignMessageStatusEnum,
5 | } from "@multiversx/sdk-web-wallet-cross-window-provider/out/enums";
6 | import { Address, Message, Transaction } from "@multiversx/sdk-core";
7 | import { ExtensionProvider } from "@multiversx/sdk-extension-provider";
8 |
9 | function getEventOrigin(event) {
10 | return event.origin || event.originalEvent.origin;
11 | }
12 |
13 | export class IframeWallet {
14 | constructor() {
15 | this._handshakeEstablished = false;
16 | this._isIframe = window.self !== window.top;
17 | this.provider = ExtensionProvider.getInstance();
18 | window.addEventListener("message", this.messageListener.bind(this));
19 | window.addEventListener("beforeunload", this.closeHandshake.bind(this));
20 | this.replyToDapp({
21 | type: WindowProviderResponseEnums.handshakeResponse,
22 | data: "",
23 | });
24 | }
25 |
26 | async login() {
27 | await this.provider.init();
28 | this.replyToDapp({
29 | type: WindowProviderResponseEnums.handshakeResponse,
30 | data: "",
31 | });
32 | const account = await this.provider.login();
33 |
34 | this.replyToDapp({
35 | type: WindowProviderResponseEnums.loginResponse,
36 | data: {
37 | address: account.address,
38 | signature: account.signature,
39 | },
40 | });
41 | }
42 |
43 | async logout() {
44 | await this.provider.init();
45 | await this.provider.logout();
46 | this.replyToDapp({
47 | type: WindowProviderResponseEnums.disconnectResponse,
48 | data: {},
49 | });
50 | }
51 |
52 | async signMessage(payload) {
53 | const address = await this.provider.getAddress();
54 |
55 | const message = new Message({
56 | address: new Address(address),
57 | data: Buffer.from(payload.message),
58 | });
59 |
60 | const signedMessage = await this.provider.signMessage(message);
61 |
62 | this.replyToDapp({
63 | type: WindowProviderResponseEnums.signMessageResponse,
64 | data: {
65 | signature: Buffer.from(signedMessage?.signature).toString("hex"),
66 | status: SignMessageStatusEnum.signed,
67 | },
68 | });
69 | }
70 |
71 | closeHandshake() {
72 | this._handshakeEstablished = false;
73 | this.replyWithCancelled();
74 | this.replyToDapp({
75 | type: WindowProviderResponseEnums.handshakeResponse,
76 | data: "",
77 | });
78 | }
79 |
80 | replyWithCancelled() {
81 | this.replyToDapp({
82 | type: WindowProviderResponseEnums.cancelResponse,
83 | data: { address: "" },
84 | });
85 | }
86 |
87 | /**
88 | * @param {MessageEvent} event
89 | */
90 | async messageListener(event) {
91 | const callbackUrl = getEventOrigin(event);
92 | const isFromSelf = callbackUrl === window.location.origin;
93 |
94 | if (isFromSelf) {
95 | return;
96 | }
97 |
98 | const { type, payload } = event.data;
99 |
100 | const isHandshakeEstablished =
101 | type === WindowProviderRequestEnums.finalizeHandshakeRequest ||
102 | // handshake must be established for all other requests
103 | this._handshakeEstablished;
104 |
105 | if (!isHandshakeEstablished && !this._isIframe) {
106 | if (window.opener) {
107 | console.error("Handshake could not be established.");
108 | }
109 |
110 | return;
111 | }
112 |
113 | switch (type) {
114 | case WindowProviderRequestEnums.loginRequest: {
115 | await this.login();
116 | break;
117 | }
118 |
119 | case WindowProviderRequestEnums.signMessageRequest: {
120 | await this.signMessage(payload);
121 | break;
122 | }
123 |
124 | case WindowProviderRequestEnums.signTransactionsRequest: {
125 | const transactions = payload.map((plainTransactionObject) =>
126 | Transaction.newFromPlainObject(plainTransactionObject),
127 | );
128 |
129 | const signedTransactions = await this.provider.signTransactions(transactions);
130 |
131 | this.replyToDapp({
132 | type: WindowProviderResponseEnums.signTransactionsResponse,
133 | data: signedTransactions.map((transaction) => transaction.toPlainObject()),
134 | });
135 | break;
136 | }
137 |
138 | case WindowProviderResponseEnums.cancelResponse:
139 | case WindowProviderRequestEnums.cancelAction: {
140 | this.replyWithCancelled();
141 | break;
142 | }
143 |
144 | case WindowProviderRequestEnums.finalizeHandshakeRequest: {
145 | this._handshakeEstablished = true;
146 | this.replyToDapp({
147 | type: WindowProviderResponseEnums.finalizeHandshakeResponse,
148 | data: { handshakeSession: "" },
149 | });
150 | break;
151 | }
152 |
153 | case WindowProviderRequestEnums.logoutRequest: {
154 | await this.logout();
155 | break;
156 | }
157 |
158 | default:
159 | break;
160 | }
161 | }
162 |
163 | /**
164 | * @param {Object} props
165 | * @param {WindowProviderResponseEnums} props.type
166 | * @param {Object} props.data
167 | */
168 | async replyToDapp(props) {
169 | const target = window.opener ?? window.parent;
170 |
171 | target.postMessage(
172 | {
173 | type: props.type,
174 | payload: {
175 | data: props.data,
176 | },
177 | },
178 | "*",
179 | );
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/wallet/basic.js:
--------------------------------------------------------------------------------
1 | const { Account, Address, Message, Transaction, MessageComputer, Mnemonic, UserSigner, UserVerifier, UserSecretKey, TransactionComputer } = require("@multiversx/sdk-core");
2 | const axios = require("axios");
3 |
4 | // https://github.com/multiversx/mx-sdk-testwallets/blob/main/users/mnemonic.txt
5 | const DummyMnemonic = "moral volcano peasant pass circle pen over picture flat shop clap goat never lyrics gather prepare woman film husband gravity behind test tiger improve";
6 | const APIUrl = "https://devnet-api.multiversx.com";
7 |
8 | module.exports.exampleDeriveAccountsFromMnemonic = function () {
9 | const mnemonic = Mnemonic.fromString(DummyMnemonic);
10 |
11 | // https://github.com/multiversx/mx-sdk-js-wallet/blob/main/src/users.spec.ts
12 | const addressIndexOfAlice = 0;
13 | const userSecretKeyOfAlice = mnemonic.deriveKey(addressIndexOfAlice);
14 | const userPublicKeyOfAlice = userSecretKeyOfAlice.generatePublicKey();
15 | const addressOfAlice = userPublicKeyOfAlice.toAddress();
16 | const addressOfAliceAsBech32 = addressOfAlice.toBech32();
17 |
18 | const addressIndexOfBob = 1;
19 | const userSecretKeyOfBob = mnemonic.deriveKey(addressIndexOfBob);
20 | const userPublicKeyOfBob = userSecretKeyOfBob.generatePublicKey();
21 | const addressOfBob = userPublicKeyOfBob.toAddress();
22 | const addressOfBobAsBech32 = addressOfBob.toBech32();
23 |
24 | console.log("Alice", addressOfAliceAsBech32);
25 | console.log("Bob", addressOfBobAsBech32);
26 | };
27 |
28 | module.exports.exampleSignAndBroadcastTransaction = async function () {
29 | const mnemonic = Mnemonic.fromString(DummyMnemonic);
30 |
31 | const userSecretKey = mnemonic.deriveKey(0);
32 | const userPublicKey = userSecretKey.generatePublicKey();
33 | const address = userPublicKey.toAddress();
34 | const signer = new UserSigner(userSecretKey);
35 |
36 | // https://docs.multiversx.com/integrators/creating-transactions/#nonce-management
37 | const nonce = await recallAccountNonce(address);
38 |
39 | // https://docs.multiversx.com/sdk-and-tools/sdk-js/sdk-js-cookbook/#preparing-a-simple-transaction
40 | const data = "for the lunch";
41 | const transaction = new Transaction({
42 | nonce: nonce,
43 | // 0.123456789000000000 EGLD
44 | value: 123456789000000000n,
45 | sender: address,
46 | receiver: new Address("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"),
47 | data: Buffer.from(data),
48 | gasPrice: 1000000000,
49 | gasLimit: 500000n,
50 | chainID: "D"
51 | });
52 |
53 |
54 | const transactionComputer = new TransactionComputer();
55 | const serializedTransaction = transactionComputer.computeBytesForSigning(transaction);
56 | const signature = await signer.sign(serializedTransaction);
57 | transaction.signature = signature;
58 |
59 | console.log("Transaction signature", transaction.signature.toString());
60 | console.log("Transaction hash", transaction.txHash);
61 |
62 | console.log("Data to broadcast:");
63 | console.log(transaction);
64 |
65 | await broadcastTransaction(transaction);
66 | };
67 |
68 | async function recallAccountNonce(address) {
69 | const url = `${APIUrl}/accounts/${address.toString()}`;
70 | const response = await axios.get(url);
71 | return response.data.nonce;
72 | }
73 |
74 | async function broadcastTransaction(transaction) {
75 | const url = `${APIUrl}/transactions`;
76 | const data = transaction.toSendable();
77 |
78 | const response = await axios.post(url, data, {
79 | headers: {
80 | "Content-Type": "application/json",
81 | },
82 | });
83 |
84 | console.log(response.data);
85 | }
86 |
87 | module.exports.exampleSignMessage = async function () {
88 | const mnemonic = Mnemonic.fromString(DummyMnemonic);
89 | const userSecretKey = mnemonic.deriveKey(0);
90 | const userPublicKey = userSecretKey.generatePublicKey();
91 | const address = userPublicKey.toAddress().toBech32();
92 | const signer = new UserSigner(userSecretKey);
93 |
94 | const dataExample = `${address}hello{}`;
95 | const message = new Message({
96 | data: Buffer.from(dataExample),
97 | address: address
98 | });
99 |
100 | const messageComputer = new MessageComputer();
101 | const serializedMessage = messageComputer.computeBytesForSigning(message);
102 | const signature = await signer.sign(serializedMessage);
103 | message.signature = signature;
104 |
105 | console.log("Message signature", message.signature);
106 |
107 | // In order to validate a message signature, follow:
108 | // https://docs.multiversx.com/sdk-and-tools/sdk-js/sdk-js-signing-providers/#verifying-the-signature-of-a-login-token
109 | };
110 |
111 | module.exports.exampleVerifyMessage = async function () {
112 | let signer = new Account(
113 | UserSecretKey.fromString("1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf"),
114 | );
115 | let verifier = new UserVerifier(
116 | UserSecretKey.fromString(
117 | "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf",
118 | ).generatePublicKey(),
119 | );
120 | const messageComputer = new MessageComputer();
121 | const dataExample = `hello`;
122 | const message = new Message({
123 | data: Buffer.from(dataExample),
124 | address: signer.address,
125 | });
126 | message.signature = await signer.signMessage(message);
127 |
128 | const serializedMessage = messageComputer.computeBytesForSigning(message);
129 | const signature = message.signature;
130 |
131 | console.log("verify() with good signature:", await verifier.verify(serializedMessage, signature));
132 |
133 | message.data = Buffer.from("bye");
134 | const serializedMessageAltered = messageComputer.computeBytesForSigning(message);
135 | console.log("verify() with bad signature (message altered):", await verifier.verify(serializedMessageAltered, signature));
136 | };
137 |
138 | module.exports.exampleVerifyTransactionSignature = async function () {
139 | let signer = new Account(
140 | UserSecretKey.fromString("1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf"),
141 | );
142 | let verifier = new UserVerifier(
143 | UserSecretKey.fromString(
144 | "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf",
145 | ).generatePublicKey(),
146 | );
147 | const transactionComputer = new TransactionComputer();
148 | const transaction = new Transaction({
149 | nonce: 8n,
150 | value: 10000000000000000000n,
151 | sender: Address.newFromBech32("erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz"),
152 | receiver: Address.newFromBech32("erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r"),
153 | gasPrice: 1000000000n,
154 | gasLimit: 50000n,
155 | chainID: "1",
156 | });
157 |
158 | const serialized = transactionComputer.computeBytesForSigning(transaction);
159 | const signature = await signer.sign(serialized);
160 | console.log("verify() with good signature:", await verifier.verify(serialized, signature));
161 |
162 | transaction.nonce = 7n;
163 | const serializedAlteredTransaction = transactionComputer.computeBytesForSigning(transaction);
164 | console.log("verify() with bad signature (message altered):", await verifier.verify(serializedAlteredTransaction, signature));
165 | };
166 |
--------------------------------------------------------------------------------
/signing-providers/src/hw.js:
--------------------------------------------------------------------------------
1 | import {
2 | Address,
3 | ApiNetworkProvider,
4 | Message,
5 | ProxyNetworkProvider,
6 | Transaction,
7 | TransactionOptions,
8 | } from "@multiversx/sdk-core";
9 | import { HWProvider } from "@multiversx/sdk-hw-provider";
10 | import { CrossWindowProvider } from "@multiversx/sdk-web-wallet-cross-window-provider";
11 | import { WalletProvider } from "@multiversx/sdk-web-wallet-provider";
12 | import { createNativeAuthInitialPart, packNativeAuthToken, verifyNativeAuthToken } from "./auth";
13 | import { API_URL, WALLET_PROVIDER_URL, CHAIN_ID, PROXY_URL } from "./config";
14 | import { displayOutcome } from "./helpers";
15 |
16 | export class HW {
17 | constructor() {
18 | this.hwProvider = new HWProvider();
19 | this.walletProvider = new WalletProvider(WALLET_PROVIDER_URL);
20 | this.apiNetworkProvider = new ApiNetworkProvider(API_URL, {
21 | clientName: "multiversx-sdk-js-examples",
22 | });
23 |
24 | this.proxyNetworkProvider = new ProxyNetworkProvider(PROXY_URL, {
25 | clientName: "multiversx-sdk-js-examples",
26 | });
27 | }
28 |
29 | async login() {
30 | await this.hwProvider.init();
31 |
32 | const addressIndex = parseInt(document.getElementById("addressIndexForLogin").value);
33 | console.log("AddressIndex", addressIndex);
34 |
35 | await this.hwProvider.login({ addressIndex: addressIndex });
36 |
37 | const address = await this.hwProvider.getAddress();
38 |
39 | displayOutcome("Logged in. Address:", address);
40 | }
41 |
42 | async loginWithToken() {
43 | await this.hwProvider.init();
44 |
45 | const addressIndex = parseInt(document.getElementById("addressIndexForLogin").value);
46 | console.log("AddressIndex", addressIndex);
47 |
48 | const nativeAuthInitialPart = await createNativeAuthInitialPart();
49 |
50 | const { address, signature } = await this.hwProvider.tokenLogin({
51 | addressIndex: addressIndex,
52 | token: Buffer.from(nativeAuthInitialPart),
53 | });
54 |
55 | const nativeAuthToken = packNativeAuthToken(address, nativeAuthInitialPart, signature.toString("hex"));
56 | verifyNativeAuthToken(nativeAuthToken);
57 | }
58 |
59 | async displayAddresses() {
60 | await this.hwProvider.init();
61 |
62 | const addresses = await this.hwProvider.getAccounts();
63 | alert(addresses.join(",\n"));
64 | }
65 |
66 | async setAddressIndex() {
67 | await this.hwProvider.init();
68 |
69 | const addressIndex = parseInt(document.getElementById("addressIndexForSetAddress").value);
70 | console.log("Set addressIndex", addressIndex);
71 |
72 | await this.hwProvider.setAddressIndex(addressIndex);
73 |
74 | displayOutcome(`Address has been set: ${await this.hwProvider.getAddress()}.`);
75 | }
76 |
77 | async signTransaction() {
78 | await this.hwProvider.init();
79 |
80 | const senderBech32 = await this.hwProvider.getAddress();
81 | const sender = new Address(senderBech32);
82 | const guardian = await this.getGuardian(sender);
83 |
84 | const transactionOptions = guardian ? TransactionOptions.withOptions({ guarded: true }) : undefined;
85 |
86 | const transaction = new Transaction({
87 | nonce: 42,
88 | value: "1",
89 | gasLimit: 70000,
90 | sender: sender,
91 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
92 | data: Buffer.from("hello"),
93 | chainID: CHAIN_ID,
94 | guardian: guardian,
95 | options: transactionOptions,
96 | });
97 |
98 | const signedTransaction = await this.hwProvider.signTransaction(transaction);
99 |
100 | if (guardian) {
101 | const guardedTransactions = await this.guardTransactions([signedTransaction]);
102 | displayOutcome(
103 | "Transaction signed & guarded.",
104 | JSON.stringify(guardedTransactions.map((tx) => tx.toSendable())),
105 | );
106 | } else {
107 | displayOutcome("Transaction signed.", signedTransaction.toSendable());
108 | }
109 | }
110 |
111 | async signTransactions() {
112 | await this.hwProvider.init();
113 |
114 | const senderBech32 = await this.hwProvider.getAddress();
115 | const sender = new Address(senderBech32);
116 | const guardian = await this.getGuardian(sender);
117 | const transactionOptions = guardian ? TransactionOptions.withOptions({ guarded: true }) : undefined;
118 |
119 | const firstTransaction = new Transaction({
120 | nonce: 42,
121 | value: "1",
122 | sender: sender,
123 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
124 | gasPrice: 1000000000,
125 | gasLimit: 50000,
126 | data: Buffer.from("hello"),
127 | chainID: CHAIN_ID,
128 | guardian: guardian,
129 | options: transactionOptions,
130 | });
131 |
132 | const secondTransaction = new Transaction({
133 | nonce: 43,
134 | value: "100000000",
135 | sender: sender,
136 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
137 | gasPrice: 1000000000,
138 | gasLimit: 50000,
139 | data: Buffer.from("hello world"),
140 | chainID: CHAIN_ID,
141 | guardian: guardian,
142 | options: transactionOptions,
143 | });
144 |
145 | const transactions = [firstTransaction, secondTransaction];
146 | const signedTransactions = await this.hwProvider.signTransactions(transactions);
147 |
148 | if (guardian) {
149 | const guardedTransactions = await this.guardTransactions(signedTransactions);
150 | displayOutcome(
151 | "Transactions signed & guarded.",
152 | JSON.stringify(guardedTransactions.map((tx) => tx.toSendable())),
153 | );
154 | } else {
155 | displayOutcome(
156 | "Transactions signed.",
157 | signedTransactions.map((transaction) => transaction.toSendable()),
158 | );
159 | }
160 | }
161 |
162 | async getGuardian(sender) {
163 | const guardianData = await this.proxyNetworkProvider.getGuardianData(sender);
164 | return guardianData.getCurrentGuardianAddress();
165 | }
166 |
167 | async showSignedTransactionsWhenGuarded() {
168 | const plainSignedTransactions = this.walletProvider.getTransactionsFromWalletUrl();
169 | const signedTransactions = [];
170 |
171 | // Now let's convert them back to sdk-js' Transaction objects.
172 | // Note that the Web Wallet provider returns the data field as a plain string.
173 | // However, sdk-js' Transaction.newFromPlainObject expects it to be base64-encoded.
174 | // Therefore, we need to apply a workaround (an additional conversion).
175 | for (const plainTransaction of plainSignedTransactions) {
176 | const plainTransactionClone = structuredClone(plainTransaction);
177 | plainTransactionClone.data = Buffer.from(plainTransactionClone.data).toString("base64");
178 | const transaction = Transaction.newFromPlainObject(plainTransactionClone);
179 | signedTransactions.push(transaction);
180 | }
181 |
182 | displayOutcome(
183 | "Transactions signed.",
184 | signedTransactions.map((transaction) => transaction.toSendable()),
185 | );
186 | }
187 |
188 | async signMessage() {
189 | await this.hwProvider.init();
190 | const address = await this.hwProvider.getAddress();
191 |
192 | const message = new Message({
193 | address: new Address(address),
194 | data: Buffer.from("hello"),
195 | });
196 |
197 | const signedMessage = await this.hwProvider.signMessage(message);
198 |
199 | displayOutcome("Message signed. Signature: ", Buffer.from(signedMessage?.signature).toString("hex"));
200 | }
201 |
202 | async guardTransactions(transactions) {
203 | // instantiate wallet cross-window provider
204 | await CrossWindowProvider.getInstance().init();
205 | const crossWindowProvider = CrossWindowProvider.getInstance();
206 | crossWindowProvider.setWalletUrl(WALLET_PROVIDER_URL);
207 |
208 | // set sender
209 | const senderBech32 = await this.hwProvider.getAddress();
210 | const sender = new Address(senderBech32);
211 | crossWindowProvider.setAddress(sender);
212 |
213 | // the user signs transactions on ledger so we need to perform an extra
214 | // user action so the popup is opened
215 | crossWindowProvider.setShouldShowConsentPopup(true);
216 |
217 | const guardedTransactions = await crossWindowProvider.guardTransactions(transactions);
218 |
219 | return guardedTransactions;
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/signing-providers/src/web-wallet.js:
--------------------------------------------------------------------------------
1 | import { Address, AddressComputer, ApiNetworkProvider, Message, Transaction } from "@multiversx/sdk-core";
2 | import { WalletProvider } from "@multiversx/sdk-web-wallet-provider";
3 | import qs from "qs";
4 | import { createNativeAuthInitialPart, packNativeAuthToken, verifyNativeAuthToken } from "./auth";
5 | import { API_URL, CHAIN_ID, WALLET_PROVIDER_URL } from "./config";
6 | import { displayOutcome } from "./helpers";
7 |
8 | export class WebWallet {
9 | constructor() {
10 | this.provider = new WalletProvider(WALLET_PROVIDER_URL);
11 | this.apiNetworkProvider = new ApiNetworkProvider(API_URL, {
12 | clientName: "multiversx-sdk-js-examples",
13 | });
14 | this._address = "";
15 | }
16 |
17 | async login() {
18 | const callbackUrl = getCurrentLocation();
19 | await this.provider.login({ callbackUrl: callbackUrl });
20 | }
21 |
22 | async loginWithToken() {
23 | const nativeAuthInitialPart = await createNativeAuthInitialPart();
24 | // This is just an example of how to store the "nativeAuthInitialPart" in-between page changes & redirects (in "localStorage").
25 | // In real-life, use the approach that best suits your application.
26 | localStorage.setItem("web-wallet-example:nativeAuthInitialPart", nativeAuthInitialPart);
27 | const callbackUrl = getCurrentLocation();
28 | await this.provider.login({
29 | callbackUrl: callbackUrl,
30 | token: nativeAuthInitialPart,
31 | });
32 | }
33 |
34 | async logout() {
35 | const callbackUrl = getCurrentLocation();
36 | await this.provider.logout({
37 | callbackUrl: callbackUrl,
38 | redirectDelayMilliseconds: 10,
39 | });
40 | }
41 |
42 | async showAddress() {
43 | const address = getUrlParams().address;
44 | this._address = address;
45 | displayOutcome(address ? "Address: " : "Error: ", address ? address : "Try to login first.");
46 | }
47 |
48 | async showTokenSignature() {
49 | const signature = getUrlParams().signature;
50 | displayOutcome(
51 | signature ? "Signature: " : "Error: ",
52 | signature ? signature : "Try to login (with token) first.",
53 | );
54 | }
55 |
56 | async validateTokenSignature() {
57 | const address = getUrlParams().address;
58 | const nativeAuthInitialPart = await localStorage.getItem("web-wallet-example:nativeAuthInitialPart");
59 | const signature = getUrlParams().signature;
60 | const nativeAuthToken = packNativeAuthToken(address, nativeAuthInitialPart, signature);
61 |
62 | verifyNativeAuthToken(nativeAuthToken);
63 | }
64 |
65 | async signTransaction() {
66 | const sender = getUrlParams().address;
67 | if (!sender) {
68 | displayOutcome("Try to login first.");
69 | return;
70 | }
71 |
72 | const senderNonce = await this.recallNonce(sender);
73 |
74 | const transaction = new Transaction({
75 | nonce: senderNonce,
76 | value: "1000000000000000000",
77 | sender: new Address(sender),
78 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
79 | gasPrice: 1000000000,
80 | gasLimit: 50000,
81 | data: Buffer.from("hello world"),
82 | chainID: CHAIN_ID,
83 | });
84 |
85 | await this.provider.signTransaction(transaction);
86 | }
87 |
88 | async signTransactions() {
89 | const sender = getUrlParams().address;
90 | if (!sender) {
91 | displayOutcome("Try to login first.");
92 | return;
93 | }
94 |
95 | const senderNonce = await this.recallNonce(sender);
96 |
97 | const firstTransaction = new Transaction({
98 | nonce: senderNonce,
99 | value: "1000000000000000000",
100 | gasLimit: 70000,
101 | sender: new Address(sender),
102 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
103 | data: Buffer.from("hello"),
104 | chainID: CHAIN_ID,
105 | });
106 |
107 | const secondTransaction = new Transaction({
108 | nonce: senderNonce + 1n,
109 | value: "3000000000000000000",
110 | gasLimit: 70000,
111 | sender: new Address(sender),
112 | receiver: new Address("erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa"),
113 | data: Buffer.from("world"),
114 | chainID: CHAIN_ID,
115 | });
116 |
117 | await this.provider.signTransactions([firstTransaction, secondTransaction]);
118 | }
119 |
120 | async signRelayedTransaction() {
121 | const sender = getUrlParams().address;
122 | if (!sender) {
123 | displayOutcome("Try to login first.");
124 | return;
125 | }
126 |
127 | const senderShard = new AddressComputer().getShardOfAddress(Address.newFromBech32(sender));
128 | const relayer = {
129 | // https://github.com/multiversx/mx-sdk-testwallets/blob/main/users/mike.pem
130 | 0: "erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa",
131 | // https://github.com/multiversx/mx-sdk-testwallets/blob/main/users/grace.pem
132 | 1: "erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede",
133 | // https://github.com/multiversx/mx-sdk-testwallets/blob/main/users/carol.pem
134 | 2: "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8",
135 | }[senderShard];
136 |
137 | console.log("Relayer shard:", senderShard);
138 | console.log("Relayer:", relayer);
139 |
140 | const senderNonce = await this.recallNonce(sender);
141 | const data = Buffer.from("hello");
142 |
143 | const transaction = new Transaction({
144 | nonce: senderNonce,
145 | value: "10000000000000000",
146 | sender: Address.newFromBech32(sender),
147 | receiver: Address.newFromBech32("erd1testnlersh4z0wsv8kjx39me4rmnvjkwu8dsaea7ukdvvc9z396qykv7z7"),
148 | relayer: Address.newFromBech32(relayer),
149 | gasPrice: 1000000000,
150 | gasLimit: 100000 + 1500 * data.length,
151 | data: data,
152 | chainID: CHAIN_ID,
153 | });
154 |
155 | await this.provider.signTransaction(transaction);
156 | }
157 |
158 | async showSignedTransactions() {
159 | const plainSignedTransactions = this.provider.getTransactionsFromWalletUrl();
160 | alert(JSON.stringify(plainSignedTransactions, null, 4));
161 |
162 | // Now let's convert them back to sdk-js' Transaction objects.
163 | // Note that the Web Wallet provider returns the data field as a plain string.
164 | // However, sdk-js' Transaction.fromPlainObject expects it to be base64-encoded.
165 | // Therefore, we need to apply a workaround (an additional conversion).
166 | for (const plainTransaction of plainSignedTransactions) {
167 | const plainTransactionClone = structuredClone(plainTransaction);
168 | plainTransactionClone.data = Buffer.from(plainTransactionClone.data).toString("base64");
169 | const transaction = Transaction.newFromPlainObject(plainTransactionClone);
170 |
171 | console.log(transaction.toSendable());
172 | }
173 | }
174 |
175 | async sendSignedTransactions() {
176 | const plainSignedTransactions = this.provider.getTransactionsFromWalletUrl();
177 |
178 | for (const plainTransaction of plainSignedTransactions) {
179 | const plainTransactionClone = structuredClone(plainTransaction);
180 | plainTransactionClone.data = Buffer.from(plainTransactionClone.data).toString("base64");
181 | const transaction = Transaction.newFromPlainObject(plainTransactionClone);
182 |
183 | await this.apiNetworkProvider.sendTransaction(transaction);
184 | }
185 | }
186 |
187 | async signMessage() {
188 | if (!this._address) {
189 | return displayOutcome("Unable to sign.", "Login & press Show address first.");
190 | }
191 |
192 | const message = new Message({
193 | address: new Address(this._address),
194 | data: Buffer.from("hello"),
195 | });
196 |
197 | const callbackUrl = getCurrentLocation();
198 | await this.provider.signMessage(message, { callbackUrl });
199 | }
200 |
201 | async showMessageSignature() {
202 | const signature = this.provider.getMessageSignatureFromWalletUrl();
203 | return displayOutcome("Signature:", signature);
204 | }
205 |
206 | async recallNonce(address) {
207 | const accountOnNetwork = await this.apiNetworkProvider.getAccount(Address.newFromBech32(address));
208 | const nonce = BigInt(accountOnNetwork.nonce);
209 |
210 | console.log(`recallNonce(), address = ${address}, nonce = ${nonce}`);
211 |
212 | return nonce;
213 | }
214 | }
215 |
216 | function getUrlParams() {
217 | const queryString = window.location.search.slice(1);
218 | const params = qs.parse(queryString);
219 |
220 | console.log("URL params", params);
221 |
222 | return params;
223 | }
224 |
225 | function getCurrentLocation() {
226 | return window.location.href.split("?")[0];
227 | }
228 |
--------------------------------------------------------------------------------
/signing-providers/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Examples
5 |
6 |
7 |
8 |
9 |
10 |
11 |
Examples of using sdk-js' signing providers
12 |
13 |
14 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
Extract data from URL (query string)
33 |
34 |
35 |
38 |
41 |
44 |
47 |
48 |
49 |
Broadcast
50 |
51 |
54 |
55 |
56 |
57 |
58 |
59 |
64 |
65 |
66 |
67 |
70 |
73 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
131 |
132 |
133 |
134 |
137 |
140 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
154 |
155 |
158 |
159 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
173 |
174 |
175 |
176 |
177 |
178 |
194 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
231 |
232 |
233 |
234 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
250 |
255 |
260 |
261 |
262 |
263 |
340 |
341 |
342 |
--------------------------------------------------------------------------------
/contracts/multisig-full.abi.json:
--------------------------------------------------------------------------------
1 | {
2 | "buildInfo": {
3 | "rustc": {
4 | "version": "1.71.0-nightly",
5 | "commitHash": "a2b1646c597329d0a25efa3889b66650f65de1de",
6 | "commitDate": "2023-05-25",
7 | "channel": "Nightly",
8 | "short": "rustc 1.71.0-nightly (a2b1646c5 2023-05-25)"
9 | },
10 | "contractCrate": {
11 | "name": "multisig",
12 | "version": "1.0.0",
13 | "gitVersion": "v0.45.2.1-reproducible-169-g37d970c"
14 | },
15 | "framework": {
16 | "name": "multiversx-sc",
17 | "version": "0.47.2"
18 | }
19 | },
20 | "docs": [
21 | "Multi-signature smart contract implementation.",
22 | "Acts like a wallet that needs multiple signers for any action performed.",
23 | "See the readme file for more detailed documentation."
24 | ],
25 | "name": "Multisig",
26 | "constructor": {
27 | "inputs": [
28 | {
29 | "name": "quorum",
30 | "type": "u32"
31 | },
32 | {
33 | "name": "board",
34 | "type": "variadic",
35 | "multi_arg": true
36 | }
37 | ],
38 | "outputs": []
39 | },
40 | "endpoints": [
41 | {
42 | "name": "upgrade",
43 | "mutability": "mutable",
44 | "inputs": [],
45 | "outputs": []
46 | },
47 | {
48 | "docs": [
49 | "Allows the contract to receive funds even if it is marked as unpayable in the protocol."
50 | ],
51 | "name": "deposit",
52 | "mutability": "mutable",
53 | "payableInTokens": [
54 | "*"
55 | ],
56 | "inputs": [],
57 | "outputs": []
58 | },
59 | {
60 | "docs": [
61 | "Clears storage pertaining to an action that is no longer supposed to be executed.",
62 | "Any signatures that the action received must first be removed, via `unsign`.",
63 | "Otherwise this endpoint would be prone to abuse."
64 | ],
65 | "name": "discardAction",
66 | "mutability": "mutable",
67 | "inputs": [
68 | {
69 | "name": "action_id",
70 | "type": "u32"
71 | }
72 | ],
73 | "outputs": []
74 | },
75 | {
76 | "docs": [
77 | "Discard all the actions with the given IDs"
78 | ],
79 | "name": "discardBatch",
80 | "mutability": "mutable",
81 | "inputs": [
82 | {
83 | "name": "action_ids",
84 | "type": "variadic",
85 | "multi_arg": true
86 | }
87 | ],
88 | "outputs": []
89 | },
90 | {
91 | "docs": [
92 | "Minimum number of signatures needed to perform any action."
93 | ],
94 | "name": "getQuorum",
95 | "mutability": "readonly",
96 | "inputs": [],
97 | "outputs": [
98 | {
99 | "type": "u32"
100 | }
101 | ]
102 | },
103 | {
104 | "docs": [
105 | "Denormalized board member count.",
106 | "It is kept in sync with the user list by the contract."
107 | ],
108 | "name": "getNumBoardMembers",
109 | "mutability": "readonly",
110 | "inputs": [],
111 | "outputs": [
112 | {
113 | "type": "u32"
114 | }
115 | ]
116 | },
117 | {
118 | "name": "getNumGroups",
119 | "mutability": "readonly",
120 | "inputs": [],
121 | "outputs": [
122 | {
123 | "type": "u32"
124 | }
125 | ]
126 | },
127 | {
128 | "docs": [
129 | "Denormalized proposer count.",
130 | "It is kept in sync with the user list by the contract."
131 | ],
132 | "name": "getNumProposers",
133 | "mutability": "readonly",
134 | "inputs": [],
135 | "outputs": [
136 | {
137 | "type": "u32"
138 | }
139 | ]
140 | },
141 | {
142 | "name": "getActionGroup",
143 | "mutability": "readonly",
144 | "inputs": [
145 | {
146 | "name": "group_id",
147 | "type": "u32"
148 | }
149 | ],
150 | "outputs": [
151 | {
152 | "type": "variadic",
153 | "multi_result": true
154 | }
155 | ]
156 | },
157 | {
158 | "name": "getLastGroupActionId",
159 | "mutability": "readonly",
160 | "inputs": [],
161 | "outputs": [
162 | {
163 | "type": "u32"
164 | }
165 | ]
166 | },
167 | {
168 | "docs": [
169 | "The index of the last proposed action.",
170 | "0 means that no action was ever proposed yet."
171 | ],
172 | "name": "getActionLastIndex",
173 | "mutability": "readonly",
174 | "inputs": [],
175 | "outputs": [
176 | {
177 | "type": "u32"
178 | }
179 | ]
180 | },
181 | {
182 | "docs": [
183 | "Initiates board member addition process.",
184 | "Can also be used to promote a proposer to board member."
185 | ],
186 | "name": "proposeAddBoardMember",
187 | "mutability": "mutable",
188 | "inputs": [
189 | {
190 | "name": "board_member_address",
191 | "type": "Address"
192 | }
193 | ],
194 | "outputs": [
195 | {
196 | "type": "u32"
197 | }
198 | ]
199 | },
200 | {
201 | "docs": [
202 | "Initiates proposer addition process..",
203 | "Can also be used to demote a board member to proposer."
204 | ],
205 | "name": "proposeAddProposer",
206 | "mutability": "mutable",
207 | "inputs": [
208 | {
209 | "name": "proposer_address",
210 | "type": "Address"
211 | }
212 | ],
213 | "outputs": [
214 | {
215 | "type": "u32"
216 | }
217 | ]
218 | },
219 | {
220 | "docs": [
221 | "Removes user regardless of whether it is a board member or proposer."
222 | ],
223 | "name": "proposeRemoveUser",
224 | "mutability": "mutable",
225 | "inputs": [
226 | {
227 | "name": "user_address",
228 | "type": "Address"
229 | }
230 | ],
231 | "outputs": [
232 | {
233 | "type": "u32"
234 | }
235 | ]
236 | },
237 | {
238 | "name": "proposeChangeQuorum",
239 | "mutability": "mutable",
240 | "inputs": [
241 | {
242 | "name": "new_quorum",
243 | "type": "u32"
244 | }
245 | ],
246 | "outputs": [
247 | {
248 | "type": "u32"
249 | }
250 | ]
251 | },
252 | {
253 | "docs": [
254 | "Propose a transaction in which the contract will perform a transfer-execute call.",
255 | "Can send EGLD without calling anything.",
256 | "Can call smart contract endpoints directly.",
257 | "Doesn't really work with builtin functions."
258 | ],
259 | "name": "proposeTransferExecute",
260 | "mutability": "mutable",
261 | "inputs": [
262 | {
263 | "name": "to",
264 | "type": "Address"
265 | },
266 | {
267 | "name": "egld_amount",
268 | "type": "BigUint"
269 | },
270 | {
271 | "name": "opt_gas_limit",
272 | "type": "Option"
273 | },
274 | {
275 | "name": "function_call",
276 | "type": "variadic",
277 | "multi_arg": true
278 | }
279 | ],
280 | "outputs": [
281 | {
282 | "type": "u32"
283 | }
284 | ]
285 | },
286 | {
287 | "name": "proposeTransferExecuteEsdt",
288 | "mutability": "mutable",
289 | "inputs": [
290 | {
291 | "name": "to",
292 | "type": "Address"
293 | },
294 | {
295 | "name": "tokens",
296 | "type": "List"
297 | },
298 | {
299 | "name": "opt_gas_limit",
300 | "type": "Option"
301 | },
302 | {
303 | "name": "function_call",
304 | "type": "variadic",
305 | "multi_arg": true
306 | }
307 | ],
308 | "outputs": [
309 | {
310 | "type": "u32"
311 | }
312 | ]
313 | },
314 | {
315 | "docs": [
316 | "Propose a transaction in which the contract will perform an async call call.",
317 | "Can call smart contract endpoints directly.",
318 | "Can use ESDTTransfer/ESDTNFTTransfer/MultiESDTTransfer to send tokens, while also optionally calling endpoints.",
319 | "Works well with builtin functions.",
320 | "Cannot simply send EGLD directly without calling anything."
321 | ],
322 | "name": "proposeAsyncCall",
323 | "mutability": "mutable",
324 | "inputs": [
325 | {
326 | "name": "to",
327 | "type": "Address"
328 | },
329 | {
330 | "name": "egld_amount",
331 | "type": "BigUint"
332 | },
333 | {
334 | "name": "opt_gas_limit",
335 | "type": "Option"
336 | },
337 | {
338 | "name": "function_call",
339 | "type": "variadic",
340 | "multi_arg": true
341 | }
342 | ],
343 | "outputs": [
344 | {
345 | "type": "u32"
346 | }
347 | ]
348 | },
349 | {
350 | "name": "proposeSCDeployFromSource",
351 | "mutability": "mutable",
352 | "inputs": [
353 | {
354 | "name": "amount",
355 | "type": "BigUint"
356 | },
357 | {
358 | "name": "source",
359 | "type": "Address"
360 | },
361 | {
362 | "name": "code_metadata",
363 | "type": "CodeMetadata"
364 | },
365 | {
366 | "name": "arguments",
367 | "type": "variadic",
368 | "multi_arg": true
369 | }
370 | ],
371 | "outputs": [
372 | {
373 | "type": "u32"
374 | }
375 | ]
376 | },
377 | {
378 | "name": "proposeSCUpgradeFromSource",
379 | "mutability": "mutable",
380 | "inputs": [
381 | {
382 | "name": "sc_address",
383 | "type": "Address"
384 | },
385 | {
386 | "name": "amount",
387 | "type": "BigUint"
388 | },
389 | {
390 | "name": "source",
391 | "type": "Address"
392 | },
393 | {
394 | "name": "code_metadata",
395 | "type": "CodeMetadata"
396 | },
397 | {
398 | "name": "arguments",
399 | "type": "variadic",
400 | "multi_arg": true
401 | }
402 | ],
403 | "outputs": [
404 | {
405 | "type": "u32"
406 | }
407 | ]
408 | },
409 | {
410 | "name": "proposeBatch",
411 | "mutability": "mutable",
412 | "inputs": [
413 | {
414 | "name": "actions",
415 | "type": "variadic",
416 | "multi_arg": true
417 | }
418 | ],
419 | "outputs": [
420 | {
421 | "type": "u32"
422 | }
423 | ]
424 | },
425 | {
426 | "docs": [
427 | "Used by board members to sign actions."
428 | ],
429 | "name": "sign",
430 | "mutability": "mutable",
431 | "inputs": [
432 | {
433 | "name": "action_id",
434 | "type": "u32"
435 | }
436 | ],
437 | "outputs": []
438 | },
439 | {
440 | "docs": [
441 | "Sign all the actions in the given batch"
442 | ],
443 | "name": "signBatch",
444 | "mutability": "mutable",
445 | "inputs": [
446 | {
447 | "name": "group_id",
448 | "type": "u32"
449 | }
450 | ],
451 | "outputs": []
452 | },
453 | {
454 | "name": "signAndPerform",
455 | "mutability": "mutable",
456 | "inputs": [
457 | {
458 | "name": "action_id",
459 | "type": "u32"
460 | }
461 | ],
462 | "outputs": [
463 | {
464 | "type": "optional",
465 | "multi_result": true
466 | }
467 | ]
468 | },
469 | {
470 | "name": "signBatchAndPerform",
471 | "mutability": "mutable",
472 | "inputs": [
473 | {
474 | "name": "group_id",
475 | "type": "u32"
476 | }
477 | ],
478 | "outputs": []
479 | },
480 | {
481 | "docs": [
482 | "Board members can withdraw their signatures if they no longer desire for the action to be executed.",
483 | "Actions that are left with no valid signatures can be then deleted to free up storage."
484 | ],
485 | "name": "unsign",
486 | "mutability": "mutable",
487 | "inputs": [
488 | {
489 | "name": "action_id",
490 | "type": "u32"
491 | }
492 | ],
493 | "outputs": []
494 | },
495 | {
496 | "docs": [
497 | "Unsign all actions with the given IDs"
498 | ],
499 | "name": "unsignBatch",
500 | "mutability": "mutable",
501 | "inputs": [
502 | {
503 | "name": "group_id",
504 | "type": "u32"
505 | }
506 | ],
507 | "outputs": []
508 | },
509 | {
510 | "docs": [
511 | "Returns `true` (`1`) if the user has signed the action.",
512 | "Does not check whether or not the user is still a board member and the signature valid."
513 | ],
514 | "name": "signed",
515 | "mutability": "readonly",
516 | "inputs": [
517 | {
518 | "name": "user",
519 | "type": "Address"
520 | },
521 | {
522 | "name": "action_id",
523 | "type": "u32"
524 | }
525 | ],
526 | "outputs": [
527 | {
528 | "type": "bool"
529 | }
530 | ]
531 | },
532 | {
533 | "name": "unsignForOutdatedBoardMembers",
534 | "mutability": "mutable",
535 | "inputs": [
536 | {
537 | "name": "action_id",
538 | "type": "u32"
539 | },
540 | {
541 | "name": "outdated_board_members",
542 | "type": "variadic",
543 | "multi_arg": true
544 | }
545 | ],
546 | "outputs": []
547 | },
548 | {
549 | "docs": [
550 | "Returns `true` (`1`) if `getActionValidSignerCount >= getQuorum`."
551 | ],
552 | "name": "quorumReached",
553 | "mutability": "readonly",
554 | "inputs": [
555 | {
556 | "name": "action_id",
557 | "type": "u32"
558 | }
559 | ],
560 | "outputs": [
561 | {
562 | "type": "bool"
563 | }
564 | ]
565 | },
566 | {
567 | "docs": [
568 | "Proposers and board members use this to launch signed actions."
569 | ],
570 | "name": "performAction",
571 | "mutability": "mutable",
572 | "inputs": [
573 | {
574 | "name": "action_id",
575 | "type": "u32"
576 | }
577 | ],
578 | "outputs": [
579 | {
580 | "type": "optional",
581 | "multi_result": true
582 | }
583 | ]
584 | },
585 | {
586 | "docs": [
587 | "Perform all the actions in the given batch"
588 | ],
589 | "name": "performBatch",
590 | "mutability": "mutable",
591 | "inputs": [
592 | {
593 | "name": "group_id",
594 | "type": "u32"
595 | }
596 | ],
597 | "outputs": []
598 | },
599 | {
600 | "name": "dnsRegister",
601 | "onlyOwner": true,
602 | "mutability": "mutable",
603 | "payableInTokens": [
604 | "EGLD"
605 | ],
606 | "inputs": [
607 | {
608 | "name": "dns_address",
609 | "type": "Address"
610 | },
611 | {
612 | "name": "name",
613 | "type": "bytes"
614 | }
615 | ],
616 | "outputs": []
617 | },
618 | {
619 | "docs": [
620 | "Iterates through all actions and retrieves those that are still pending.",
621 | "Serialized full action data:",
622 | "- the action id",
623 | "- the serialized action data",
624 | "- (number of signers followed by) list of signer addresses."
625 | ],
626 | "name": "getPendingActionFullInfo",
627 | "mutability": "readonly",
628 | "inputs": [
629 | {
630 | "name": "opt_range",
631 | "type": "optional>",
632 | "multi_arg": true
633 | }
634 | ],
635 | "outputs": [
636 | {
637 | "type": "variadic",
638 | "multi_result": true
639 | }
640 | ],
641 | "labels": [
642 | "multisig-external-view"
643 | ],
644 | "allow_multiple_var_args": true
645 | },
646 | {
647 | "docs": [
648 | "Indicates user rights.",
649 | "`0` = no rights,",
650 | "`1` = can propose, but not sign,",
651 | "`2` = can propose and sign."
652 | ],
653 | "name": "userRole",
654 | "mutability": "readonly",
655 | "inputs": [
656 | {
657 | "name": "user",
658 | "type": "Address"
659 | }
660 | ],
661 | "outputs": [
662 | {
663 | "type": "UserRole"
664 | }
665 | ],
666 | "labels": [
667 | "multisig-external-view"
668 | ]
669 | },
670 | {
671 | "docs": [
672 | "Lists all users that can sign actions."
673 | ],
674 | "name": "getAllBoardMembers",
675 | "mutability": "readonly",
676 | "inputs": [],
677 | "outputs": [
678 | {
679 | "type": "variadic",
680 | "multi_result": true
681 | }
682 | ],
683 | "labels": [
684 | "multisig-external-view"
685 | ]
686 | },
687 | {
688 | "docs": [
689 | "Lists all proposers that are not board members."
690 | ],
691 | "name": "getAllProposers",
692 | "mutability": "readonly",
693 | "inputs": [],
694 | "outputs": [
695 | {
696 | "type": "variadic",
697 | "multi_result": true
698 | }
699 | ],
700 | "labels": [
701 | "multisig-external-view"
702 | ]
703 | },
704 | {
705 | "docs": [
706 | "Serialized action data of an action with index."
707 | ],
708 | "name": "getActionData",
709 | "mutability": "readonly",
710 | "inputs": [
711 | {
712 | "name": "action_id",
713 | "type": "u32"
714 | }
715 | ],
716 | "outputs": [
717 | {
718 | "type": "Action"
719 | }
720 | ],
721 | "labels": [
722 | "multisig-external-view"
723 | ]
724 | },
725 | {
726 | "docs": [
727 | "Gets addresses of all users who signed an action.",
728 | "Does not check if those users are still board members or not,",
729 | "so the result may contain invalid signers."
730 | ],
731 | "name": "getActionSigners",
732 | "mutability": "readonly",
733 | "inputs": [
734 | {
735 | "name": "action_id",
736 | "type": "u32"
737 | }
738 | ],
739 | "outputs": [
740 | {
741 | "type": "List"
742 | }
743 | ],
744 | "labels": [
745 | "multisig-external-view"
746 | ]
747 | },
748 | {
749 | "docs": [
750 | "Gets addresses of all users who signed an action and are still board members.",
751 | "All these signatures are currently valid."
752 | ],
753 | "name": "getActionSignerCount",
754 | "mutability": "readonly",
755 | "inputs": [
756 | {
757 | "name": "action_id",
758 | "type": "u32"
759 | }
760 | ],
761 | "outputs": [
762 | {
763 | "type": "u32"
764 | }
765 | ],
766 | "labels": [
767 | "multisig-external-view"
768 | ]
769 | },
770 | {
771 | "docs": [
772 | "It is possible for board members to lose their role.",
773 | "They are not automatically removed from all actions when doing so,",
774 | "therefore the contract needs to re-check every time when actions are performed.",
775 | "This function is used to validate the signers before performing an action.",
776 | "It also makes it easy to check before performing an action."
777 | ],
778 | "name": "getActionValidSignerCount",
779 | "mutability": "readonly",
780 | "inputs": [
781 | {
782 | "name": "action_id",
783 | "type": "u32"
784 | }
785 | ],
786 | "outputs": [
787 | {
788 | "type": "u32"
789 | }
790 | ],
791 | "labels": [
792 | "multisig-external-view"
793 | ]
794 | }
795 | ],
796 | "events": [
797 | {
798 | "identifier": "asyncCallSuccess",
799 | "inputs": [
800 | {
801 | "name": "results",
802 | "type": "variadic",
803 | "indexed": true
804 | }
805 | ]
806 | },
807 | {
808 | "identifier": "asyncCallError",
809 | "inputs": [
810 | {
811 | "name": "err_code",
812 | "type": "u32",
813 | "indexed": true
814 | },
815 | {
816 | "name": "err_message",
817 | "type": "bytes",
818 | "indexed": true
819 | }
820 | ]
821 | },
822 | {
823 | "identifier": "startPerformAction",
824 | "inputs": [
825 | {
826 | "name": "data",
827 | "type": "ActionFullInfo"
828 | }
829 | ]
830 | },
831 | {
832 | "identifier": "performChangeUser",
833 | "inputs": [
834 | {
835 | "name": "action_id",
836 | "type": "u32",
837 | "indexed": true
838 | },
839 | {
840 | "name": "changed_user",
841 | "type": "Address",
842 | "indexed": true
843 | },
844 | {
845 | "name": "old_role",
846 | "type": "UserRole",
847 | "indexed": true
848 | },
849 | {
850 | "name": "new_role",
851 | "type": "UserRole",
852 | "indexed": true
853 | }
854 | ]
855 | },
856 | {
857 | "identifier": "performChangeQuorum",
858 | "inputs": [
859 | {
860 | "name": "action_id",
861 | "type": "u32",
862 | "indexed": true
863 | },
864 | {
865 | "name": "new_quorum",
866 | "type": "u32",
867 | "indexed": true
868 | }
869 | ]
870 | },
871 | {
872 | "identifier": "performAsyncCall",
873 | "inputs": [
874 | {
875 | "name": "action_id",
876 | "type": "u32",
877 | "indexed": true
878 | },
879 | {
880 | "name": "to",
881 | "type": "Address",
882 | "indexed": true
883 | },
884 | {
885 | "name": "egld_value",
886 | "type": "BigUint",
887 | "indexed": true
888 | },
889 | {
890 | "name": "gas",
891 | "type": "u64",
892 | "indexed": true
893 | },
894 | {
895 | "name": "endpoint",
896 | "type": "bytes",
897 | "indexed": true
898 | },
899 | {
900 | "name": "arguments",
901 | "type": "variadic",
902 | "indexed": true
903 | }
904 | ]
905 | },
906 | {
907 | "identifier": "performTransferExecuteEgld",
908 | "inputs": [
909 | {
910 | "name": "action_id",
911 | "type": "u32",
912 | "indexed": true
913 | },
914 | {
915 | "name": "to",
916 | "type": "Address",
917 | "indexed": true
918 | },
919 | {
920 | "name": "egld_value",
921 | "type": "BigUint",
922 | "indexed": true
923 | },
924 | {
925 | "name": "gas",
926 | "type": "u64",
927 | "indexed": true
928 | },
929 | {
930 | "name": "endpoint",
931 | "type": "bytes",
932 | "indexed": true
933 | },
934 | {
935 | "name": "arguments",
936 | "type": "variadic",
937 | "indexed": true
938 | }
939 | ]
940 | },
941 | {
942 | "identifier": "performTransferExecuteEsdt",
943 | "inputs": [
944 | {
945 | "name": "action_id",
946 | "type": "u32",
947 | "indexed": true
948 | },
949 | {
950 | "name": "to",
951 | "type": "Address",
952 | "indexed": true
953 | },
954 | {
955 | "name": "tokens",
956 | "type": "List",
957 | "indexed": true
958 | },
959 | {
960 | "name": "gas",
961 | "type": "u64",
962 | "indexed": true
963 | },
964 | {
965 | "name": "endpoint",
966 | "type": "bytes",
967 | "indexed": true
968 | },
969 | {
970 | "name": "arguments",
971 | "type": "variadic",
972 | "indexed": true
973 | }
974 | ]
975 | },
976 | {
977 | "identifier": "performDeployFromSource",
978 | "inputs": [
979 | {
980 | "name": "action_id",
981 | "type": "u32",
982 | "indexed": true
983 | },
984 | {
985 | "name": "egld_value",
986 | "type": "BigUint",
987 | "indexed": true
988 | },
989 | {
990 | "name": "source_address",
991 | "type": "Address",
992 | "indexed": true
993 | },
994 | {
995 | "name": "code_metadata",
996 | "type": "CodeMetadata",
997 | "indexed": true
998 | },
999 | {
1000 | "name": "gas",
1001 | "type": "u64",
1002 | "indexed": true
1003 | },
1004 | {
1005 | "name": "arguments",
1006 | "type": "variadic",
1007 | "indexed": true
1008 | }
1009 | ]
1010 | },
1011 | {
1012 | "identifier": "performUpgradeFromSource",
1013 | "inputs": [
1014 | {
1015 | "name": "action_id",
1016 | "type": "u32",
1017 | "indexed": true
1018 | },
1019 | {
1020 | "name": "target_address",
1021 | "type": "Address",
1022 | "indexed": true
1023 | },
1024 | {
1025 | "name": "egld_value",
1026 | "type": "BigUint",
1027 | "indexed": true
1028 | },
1029 | {
1030 | "name": "source_address",
1031 | "type": "Address",
1032 | "indexed": true
1033 | },
1034 | {
1035 | "name": "code_metadata",
1036 | "type": "CodeMetadata",
1037 | "indexed": true
1038 | },
1039 | {
1040 | "name": "gas",
1041 | "type": "u64",
1042 | "indexed": true
1043 | },
1044 | {
1045 | "name": "arguments",
1046 | "type": "variadic",
1047 | "indexed": true
1048 | }
1049 | ]
1050 | }
1051 | ],
1052 | "esdtAttributes": [],
1053 | "hasCallback": true,
1054 | "types": {
1055 | "Action": {
1056 | "type": "enum",
1057 | "variants": [
1058 | {
1059 | "name": "Nothing",
1060 | "discriminant": 0
1061 | },
1062 | {
1063 | "name": "AddBoardMember",
1064 | "discriminant": 1,
1065 | "fields": [
1066 | {
1067 | "name": "0",
1068 | "type": "Address"
1069 | }
1070 | ]
1071 | },
1072 | {
1073 | "name": "AddProposer",
1074 | "discriminant": 2,
1075 | "fields": [
1076 | {
1077 | "name": "0",
1078 | "type": "Address"
1079 | }
1080 | ]
1081 | },
1082 | {
1083 | "name": "RemoveUser",
1084 | "discriminant": 3,
1085 | "fields": [
1086 | {
1087 | "name": "0",
1088 | "type": "Address"
1089 | }
1090 | ]
1091 | },
1092 | {
1093 | "name": "ChangeQuorum",
1094 | "discriminant": 4,
1095 | "fields": [
1096 | {
1097 | "name": "0",
1098 | "type": "u32"
1099 | }
1100 | ]
1101 | },
1102 | {
1103 | "name": "SendTransferExecuteEgld",
1104 | "discriminant": 5,
1105 | "fields": [
1106 | {
1107 | "name": "0",
1108 | "type": "CallActionData"
1109 | }
1110 | ]
1111 | },
1112 | {
1113 | "name": "SendTransferExecuteEsdt",
1114 | "discriminant": 6,
1115 | "fields": [
1116 | {
1117 | "name": "0",
1118 | "type": "EsdtTransferExecuteData"
1119 | }
1120 | ]
1121 | },
1122 | {
1123 | "name": "SendAsyncCall",
1124 | "discriminant": 7,
1125 | "fields": [
1126 | {
1127 | "name": "0",
1128 | "type": "CallActionData"
1129 | }
1130 | ]
1131 | },
1132 | {
1133 | "name": "SCDeployFromSource",
1134 | "discriminant": 8,
1135 | "fields": [
1136 | {
1137 | "name": "amount",
1138 | "type": "BigUint"
1139 | },
1140 | {
1141 | "name": "source",
1142 | "type": "Address"
1143 | },
1144 | {
1145 | "name": "code_metadata",
1146 | "type": "CodeMetadata"
1147 | },
1148 | {
1149 | "name": "arguments",
1150 | "type": "List"
1151 | }
1152 | ]
1153 | },
1154 | {
1155 | "name": "SCUpgradeFromSource",
1156 | "discriminant": 9,
1157 | "fields": [
1158 | {
1159 | "name": "sc_address",
1160 | "type": "Address"
1161 | },
1162 | {
1163 | "name": "amount",
1164 | "type": "BigUint"
1165 | },
1166 | {
1167 | "name": "source",
1168 | "type": "Address"
1169 | },
1170 | {
1171 | "name": "code_metadata",
1172 | "type": "CodeMetadata"
1173 | },
1174 | {
1175 | "name": "arguments",
1176 | "type": "List"
1177 | }
1178 | ]
1179 | }
1180 | ]
1181 | },
1182 | "ActionFullInfo": {
1183 | "type": "struct",
1184 | "docs": [
1185 | "Not used internally, just to retrieve results via endpoint."
1186 | ],
1187 | "fields": [
1188 | {
1189 | "name": "action_id",
1190 | "type": "u32"
1191 | },
1192 | {
1193 | "name": "group_id",
1194 | "type": "u32"
1195 | },
1196 | {
1197 | "name": "action_data",
1198 | "type": "Action"
1199 | },
1200 | {
1201 | "name": "signers",
1202 | "type": "List"
1203 | }
1204 | ]
1205 | },
1206 | "ActionStatus": {
1207 | "type": "enum",
1208 | "variants": [
1209 | {
1210 | "name": "Available",
1211 | "discriminant": 0
1212 | },
1213 | {
1214 | "name": "Aborted",
1215 | "discriminant": 1
1216 | }
1217 | ]
1218 | },
1219 | "CallActionData": {
1220 | "type": "struct",
1221 | "fields": [
1222 | {
1223 | "name": "to",
1224 | "type": "Address"
1225 | },
1226 | {
1227 | "name": "egld_amount",
1228 | "type": "BigUint"
1229 | },
1230 | {
1231 | "name": "opt_gas_limit",
1232 | "type": "Option"
1233 | },
1234 | {
1235 | "name": "endpoint_name",
1236 | "type": "bytes"
1237 | },
1238 | {
1239 | "name": "arguments",
1240 | "type": "List"
1241 | }
1242 | ]
1243 | },
1244 | "EsdtTokenPayment": {
1245 | "type": "struct",
1246 | "fields": [
1247 | {
1248 | "name": "token_identifier",
1249 | "type": "TokenIdentifier"
1250 | },
1251 | {
1252 | "name": "token_nonce",
1253 | "type": "u64"
1254 | },
1255 | {
1256 | "name": "amount",
1257 | "type": "BigUint"
1258 | }
1259 | ]
1260 | },
1261 | "EsdtTransferExecuteData": {
1262 | "type": "struct",
1263 | "fields": [
1264 | {
1265 | "name": "to",
1266 | "type": "Address"
1267 | },
1268 | {
1269 | "name": "tokens",
1270 | "type": "List"
1271 | },
1272 | {
1273 | "name": "opt_gas_limit",
1274 | "type": "Option"
1275 | },
1276 | {
1277 | "name": "endpoint_name",
1278 | "type": "bytes"
1279 | },
1280 | {
1281 | "name": "arguments",
1282 | "type": "List"
1283 | }
1284 | ]
1285 | },
1286 | "UserRole": {
1287 | "type": "enum",
1288 | "variants": [
1289 | {
1290 | "name": "None",
1291 | "discriminant": 0
1292 | },
1293 | {
1294 | "name": "Proposer",
1295 | "discriminant": 1
1296 | },
1297 | {
1298 | "name": "BoardMember",
1299 | "discriminant": 2
1300 | }
1301 | ]
1302 | }
1303 | }
1304 | }
1305 |
--------------------------------------------------------------------------------