diff --git a/README.md b/README.md
index ac05c0d..049bf2e 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,3 @@
-
@@ -7,8 +6,9 @@
## About
-Cosmwasm.js was created to help new developers get started with their first dApps.
-It is just a wrapper package to easily import needed features from CosmJS.
+Cosmwasm.js was created to help new developers get started with their first
+dApps. It is just a wrapper package to easily import needed features from
+CosmJS.
## Get started
@@ -49,17 +49,17 @@ import { SigningCosmWasmClient, Secp256k1HdWallet } from "cosmwasm";
const rpcEndpoint = "https://rpc.cliffnet.cosmwasm.com:443/";
// Using a random generated mnemonic
-const mnemonic = "rifle same bitter control garage duck grab spare mountain doctor rubber cook";
+const mnemonic =
+ "rifle same bitter control garage duck grab spare mountain doctor rubber cook";
async function main() {
-
// Create a wallet
const wallet = await Secp256k1HdWallet.fromMnemonic(mnemonic);
// Using
const client = await SigningCosmWasmClient.connectWithSigner(
rpcEndpoint,
- wallet
+ wallet,
);
console.log(client);
}
@@ -67,6 +67,25 @@ async function main() {
main();
```
+### Connect with keplr and get a signing starget client
+
+```ts
+import { setupWebKeplr } from "cosmwasm";
+
+const config = {
+ chainId: "cliffnet-1",
+ rpcEndpoint: "https://rpc.cliffnet.cosmwasm.com:443/",
+ prefix: "wasm",
+};
+
+async function main() {
+ const client = await setupWebKeplr(config);
+ console.log(client);
+}
+
+main();
+```
+
### Interacting with contracts
```ts
@@ -76,7 +95,8 @@ import { CosmWasmClient } from "cosmwasm";
const rpcEndpoint = "https://rpc.cliffnet.cosmwasm.com:443/";
// This is your contract address
-const contractAddr = "wasm19qws2lfd8pskyn0cfgpl5yjjyq3msy5402qr8nkzff9kdnkaepyqycedfh";
+const contractAddr =
+ "wasm19qws2lfd8pskyn0cfgpl5yjjyq3msy5402qr8nkzff9kdnkaepyqycedfh";
async function main() {
const client = await CosmWasmClient.connect(rpcEndpoint);
diff --git a/package.json b/package.json
index 8422804..a358496 100644
--- a/package.json
+++ b/package.json
@@ -37,10 +37,12 @@
"@cosmjs/crypto": "0.27.1",
"@cosmjs/encoding": "0.27.1",
"@cosmjs/faucet-client": "0.27.1",
+ "@cosmjs/ledger-amino": "0.27.1",
"@cosmjs/proto-signing": "0.27.1",
"@cosmjs/stargate": "0.27.1"
},
"devDependencies": {
+ "@types/ledgerhq__hw-transport": "^4.21.4",
"@typescript-eslint/eslint-plugin": "^5.11.0",
"@typescript-eslint/parser": "^5.11.0",
"eslint": "^7.32.0",
@@ -48,10 +50,10 @@
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-node": "^11.1.0",
+ "eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-promise": "^5.2.0",
+ "eslint-plugin-simple-import-sort": "^7.0.0",
"prettier": "^2.5.1",
- "typescript": "^4.5.5",
- "eslint-plugin-prettier": "^4.0.0",
- "eslint-plugin-simple-import-sort": "^7.0.0"
+ "typescript": "^4.5.5"
}
}
diff --git a/src/helpers/setup.ts b/src/helpers/setup.ts
new file mode 100644
index 0000000..f4f7031
--- /dev/null
+++ b/src/helpers/setup.ts
@@ -0,0 +1,159 @@
+/**
+ * Setup helper functions
+ *
+ * These are multiple helper function to get quickly started.
+ * There are currently 4 different functions to choose from:
+ * (a) Web / Keplr
+ * (b) Web / Ledger
+ * (c) Node / Local Mnemonic
+ * (d) Node / Ledger
+ */
+import { makeCosmoshubPath } from "../amino";
+import { SigningCosmWasmClient } from "../cosmwasm-stargate";
+import { LedgerSigner } from "../ledger-amino";
+import { DirectSecp256k1HdWallet } from "../proto-signing";
+import { GasPrice } from "../stargate";
+
+/**
+ * All setup functions are using the same config pattern
+ */
+interface Config {
+ chainId: string;
+ rpcEndpoint: string;
+ prefix: string;
+ gasPrice?: GasPrice;
+}
+
+// Window has to be re-declared to get keplr working
+declare const window: any;
+
+/**
+ * (a) Web / Keplr
+ * Prompts keplr and returns a signing client after the user
+ * gave permissions.
+ *
+ * @param config
+ * @returns SigningCosmWasmClient
+ */
+export async function setupWebKeplr(config: Config): Promise {
+ // check browser compatibility
+ if (!window.keplr) {
+ throw new Error("Keplr is not supported or installed on this browser!");
+ }
+
+ // try to enable keplr with given chainId
+ await window.keplr.enable(config.chainId).catch(() => {
+ throw new Error("Keplr can't connect to this chainId!");
+ });
+
+ const { prefix, gasPrice } = config;
+
+ // Setup signer
+ const offlineSigner = await window.getOfflineSignerAuto(config.chainId);
+
+ // Init SigningCosmWasmClient client
+ const signingClient = await SigningCosmWasmClient.connectWithSigner(config.rpcEndpoint, offlineSigner, {
+ prefix,
+ gasPrice,
+ });
+
+ return signingClient;
+}
+
+/**
+ * (b) Web / Ledger
+ * Returns a signing client after the user gave permissions.
+ *
+ * @param config
+ * @returns SigningCosmWasmClient
+ */
+export async function setupWebLedger(config: Config, transport: any): Promise {
+ const { prefix, gasPrice } = config;
+ const interactiveTimeout = 120_000;
+
+ // Prepare ledger
+ const ledgerTransport = await transport.create(interactiveTimeout, interactiveTimeout);
+
+ // Setup signer
+ const offlineSigner = new LedgerSigner(ledgerTransport, {
+ hdPaths: [makeCosmoshubPath(0)],
+ prefix: prefix,
+ });
+
+ // Init SigningCosmWasmClient client
+ const client = await SigningCosmWasmClient.connectWithSigner(config.rpcEndpoint, offlineSigner, {
+ prefix,
+ gasPrice,
+ });
+
+ const chainId = await client.getChainId();
+
+ if (chainId !== config.chainId) {
+ throw Error("Given ChainId doesn't match the clients ChainID!");
+ }
+
+ return client;
+}
+
+/**
+ * (c) Node / Local Mnemonic
+ * Using a local mnemonic and returns a signing clien
+ *
+ * @param config
+ * @param mnemonic
+ * @returns SigningCosmWasmClient
+ */
+export async function setupNodeLocal(config: Config, mnemonic: string): Promise {
+ const { prefix, gasPrice } = config;
+
+ // Setup signer
+ const offlineSigner = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix });
+
+ // Init SigningCosmWasmClient client
+ const client = await SigningCosmWasmClient.connectWithSigner(config.rpcEndpoint, offlineSigner, {
+ prefix,
+ gasPrice,
+ });
+
+ const chainId = await client.getChainId();
+
+ if (chainId !== config.chainId) {
+ throw Error("Given ChainId doesn't match the clients ChainID!");
+ }
+
+ return client;
+}
+
+/**
+ * (d) Node / Ledger
+ * Returns a signing client after the user gave permissions.
+ *
+ * @param config
+ * @returns SigningCosmWasmClient
+ */
+export async function setupNodeLedger(config: Config, transport: any): Promise {
+ const { prefix, gasPrice } = config;
+ const interactiveTimeout = 120_000;
+
+ // Prepare ledger
+ const ledgerTransport = await transport.create(interactiveTimeout, interactiveTimeout);
+
+ // Setup signer
+ const offlineSigner = new LedgerSigner(ledgerTransport, {
+ hdPaths: [makeCosmoshubPath(0)],
+ prefix: prefix,
+ });
+
+ const client = await SigningCosmWasmClient.connectWithSigner(config.rpcEndpoint, offlineSigner, {
+ prefix: prefix,
+ gasPrice: gasPrice,
+ });
+
+ const chainId = await client.getChainId();
+
+ if (chainId !== config.chainId) {
+ throw Error("Given ChainId doesn't match the clients ChainID!");
+ }
+
+ return client;
+}
diff --git a/src/index.ts b/src/index.ts
index 936aa0e..35b8847 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,3 +1,6 @@
+/**
+ * Exporting all the defined CosmJS symbols
+ */
export * from "./amino";
export * from "./cosmwasm-stargate";
export * from "./crypto";
@@ -5,3 +8,8 @@ export * from "./encoding";
export * from "./faucet-client";
export * from "./proto-signing";
export * from "./stargate";
+
+/**
+ * Exporting CosmWasmJS Helpers
+ */
+export { setupNodeLedger, setupNodeLocal, setupWebKeplr, setupWebLedger } from "./helpers/setup";
diff --git a/src/ledger-amino.ts b/src/ledger-amino.ts
new file mode 100644
index 0000000..42c5ca0
--- /dev/null
+++ b/src/ledger-amino.ts
@@ -0,0 +1 @@
+export { LedgerSigner } from "@cosmjs/ledger-amino";
diff --git a/yarn.lock b/yarn.lock
index 35ff675..61b1ade 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -23,6 +23,13 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
+"@babel/runtime@^7.11.2":
+ version "7.17.2"
+ resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz"
+ integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@confio/ics23@^0.6.3":
version "0.6.5"
resolved "https://registry.npmjs.org/@confio/ics23/-/ics23-0.6.5.tgz"
@@ -101,6 +108,19 @@
"@cosmjs/stream" "0.27.1"
xstream "^11.14.0"
+"@cosmjs/ledger-amino@0.27.1":
+ version "0.27.1"
+ resolved "https://registry.npmjs.org/@cosmjs/ledger-amino/-/ledger-amino-0.27.1.tgz"
+ integrity sha512-MbRw37+q37xP6ICQZll1ulFkqW/rpUgkatnA0aOuFfvf9DivRO0XRXGM7sw/3aKInxt9+8M1DGNFWM3HchwExw==
+ dependencies:
+ "@cosmjs/amino" "0.27.1"
+ "@cosmjs/crypto" "0.27.1"
+ "@cosmjs/encoding" "0.27.1"
+ "@cosmjs/math" "0.27.1"
+ "@cosmjs/utils" "0.27.1"
+ ledger-cosmos-js "^2.1.8"
+ semver "^7.3.2"
+
"@cosmjs/math@0.27.1":
version "0.27.1"
resolved "https://registry.npmjs.org/@cosmjs/math/-/math-0.27.1.tgz"
@@ -204,6 +224,35 @@
resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+"@ledgerhq/devices@^5.51.1":
+ version "5.51.1"
+ resolved "https://registry.npmjs.org/@ledgerhq/devices/-/devices-5.51.1.tgz"
+ integrity sha512-4w+P0VkbjzEXC7kv8T1GJ/9AVaP9I6uasMZ/JcdwZBS3qwvKo5A5z9uGhP5c7TvItzcmPb44b5Mw2kT+WjUuAA==
+ dependencies:
+ "@ledgerhq/errors" "^5.50.0"
+ "@ledgerhq/logs" "^5.50.0"
+ rxjs "6"
+ semver "^7.3.5"
+
+"@ledgerhq/errors@^5.50.0":
+ version "5.50.0"
+ resolved "https://registry.npmjs.org/@ledgerhq/errors/-/errors-5.50.0.tgz"
+ integrity sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow==
+
+"@ledgerhq/hw-transport@^5.25.0":
+ version "5.51.1"
+ resolved "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz"
+ integrity sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw==
+ dependencies:
+ "@ledgerhq/devices" "^5.51.1"
+ "@ledgerhq/errors" "^5.50.0"
+ events "^3.3.0"
+
+"@ledgerhq/logs@^5.50.0":
+ version "5.50.0"
+ resolved "https://registry.npmjs.org/@ledgerhq/logs/-/logs-5.50.0.tgz"
+ integrity sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA==
+
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
@@ -288,11 +337,23 @@
resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
+"@types/ledgerhq__hw-transport@^4.21.4":
+ version "4.21.4"
+ resolved "https://registry.npmjs.org/@types/ledgerhq__hw-transport/-/ledgerhq__hw-transport-4.21.4.tgz"
+ integrity sha512-vep+6yZnGv6owAthIY0w3f72w4dJIb4+yE5PCHveInTlZE9wukvU6Wc5Eig0OUUxcdhTazzeZx1xUaNVLqyQSg==
+ dependencies:
+ "@types/node" "*"
+
"@types/long@^4.0.1":
version "4.0.1"
resolved "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz"
integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==
+"@types/node@*", "@types/node@^13.7.0":
+ version "13.13.52"
+ resolved "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz"
+ integrity sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==
+
"@types/node@11.11.6":
version "11.11.6"
resolved "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz"
@@ -303,11 +364,6 @@
resolved "https://registry.npmjs.org/@types/node/-/node-17.0.15.tgz"
integrity sha512-zWt4SDDv1S9WRBNxLFxFRHxdD9tvH8f5/kg5/IaLFdnSNXsDY4eL3Q3XXN+VxUnWIhyVFDwcsmAprvwXoM/ClA==
-"@types/node@^13.7.0":
- version "13.13.52"
- resolved "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz"
- integrity sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==
-
"@typescript-eslint/eslint-plugin@^5.11.0":
version "5.11.0"
resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.11.0.tgz"
@@ -839,7 +895,7 @@ eslint-plugin-node@^11.1.0:
eslint-plugin-prettier@^4.0.0:
version "4.0.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz#8b99d1e4b8b24a762472b4567992023619cb98e0"
+ resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz"
integrity sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==
dependencies:
prettier-linter-helpers "^1.0.0"
@@ -851,7 +907,7 @@ eslint-plugin-promise@^5.2.0:
eslint-plugin-simple-import-sort@^7.0.0:
version "7.0.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz#a1dad262f46d2184a90095a60c66fef74727f0f8"
+ resolved "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz"
integrity sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw==
eslint-scope@^5.1.1:
@@ -980,6 +1036,11 @@ esutils@^2.0.2:
resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+events@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz"
+ integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
+
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
@@ -987,7 +1048,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
fast-diff@^1.1.2:
version "1.2.0"
- resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
+ resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz"
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
fast-glob@^3.2.9:
@@ -1396,6 +1457,16 @@ json5@^1.0.1:
dependencies:
minimist "^1.2.0"
+ledger-cosmos-js@^2.1.8:
+ version "2.1.8"
+ resolved "https://registry.npmjs.org/ledger-cosmos-js/-/ledger-cosmos-js-2.1.8.tgz"
+ integrity sha512-Gl7SWMq+3R9OTkF1hLlg5+1geGOmcHX9OdS+INDsGNxSiKRWlsWCvQipGoDnRIQ6CPo2i/Ze58Dw0Mt/l3UYyA==
+ dependencies:
+ "@babel/runtime" "^7.11.2"
+ "@ledgerhq/hw-transport" "^5.25.0"
+ bech32 "^1.1.4"
+ ripemd160 "^2.0.2"
+
levn@^0.4.1:
version "0.4.1"
resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz"
@@ -1632,7 +1703,7 @@ prelude-ls@^1.2.1:
prettier-linter-helpers@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
+ resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz"
integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
dependencies:
fast-diff "^1.1.2"
@@ -1716,6 +1787,11 @@ readonly-date@^1.0.0:
resolved "https://registry.npmjs.org/readonly-date/-/readonly-date-1.0.0.tgz"
integrity sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==
+regenerator-runtime@^0.13.4:
+ version "0.13.9"
+ resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
+ integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
+
regexpp@^3.0.0, regexpp@^3.1.0, regexpp@^3.2.0:
version "3.2.0"
resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz"
@@ -1767,6 +1843,13 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
+rxjs@6:
+ version "6.6.7"
+ resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz"
+ integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
+ dependencies:
+ tslib "^1.9.0"
+
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
@@ -1777,7 +1860,7 @@ semver@^6.1.0:
resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
-semver@^7.2.1, semver@^7.3.5:
+semver@^7.2.1, semver@^7.3.2, semver@^7.3.5:
version "7.3.5"
resolved "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz"
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
@@ -1938,7 +2021,7 @@ tsconfig-paths@^3.12.0:
minimist "^1.2.0"
strip-bom "^3.0.0"
-tslib@^1.8.1:
+tslib@^1.8.1, tslib@^1.9.0:
version "1.14.1"
resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==