From 663014c5a3582989b3f77367aecbeee74e9e289c Mon Sep 17 00:00:00 2001 From: Hakim <59644786+haammar-ledger@users.noreply.github.com> Date: Mon, 16 May 2022 18:24:46 +0200 Subject: [PATCH 1/2] LIVE-1292 Cover Polkadot existential deposit edge case (#1953) (#1957) * LIVE-1292 Cover Polkadot existential deposit edge case (#1953) * Fix transaction mode mapping Send max must be balances.transfer palletMethod instead of balances.transferKeepAlive * Fix 1 DOT Polkadot limitation On the Polkadot network, an address is only active when it holds a minimum amount, currently set at 1 DOT. There's a limitation when you hold ~ 1 DOT. Use recommended 1.1 DOT value. * Improve code readability Don't use ternary operator * Split Existential Deposit and margin Restore EXISTENTIAL_DEPOSIT, use new constant EXISTENTIAL_DEPOSIT_RECOMMENDED_MARGIN to handle recommended margin * Polkabot * trigger bot * trigger bot * Change Polkadot bot to 'Mooncake' seed * Change Polkadot bot to 'Mere Denis' seed * Change Polkadot bot to 'Silicium' seed * Update Polkadot bot spec to withdraw staking funds sometimes * Fix bot condition to do bond transactions * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * lint - remove unused import Co-authored-by: Alexandre Alouit --- .github/workflows/bot-polkadot-silicium.yml | 79 +++++++++++++++++++ src/families/polkadot/cli-transaction.ts | 2 +- .../polkadot/js-getTransactionStatus.ts | 7 ++ src/families/polkadot/js-signOperation.ts | 20 ++--- src/families/polkadot/logic.ts | 1 + src/families/polkadot/specs.ts | 65 +++++++++------ 6 files changed, 139 insertions(+), 35 deletions(-) create mode 100644 .github/workflows/bot-polkadot-silicium.yml diff --git a/.github/workflows/bot-polkadot-silicium.yml b/.github/workflows/bot-polkadot-silicium.yml new file mode 100644 index 0000000000..4d8896812e --- /dev/null +++ b/.github/workflows/bot-polkadot-silicium.yml @@ -0,0 +1,79 @@ +name: Bot 'Polkadot on Silicium' +on: + push: + branches: + - family/polkadot + +jobs: + start-runner: + name: "start ec2 instance (Linux)" + if: ${{ always() }} + uses: ledgerhq/actions/.github/workflows/start-linux-runner.yml@main + secrets: + CI_BOT_TOKEN: ${{ secrets.CI_BOT_TOKEN }} + + stop-runner: + name: "stop ec2 instance (Linux)" + needs: [start-runner, run-bot] + uses: ledgerhq/actions/.github/workflows/stop-linux-runner.yml@main + if: ${{ always() }} + with: + label: ${{ needs.start-runner.outputs.label }} + ec2-instance-id: ${{ needs.start-runner.outputs.ec2-instance-id }} + secrets: + CI_BOT_TOKEN: ${{ secrets.CI_BOT_TOKEN }} + + run-bot: + needs: [start-runner] + runs-on: ${{ needs.start-runner.outputs.label }} + steps: + - name: prepare runner + run: | + sudo growpart /dev/nvme0n1 1 + sudo resize2fs /dev/nvme0n1p1 + - uses: actions/checkout@v2 + - name: Retrieving coin apps + uses: actions/checkout@v2 + with: + repository: LedgerHQ/coin-apps + token: ${{ secrets.PAT }} + path: coin-apps + - uses: actions/setup-node@master + with: + node-version: 14.x + - name: install yarn + run: npm i -g yarn + - name: pull docker image + run: docker pull ghcr.io/ledgerhq/speculos + - name: kill apt-get + run: sudo killall -w apt-get apt || echo OK + - name: Install linux deps + run: sudo apt-get install -y libusb-1.0-0-dev jq + - name: Install dependencies + run: | + yarn global add yalc + yarn --frozen-lockfile + yarn ci-setup-cli + - name: BOT + env: + SEED: ${{ secrets.SEED3 }} + BOT_REPORT_FOLDER: botreport + VERBOSE_FILE: botreport/logs.txt + GITHUB_SHA: ${GITHUB_SHA} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_RUN_ID: ${{ github.run_id }} + GITHUB_WORKFLOW: ${{ github.workflow }} + SLACK_API_TOKEN: ${{ secrets.SLACK_API_TOKEN }} + SLACK_CHANNEL: ci-dot-ll + BOT_FILTER_FAMILY: polkadot + run: mkdir botreport; COINAPPS=$PWD/coin-apps yarn ci-test-bot + timeout-minutes: 120 + - name: Run coverage + if: failure() || success() + run: CODECOV_TOKEN=${{ secrets.CODECOV_TOKEN }} npx codecov + - name: upload logs + if: failure() || success() + uses: actions/upload-artifact@v1 + with: + name: botreport + path: botreport/ diff --git a/src/families/polkadot/cli-transaction.ts b/src/families/polkadot/cli-transaction.ts index 8a4e761357..2bcb200c1d 100644 --- a/src/families/polkadot/cli-transaction.ts +++ b/src/families/polkadot/cli-transaction.ts @@ -13,7 +13,7 @@ const options = [ { name: "mode", type: String, - desc: "mode of transaction: send, nominate, bond, claimReward", + desc: "mode of transaction: send, nominate, bond, claimReward, withdrawUnbonded", }, { name: "fees", diff --git a/src/families/polkadot/js-getTransactionStatus.ts b/src/families/polkadot/js-getTransactionStatus.ts index a2feed207d..2ed1a9db4f 100644 --- a/src/families/polkadot/js-getTransactionStatus.ts +++ b/src/families/polkadot/js-getTransactionStatus.ts @@ -36,6 +36,7 @@ import { calculateAmount, getMinimumAmountToBond, getMinimumBalance, + EXISTENTIAL_DEPOSIT_RECOMMENDED_MARGIN, } from "./logic"; import { isValidAddress } from "./address"; import { getCurrentPolkadotPreloadData } from "./preload"; @@ -78,6 +79,12 @@ const getSendTransactionStatus = async ( const leftover = a.spendableBalance.minus(totalSpent); if ( + a.spendableBalance.lte( + EXISTENTIAL_DEPOSIT.plus(EXISTENTIAL_DEPOSIT_RECOMMENDED_MARGIN) + ) + ) { + errors.amount = new NotEnoughBalance(); + } else if ( minimumBalanceExistential.gt(0) && leftover.lt(minimumBalanceExistential) && leftover.gt(0) diff --git a/src/families/polkadot/js-signOperation.ts b/src/families/polkadot/js-signOperation.ts index 565d321614..c0f204aa08 100644 --- a/src/families/polkadot/js-signOperation.ts +++ b/src/families/polkadot/js-signOperation.ts @@ -29,6 +29,7 @@ const MODE_TO_TYPE = { }; const MODE_TO_PALLET_METHOD = { send: "balances.transferKeepAlive", + sendMax: "balances.transfer", bond: "staking.bond", bondExtra: "staking.bondExtra", unbond: "staking.unbond", @@ -41,16 +42,15 @@ const MODE_TO_PALLET_METHOD = { }; const getExtra = (type: string, account: Account, transaction: Transaction) => { - const extra = MODE_TO_PALLET_METHOD[transaction.mode] - ? { - palletMethod: - MODE_TO_PALLET_METHOD[ - transaction.mode === "bond" && !isFirstBond(account) - ? "bondExtra" - : transaction.mode - ], - } - : {}; + const extra = { + palletMethod: MODE_TO_PALLET_METHOD[transaction.mode], + }; + + if (transaction.mode == "send" && transaction.useAllAmount) { + extra.palletMethod = MODE_TO_PALLET_METHOD["sendMax"]; + } else if (transaction.mode === "bond" && !isFirstBond(account)) { + extra.palletMethod = MODE_TO_PALLET_METHOD["bondExtra"]; + } switch (type) { case "OUT": diff --git a/src/families/polkadot/logic.ts b/src/families/polkadot/logic.ts index a701e685d6..daaa26383a 100644 --- a/src/families/polkadot/logic.ts +++ b/src/families/polkadot/logic.ts @@ -4,6 +4,7 @@ import type { Transaction } from "./types"; import { getCurrentPolkadotPreloadData } from "./preload"; export const EXISTENTIAL_DEPOSIT = new BigNumber(10000000000); +export const EXISTENTIAL_DEPOSIT_RECOMMENDED_MARGIN = new BigNumber(1000000000); // Polkadot recommended Existential Deposit error margin export const MAX_NOMINATIONS = 16; export const MAX_UNLOCKINGS = 32; export const PRELOAD_MAX_AGE = 60 * 1000; diff --git a/src/families/polkadot/specs.ts b/src/families/polkadot/specs.ts index 9db3888439..983b5e31dd 100644 --- a/src/families/polkadot/specs.ts +++ b/src/families/polkadot/specs.ts @@ -16,12 +16,14 @@ import { canUnbond, canNominate, isFirstBond, - getMinimumAmountToBond, + hasMinimumBondBalance, } from "../../families/polkadot/logic"; import { DeviceModelId } from "@ledgerhq/devices"; const currency = getCryptoCurrencyById("polkadot"); -const POLKADOT_MIN_SAFE = parseCurrencyUnit(currency.units[0], "0.05"); +// FIXME Should be replaced with EXISTENTIAL_DEPOSIT_RECOMMENDED_MARGIN in logic.ts +const POLKADOT_MIN_SAFE = parseCurrencyUnit(currency.units[0], "0.1"); +// FIXME Should be replaced with EXISTENTIAL_DEPOSIT in logic.ts const EXISTENTIAL_DEPOSIT = parseCurrencyUnit(currency.units[0], "1.0"); const MIN_LOCKED_BALANCE_REQ = parseCurrencyUnit(currency.units[0], "1.0"); const polkadot: AppSpec = { @@ -49,8 +51,9 @@ const polkadot: AppSpec = { mutations: [ { name: "send 50%~", - maxRun: 1, + maxRun: 2, transaction: ({ account, siblings, bridge }) => { + invariant(account.polkadotResources, "polkadot"); const sibling = pickSiblings(siblings, 2); let amount = account.spendableBalance .div(1.9 + 0.2 * Math.random()) @@ -81,18 +84,11 @@ const polkadot: AppSpec = { }, { name: "bond - bondExtra", - maxRun: 2, + maxRun: 1, transaction: ({ account, bridge }) => { + invariant(account.polkadotResources, "polkadot"); invariant(canBond(account), "can't bond"); - const { minimumBondBalance } = getCurrentPolkadotPreloadData(); - invariant( - new BigNumber(100000).gt( - getMinimumAmountToBond(account, new BigNumber(minimumBondBalance)) - ), - "can't bond because too much unbond" - ); - const { polkadotResources } = account; - invariant(polkadotResources, "polkadot"); + invariant(hasMinimumBondBalance(account), "not enough balance to bond"); const options: { recipient?: string; rewardDestination?: string; @@ -134,14 +130,14 @@ const polkadot: AppSpec = { }, { name: "unbond", - maxRun: 1, + maxRun: 2, transaction: ({ account, bridge }) => { - invariant(canUnbond(account), "can't unbond"); const { polkadotResources } = account; invariant(polkadotResources, "polkadot"); + invariant(canUnbond(account), "can't unbond"); invariant( account.spendableBalance.gt(POLKADOT_MIN_SAFE), - "cant cover fee" + "can't cover fee" ); const amount = (polkadotResources as PolkadotResources).lockedBalance .minus((polkadotResources as PolkadotResources).unlockingBalance) @@ -163,17 +159,15 @@ const polkadot: AppSpec = { name: "rebond", maxRun: 1, transaction: ({ account, bridge }) => { + const { polkadotResources } = account; + invariant(polkadotResources, "polkadot"); invariant( - account.polkadotResources?.unlockingBalance.gt( - MIN_LOCKED_BALANCE_REQ - ), + polkadotResources?.unlockingBalance.gt(MIN_LOCKED_BALANCE_REQ), "can't rebond" ); - const { polkadotResources } = account; - invariant(polkadotResources, "polkadot"); invariant( account.spendableBalance.gt(POLKADOT_MIN_SAFE), - "cant cover fee" + "can't cover fee" ); const amount = BigNumber.maximum( (polkadotResources as PolkadotResources).unlockingBalance.times(0.2), @@ -196,9 +190,8 @@ const polkadot: AppSpec = { name: "nominate", maxRun: 1, transaction: ({ account, bridge }) => { + invariant(account.polkadotResources, "polkadot"); invariant(canNominate(account), "can't nominate"); - const { polkadotResources } = account; - invariant(polkadotResources, "polkadot"); invariant( account.spendableBalance.gt(POLKADOT_MIN_SAFE), "cant cover fee" @@ -221,6 +214,30 @@ const polkadot: AppSpec = { }; }, }, + { + name: "withdraw", + maxRun: 2, + transaction: ({ account, bridge }) => { + const { polkadotResources } = account; + invariant(polkadotResources, "polkadot"); + invariant( + polkadotResources?.unlockedBalance.gt(0), + "nothing to withdraw" + ); + invariant( + account.spendableBalance.gt(POLKADOT_MIN_SAFE), + "can't cover fee" + ); + return { + transaction: bridge.createTransaction(account), + updates: [ + { + mode: "withdrawUnbonded", + }, + ], + }; + }, + }, ], }; export default { From 22987022095b52f2a9048cd76dc1994dbcedbb84 Mon Sep 17 00:00:00 2001 From: Hakim <59644786+haammar-ledger@users.noreply.github.com> Date: Tue, 17 May 2022 10:51:02 +0200 Subject: [PATCH 2/2] Update stellar-sdk to 10.1.0 (#1965) * Update stellar-sdk to 10.1.0 * Revert CLI * Align yarn.lock on develop --- package.json | 2 +- yarn.lock | 43 ++++++++++++++++++------------------------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 5daf78fb13..a162844e94 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "secp256k1": "^4.0.3", "semver": "^7.3.5", "sha.js": "^2.4.11", - "stellar-sdk": "^10.0.1", + "stellar-sdk": "^10.1.0", "superstruct": "^0.14.2", "triple-beam": "^1.3.0", "varuint-bitcoin": "1.1.2", diff --git a/yarn.lock b/yarn.lock index 932bd14be0..a455d6779c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5813,11 +5813,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.5: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" @@ -7280,7 +7275,7 @@ multihashes@^0.4.15, multihashes@~0.4.15: multibase "^0.7.0" varint "^5.0.0" -nan@^2.13.2, nan@^2.14.0, nan@^2.2.1: +nan@^2.13.2, nan@^2.2.1: version "2.15.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== @@ -7335,7 +7330,7 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -node-gyp-build@^4.1.0, node-gyp-build@^4.2.0, node-gyp-build@^4.2.2, node-gyp-build@^4.3.0: +node-gyp-build@^4.2.0, node-gyp-build@^4.2.2, node-gyp-build@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== @@ -8559,14 +8554,12 @@ snakecase-keys@3.2.1: map-obj "^4.1.0" to-snake-case "^1.0.0" -sodium-native@^2.3.0: - version "2.4.9" - resolved "https://registry.yarnpkg.com/sodium-native/-/sodium-native-2.4.9.tgz#7a7beb997efdbd2c773a385fb959f0cead5f5162" - integrity sha512-mbkiyA2clyfwAyOFIzMvsV6ny2KrKEIhFVASJxWfsmgfUEymgLIS2MLHHcGIQMkrcKhPErRaMR5Dzv0EEn+BWg== +sodium-native@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/sodium-native/-/sodium-native-3.3.0.tgz#50ee52ac843315866cce3d0c08ab03eb78f22361" + integrity sha512-rg6lCDM/qa3p07YGqaVD+ciAbUqm6SoO4xmlcfkbU5r1zIGrguXztLiEtaLYTV5U6k8KSIUFmnU3yQUSKmf6DA== dependencies: - ini "^1.3.5" - nan "^2.14.0" - node-gyp-build "^4.1.0" + node-gyp-build "^4.3.0" source-map-support@^0.5.17, source-map-support@^0.5.6: version "0.5.21" @@ -8633,10 +8626,10 @@ stack-utils@^2.0.3: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -stellar-base@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/stellar-base/-/stellar-base-7.0.0.tgz#6ebb359fc0a5e8fe6ed855c62de78cbec779b463" - integrity sha512-Sfk/u/6HT+8xSQ4HvTI3XgthTS3fhv/ie6Jx4OzLvg81pt09bDDN5YvRbG6v3gZdiRA0pwg2RRz5YS3ph5ePEg== +stellar-base@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/stellar-base/-/stellar-base-8.0.0.tgz#619e5fb7951fa8abb26322e51e611169a5d1cf62" + integrity sha512-MH0V94Hn/Ekq/yZxd+EQijkvfU+21zuqJAyEP8X+fhRR0IQYwGNByY003oLqIkgwqXotyofEQ76W5MoNFZmlpw== dependencies: base32.js "^0.1.0" bignumber.js "^4.0.0" @@ -8644,14 +8637,14 @@ stellar-base@^7.0.0: js-xdr "^1.1.3" lodash "^4.17.21" sha.js "^2.3.6" - tweetnacl "^1.0.0" + tweetnacl "^1.0.3" optionalDependencies: - sodium-native "^2.3.0" + sodium-native "^3.3.0" -stellar-sdk@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/stellar-sdk/-/stellar-sdk-10.0.1.tgz#0a5ec3cd913c2a32897c1ef13abf41017b91634f" - integrity sha512-NWvDAldw4GVFqWo1wGoR9lCyJ6xIcO2ab4YX8BQYp1Z45/Rogv3Kj3RERsso/9m+MR+d2OfTmAhIy0vruRGv/Q== +stellar-sdk@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/stellar-sdk/-/stellar-sdk-10.1.0.tgz#91cf9ed3c84c9a9d4c8aaa6cca29bd997b7c778a" + integrity sha512-8PTKuyrQ5bIbsyrLav+JE/c+rrLynBygiB4dpvG30ou7K8P6a5ainmGyVMbCzlz1OkKWoaAW5rJAkl3RhlTZkA== dependencies: "@types/eventsource" "^1.1.2" "@types/node" ">= 8" @@ -8664,7 +8657,7 @@ stellar-sdk@^10.0.1: eventsource "^1.0.7" lodash "^4.17.21" randombytes "^2.1.0" - stellar-base "^7.0.0" + stellar-base "^8.0.0" toml "^2.3.0" tslib "^1.10.0" urijs "^1.19.1"