-
Notifications
You must be signed in to change notification settings - Fork 479
[LWDM] feat(coin-tester): add the NEAR coin-tester #20297
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
pawell24
wants to merge
6
commits into
feat/LIVE-34284-near-coinmodule-api-migration
Choose a base branch
from
feat/LIVE-34293-near-coin-tester
base: feat/LIVE-34284-near-coinmodule-api-migration
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 4 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
4aec0eb
fix(live-common): parse optimistic operation fees above 1e21
pawell24 532f8ce
feat(live-common): add the NEAR bridge api for the generic framework
pawell24 0e716a3
feat(coin-tester): add the NEAR coin-tester
pawell24 9a8570d
test(coin-tester): assert NEAR balance arithmetic, not just op types
pawell24 6ba3186
refactor(coin-tester): shrink comment blocks to single lines
pawell24 8307b24
refactor(bridge): shrink comment blocks to single lines
pawell24 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| "@ledgerhq/coin-tester-near": minor | ||
| "@ledgerhq/live-common": minor | ||
| --- | ||
|
|
||
| Add the NEAR coin-tester |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| # staking-pool wasm, downloaded on first run | ||
| .cache/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # @ledgerhq/coin-tester-near |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| # @ledgerhq/coin-tester-near | ||
|
|
||
| > [!NOTE] | ||
| > **Status: STABLE** — Production-ready; API is considered stable. | ||
|
|
||
| Deterministic integration tester for the NEAR family. Starts a local `near-sandbox` node and runs | ||
| one scenario through both the legacy account bridge and the generic coin-framework bridge | ||
| (Alpaca/`CoinModuleApi`), so the two are compared against each other on every run. | ||
|
|
||
| ## Run | ||
|
|
||
| ```sh | ||
| pnpm coin:tester:near start | ||
| ``` | ||
|
|
||
| No Docker required: `near-sandbox` downloads a native binary for the host platform on first run | ||
| (Darwin arm64 and Linux x86_64 are both published). The coin module must be built first | ||
| (`pnpm --filter @ledgerhq/coin-near build`). | ||
|
|
||
| ## Coverage | ||
|
|
||
| - transfers to an existing named account and to a fresh implicit account | ||
| - staking: delegate, undelegate, and withdraw after the unlock period | ||
| - the account's balance breakdown and operation history after each transaction | ||
|
|
||
| ## How it works | ||
|
|
||
| **Node.** `near-sandbox` is a build of `neard` carrying the `sandbox` feature, which adds the | ||
| `sandbox_fast_forward` RPC. Genesis is left untouched — shrinking `epoch_length` to speed up the | ||
| staking unlock stops the node from ever becoming ready, so block height is advanced explicitly | ||
| instead. | ||
|
|
||
| The node sometimes keeps a socket open and never completes its shutdown, which holds the event loop | ||
| open long after the assertions are done. Teardown is therefore bounded by a timeout, and the run uses | ||
| `--forceExit`, as the polkadot and stellar testers do for the same reason. `near-sandbox` exposes no | ||
| handle on the process, so there is nothing to kill directly; the RPC port is picked per run, so a | ||
| leftover node never collides with the next one. | ||
|
|
||
| **Staking pool.** NEAR staking runs through a contract rather than the protocol, so the scenario | ||
| deploys `staking_pool.wasm` from `near/core-contracts` and delegates to it. The download is pinned to | ||
| a commit and checked against its sha256, so neither upstream nor a stale cache can swap the contract | ||
| out from under the run. The pool logs a | ||
| `minimum_stake` failure once per epoch because it also tries to become a protocol validator with far | ||
| less than the seat price; that is expected and does not affect delegation. | ||
|
|
||
| **Indexer.** The coin module reads its history, gas price, staking deposits and validator list from | ||
| an indexer that no local node provides, so those four endpoints are served by an `msw` stub built | ||
| from the sandbox's own state. `/v3/stats` is stubbed too, despite `getGasPrice` having a node-RPC | ||
| fallback: that fallback only covers a response without a `gas_price`, not a request that throws, and | ||
| an unstubbed host fails DNS resolution long before it is reached. | ||
|
|
||
| **Signing.** The local signer signs `sha256` of the Borsh-serialized transaction, which is what the | ||
| device app does. Signing the raw payload produces a signature the network rejects at broadcast. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import type { Config } from "jest"; | ||
|
|
||
| const esmDeps = ["ky"]; | ||
|
|
||
| const config: Config = { | ||
| testEnvironment: "node", | ||
| setupFilesAfterEnv: ["@ledgerhq/wallet-framework-test-setup"], | ||
| transform: { | ||
| "^.+\\.(t|j)sx?$": [ | ||
| "@swc/jest", | ||
| { | ||
| jsc: { | ||
| target: "esnext", | ||
| }, | ||
| }, | ||
| ], | ||
| [`node_modules[\\\\|/].pnpm[\\\\|/](${esmDeps.join("|")}).+\\.(js|jsx|mjs)$`]: [ | ||
| "@swc/jest", | ||
| { | ||
| jsc: { | ||
| target: "esnext", | ||
| }, | ||
| }, | ||
| ], | ||
| }, | ||
| moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], | ||
| testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"], | ||
| transformIgnorePatterns: [`node_modules/.pnpm/(?!(${esmDeps.join("|")}))`], | ||
| reporters: ["default", ...(process.env.CI ? ["github-actions"] : [])], | ||
| }; | ||
|
|
||
| export default config; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| { | ||
| "name": "@ledgerhq/coin-tester-near", | ||
| "version": "0.1.0", | ||
| "private": true, | ||
| "description": "Ledger NEAR Coin Tester", | ||
| "main": "src/scenarii.test.ts", | ||
| "keywords": [ | ||
| "Ledger", | ||
| "LedgerWallet", | ||
| "NEAR", | ||
| "Testing" | ||
| ], | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/LedgerHQ/ledger-live.git" | ||
| }, | ||
| "bugs": { | ||
| "url": "https://github.com/LedgerHQ/ledger-live/issues" | ||
| }, | ||
| "homepage": "https://github.com/LedgerHQ/ledger-live/tree/develop/libs/coin-tester-modules/coin-tester-near", | ||
| "typesVersions": { | ||
| "*": { | ||
| "lib/*": [ | ||
| "lib/*" | ||
| ], | ||
| "lib-es/*": [ | ||
| "lib-es/*" | ||
| ], | ||
| "*": [ | ||
| "lib/*" | ||
| ] | ||
| } | ||
| }, | ||
| "exports": { | ||
| "./lib/*": "./lib/*.js", | ||
| "./lib-es/*": "./lib-es/*.js", | ||
| "./package.json": "./package.json" | ||
| }, | ||
| "license": "Apache-2.0", | ||
| "scripts": { | ||
| "clean": "rimraf lib lib-es", | ||
| "build": "tsc --project tsconfig.build.json && tsc --project tsconfig.build.json -m esnext --moduleResolution bundler --outDir lib-es", | ||
| "prewatch": "pnpm build", | ||
| "watch": "tsc --watch --project tsconfig.build.json", | ||
| "watch:es": "tsc --watch --project tsconfig.build.json -m esnext --moduleResolution bundler --outDir lib-es", | ||
| "lint": "oxlint src", | ||
| "lint:fix": "oxfmt src && oxlint src --fix --quiet", | ||
| "format": "oxfmt src", | ||
| "format:check": "oxfmt src --check", | ||
| "typecheck": "tsc --noEmit --customConditions node", | ||
| "start": "pnpm jest --runTestsByPath src/*.test.ts --runInBand --forceExit", | ||
| "unimported": "pnpm knip --directory ../../.. -W libs/coin-tester-modules/coin-tester-near" | ||
| }, | ||
| "dependencies": { | ||
| "@ledgerhq/coin-near": "workspace:^", | ||
| "@ledgerhq/coin-tester": "workspace:^", | ||
| "@ledgerhq/ledger-wallet-framework": "workspace:^", | ||
| "@ledgerhq/live-common": "workspace:^", | ||
| "@ledgerhq/live-config": "workspace:^", | ||
| "@ledgerhq/types-live": "workspace:^", | ||
| "bignumber.js": "^9", | ||
| "msw": "catalog:", | ||
| "near-api-js": "^3.0.2", | ||
| "near-sandbox": "^0.3.2" | ||
| }, | ||
| "devDependencies": { | ||
| "@ledgerhq/wallet-framework-test-setup": "workspace:^", | ||
| "@swc/core": "catalog:", | ||
| "@swc/jest": "catalog:", | ||
| "@types/jest": "catalog:", | ||
| "@types/node": "catalog:", | ||
| "jest": "catalog:", | ||
| "oxfmt": "catalog:", | ||
| "oxlint": "catalog:", | ||
| "rimraf": "^5", | ||
| "typescript": "catalog:" | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| import BigNumber from "bignumber.js"; | ||
| import type { Account } from "@ledgerhq/types-live"; | ||
| import { decodeAccountId } from "@ledgerhq/ledger-wallet-framework/account"; | ||
| import { getCryptoCurrencyById } from "@ledgerhq/ledger-wallet-framework/currencies"; | ||
| import { | ||
| getDerivationScheme, | ||
| runDerivationScheme, | ||
| } from "@ledgerhq/ledger-wallet-framework/derivation"; | ||
| import { DEFAULT_ACCOUNT_ID } from "near-sandbox"; | ||
|
|
||
| export const NETWORK_ID = "sandbox"; | ||
|
|
||
| /** Every account the scenario touches is a subaccount of the genesis account. */ | ||
| export const SENDER_ID = `sender.${DEFAULT_ACCOUNT_ID}`; | ||
| export const NAMED_RECIPIENT_ID = `recipient.${DEFAULT_ACCOUNT_ID}`; | ||
| export const POOL_ID = `pool.${DEFAULT_ACCOUNT_ID}`; | ||
|
|
||
| /** | ||
| * A 64-character hex address. NEAR creates such an account implicitly on first transfer, which is | ||
| * the branch that costs more than a transfer to an existing account. | ||
| */ | ||
| export const IMPLICIT_RECIPIENT_ID = | ||
| "4e7de0a21d8a20f970c86b6edf407906d7ba9e205979c3268270eef80a286e2d"; | ||
|
|
||
| export const NEAR = 10n ** 24n; | ||
|
|
||
| export const SENDER_BALANCE = 200n * NEAR; | ||
| export const RECIPIENT_BALANCE = 10n * NEAR; | ||
| export const POOL_BALANCE = 50n * NEAR; | ||
|
|
||
| /** Base URL the coin config points at; msw intercepts it, nothing listens on it. */ | ||
| export const INDEXER_URL = "http://indexer.coin-tester-near.local"; | ||
|
|
||
| /** Four epochs of 500 blocks, the pool's `NUM_EPOCHS_TO_UNLOCK`, plus a margin. */ | ||
| export const EPOCHS_TO_UNLOCK_BLOCKS = 2500; | ||
|
|
||
| export const coinConfig = (rpcUrl: string) => () => ({ | ||
| status: { type: "active" as const }, | ||
| infra: { | ||
| API_NEAR_PRIVATE_NODE: rpcUrl, | ||
| API_NEAR_PUBLIC_NODE: rpcUrl, | ||
| API_NEAR_INDEXER: INDEXER_URL, | ||
| API_NEARBLOCKS_INDEXER: INDEXER_URL, | ||
| }, | ||
| }); | ||
|
|
||
| export const NEAR_CURRENCY = getCryptoCurrencyById("near"); | ||
|
|
||
| /** The account the scenario starts from, before the first sync fills it in. */ | ||
| export const makeAccount = (address: string): Account => { | ||
| const id = `js:2:${NEAR_CURRENCY.id}:${address}:`; | ||
| const { derivationMode, xpubOrAddress } = decodeAccountId(id); | ||
| const scheme = getDerivationScheme({ derivationMode, currency: NEAR_CURRENCY }); | ||
| const index = 0; | ||
|
|
||
| return { | ||
| type: "Account", | ||
| id, | ||
| xpub: xpubOrAddress, | ||
| subAccounts: [], | ||
| seedIdentifier: xpubOrAddress, | ||
| used: true, | ||
| swapHistory: [], | ||
| derivationMode, | ||
| currency: NEAR_CURRENCY, | ||
| index, | ||
| nfts: [], | ||
| freshAddress: xpubOrAddress, | ||
| freshAddressPath: runDerivationScheme(scheme, NEAR_CURRENCY, { | ||
| account: index, | ||
| node: 0, | ||
| address: 0, | ||
| }), | ||
| creationDate: new Date(), | ||
| lastSyncDate: new Date(0), | ||
| blockHeight: 0, | ||
| balance: new BigNumber(0), | ||
| spendableBalance: new BigNumber(0), | ||
| operationsCount: 0, | ||
| operations: [], | ||
| pendingOperations: [], | ||
| balanceHistoryCache: { | ||
| HOUR: { latestDate: null, balances: [] }, | ||
| DAY: { latestDate: null, balances: [] }, | ||
| WEEK: { latestDate: null, balances: [] }, | ||
| }, | ||
| } as unknown as Account; | ||
| }; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.