Skip to content
This repository has been archived by the owner on Oct 4, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1102 from near/ledger
Browse files Browse the repository at this point in the history
fix: bring back ledger support
  • Loading branch information
gagdiez authored Feb 29, 2024
2 parents ee98774 + d177cd0 commit e570a2d
Show file tree
Hide file tree
Showing 18 changed files with 728 additions and 35 deletions.
112 changes: 100 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ _Click on a command for more information and examples._
| [`near tx-status`](#near-tx-status) | queries a transaction's status by `txHash` |


[ [**OPTIONS**](#options) ]

> For EVM support see [Project Aurora's](https://aurora.dev) [`aurora-cli`](https://github.com/aurora-is-near/aurora-cli).
---

## Setup
Expand Down Expand Up @@ -100,7 +96,7 @@ npm install -g near-cli
- You can change the network by prepending an environment variable to your command.

```bash
NEAR_NETWORK=betanet near send ...
NEAR_NETWORK=testnet near send ...
```

- Alternatively, you can set up a global environment variable by running:
Expand Down Expand Up @@ -162,6 +158,8 @@ near add-credentials example-acct.testnet --seedPhrase "antique attitude say evo

> Adds either a **full access** or **function access** key to a given account.
> Optionally allows to sign with a Ledger: `--signWithLedger` `--ledgerPath`
**Note:** You will use an _existing_ full access key for the account you would like to add a _new_ key to. ([`near login`](http://docs.near.org/docs/tools/near-cli#near-login))

#### 1) add a `full access` key
Expand Down Expand Up @@ -224,6 +222,7 @@ near add-key example-acct.testnet GkMNfc92fwM1AmwH1MTjF4b7UZuceamsq96XPkHsQ9vi -
### `near delete-key`

> Deletes an existing key for a given account.
> Optionally allows to sign with a Ledger: `--signWithLedger` `--ledgerPath`
- arguments: `accountId` `publicKey`
- options: `--networkId`, `force`
Expand Down Expand Up @@ -253,7 +252,7 @@ near delete-key example-acct.testnet Cxg2wgFYrdLTEkMu6j5D6aEZqTb3kXbmJygS48ZKbo1
> Displays a key-pair and seed-phrase and optionally stores it locally in `.near-credentials`.
- arguments: `accountId` or `none`
- options: `--fromSeedPhrase`, `--saveImplicit`
- options: `--fromSeedPhrase`, `--saveImplicit`, `--queryLedgerPK`

**Note:** There are several ways to use `generate-key` that return very different results. Please reference the examples below for further details.

Expand Down Expand Up @@ -361,6 +360,78 @@ Implicit account: 9c07afc7673ea0f9a20c8a279e8bbe1dd1e283254263bb3b07403e4b6fd7a4

Will store the key pair corresponding to the seedPhrase in `.near-credentials` with an `accountId` that you specify.

<details>
<summary><strong>Example Response</strong></summary>
<p>

```
Seed phrase: antique attitude say evolve ring arrive hollow auto wide bronze usual unfold
Key pair: {"publicKey":"ed25519:BW5Q957u1rTATGpanKUktjVmixEmT56Df4Dt9hoGWEXz","secretKey":"ed25519:5StmPDg9xVNzpyudwxT8Y72iyRq7Fa86hcpsRk6Cq5eWGWqwsPbPT9woXbJs9Qe69crZJHh4DMkrGEPGDDfmXmy2"}
Implicit account: 9c07afc7673ea0f9a20c8a279e8bbe1dd1e283254263bb3b07403e4b6fd7a411
```

</p>
</details>

---

#### 4a) `near generate-key --queryLedgerPK`

> Uses a connected Ledger device to display a public key and [implicit account](http://docs.near.org/docs/roles/integrator/implicit-accounts) using the default HD path (`"44'/397'/0'/0'/1'"`)
```bash
near generate-key --queryLedgerPK
```

You should then see the following prompt to confirm this request on your Ledger device:

Make sure to connect your Ledger and open NEAR app
Getting Public Key from Ledger...

After confirming the request on your Ledger device, a public key and implicit accountId will be displayed.

<details>
<summary><strong>Example Response</strong></summary>
<p>

```bash
Using public key: ed25519:B22RP10g695wyeRvKIWv61NjmQZEkWTMzAYgdfx6oSeB2
Implicit account: 42c320xc20739fd9a6bqf2f89z61rd14efe5d3de234199bc771235a4bb8b0e1
```

</p>
</details>

---

#### 3b) `near generate-key --queryLedgerPK --ledgerPath="HD path you specify"`

> Uses a connected Ledger device to display a public key and [implicit account](http://docs.near.org/docs/roles/integrator/implicit-accounts) using a custom HD path.
```bash
near generate-key --queryLedgerPK --ledgerPath="44'/397'/0'/0'/2'"
```

You should then see the following prompt to confirm this request on your Ledger device:

Make sure to connect your Ledger and open NEAR app
Waiting for confirmation on Ledger...

After confirming the request on your Ledger device, a public key and implicit accountId will be displayed.

<details>
<summary><strong>Example Response</strong></summary>
<p>

```bash
Using public key: ed25519:B22RP10g695wye3dfa32rDjmQZEkWTMzAYgCX6oSeB2
Implicit account: 42c320xc20739ASD9a6bqf2Dsaf289z61rd14efe5d3de23213789009afDsd5bb8b0e1
```

</p>
</details>


---

### `near list-keys`
Expand Down Expand Up @@ -439,7 +510,7 @@ near login
> Creates an account using an existing account or a faucet service to pay for the account's creation and initial balance.
- arguments: `accountId`
- options: `--initialBalance`, `--useFaucet`, `--useAccount`
- options: `--initialBalance`, `--useFaucet`, `--useAccount`, `--seedPhrase`, `--publicKey`, `--signWithLedger`, `--ledgerPath`, `--useLedgerPK`, `--PkLedgerPath`

**Examples:**:

Expand All @@ -453,12 +524,28 @@ near create-account new-acc.testnet --useAccount example-acct.testnet
near create-account new-acc.testnet --useFaucet
```

```bash
# Creating a pre-funded account that can be controlled by the Ledger's public key
near create-account new-acc.testnet --useFaucet --useLedgerPK
```

```bash
# Creating an account using a Ledger account
near create-account new-acc.testnet --useAccount ledger-acct.testnet --signWithLedger
```

**Subaccount example:**

```bash
# Using an account to create a sub-account
near create-account sub-acct.example-acct.testnet --useAccount example-acct.testnet
```

```bash
# Creating a sub-account using the Ledger that can also be controlled by the ledger
near create-account sub.acc.testnet --useAccount sub.acc.testnet --signWithLedger --useLedgerPK
```

**Example using `--initialBalance`:**

```bash
Expand All @@ -482,7 +569,7 @@ near create-account sub-acct2.example-acct.testnet --useAccount example-acct.tes
> Deletes an account and transfers remaining balance to a beneficiary account.
- arguments: `accountId` `beneficiaryId`
- options: `force`
- options: `force`, `--signWithLedger`, `--ledgerPath`

**Example:**

Expand Down Expand Up @@ -510,7 +597,8 @@ near delete-account sub-acct2.example-acct.testnet example-acct.testnet

> Sends NEAR tokens (Ⓝ) from one account to another.
- arguments: `senderId` `receiverId` `amount`
- arguments: `senderId` `receiverId` `amount`
- options: `--signWithLedger`, `--ledgerPath`

**Note:** You will need a full access key for the sending account. ([`near login`](http://docs.near.org/docs/tools/near-cli#near-login))

Expand Down Expand Up @@ -577,7 +665,7 @@ near state example.testnet
**Note:** Contract calls require a transaction fee (gas) so you will need an access key for the `--accountId` that will be charged. ([`near login`](http://docs.near.org/docs/tools/near-cli#near-login))

- arguments: `contractName` `method_name` `{ args }` `--accountId`
- options: `--gas` `--deposit`
- options: `--gas` `--deposit` `--signWithLedger` `--ledgerPath`

**Example:**

Expand Down Expand Up @@ -612,13 +700,13 @@ near call guest-book.testnet addMessage '{"text": "Aloha"}' --account-id example
**Example:**

```bash
near deploy --accountId example-contract.testnet --wasmFile out/example.wasm
near deploy example-contract.testnet out/example.wasm
```

**Initialize Example:**

```bash
near deploy --accountId example-contract.testnet --wasmFile out/example.wasm --initFunction new --initArgs '{"owner_id": "example-contract.testnet", "total_supply": "10000000"}'
near deploy example-contract.testnet out/example.wasm --initFunction new --initArgs '{"owner_id": "example-contract.testnet", "total_supply": "10000000"}'
```

<details>
Expand Down
3 changes: 2 additions & 1 deletion bin/near
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env node
const flaggedRespawn = require('flagged-respawn');

require('v8flags')((e, flags) => {
if (e) {
throw e;
Expand All @@ -14,4 +15,4 @@ require('v8flags')((e, flags) => {
require('./near-cli.js');
}
});
})
});
48 changes: 45 additions & 3 deletions commands/account/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const inspectResponse = require('../../utils/inspect-response');
const { assertCredentials, storeCredentials } = require('../../utils/credentials');
const { DEFAULT_NETWORK } = require('../../config');
const chalk = require('chalk');
const { getPublicKeyForPath } = require('../../utils/ledger');
const { parseSeedPhrase } = require('near-seed-phrase');

module.exports = {
command: 'create-account <new-account-id>',
Expand Down Expand Up @@ -33,6 +35,33 @@ module.exports = {
type: 'string',
required: false
})
.option('seedPhrase', {
desc: 'seedPhrase from which to derive the account\'s publicKey',
type: 'string',
required: false
})
.option('signWithLedger', {
alias: ['useLedgerKey'],
desc: 'Use Ledger for signing',
type: 'boolean',
default: false
})
.option('ledgerPath', {
desc: 'Path to the Ledger key',
type: 'string',
default: "44'/397'/0'/0'/1'"
})
.option('useLedgerPK', {
alias: ['newLedgerKey'],
desc: 'Initialize the account using the public key from the Ledger',
type: 'boolean',
default: false
})
.option('PkLedgerPath', {
desc: 'Path to the Ledger key that will be added to the account',
type: 'string',
default: "44'/397'/0'/0'/1'"
})
.option('networkId', {
desc: 'Which network to use. Supports: mainnet, testnet, custom',
type: 'string',
Expand Down Expand Up @@ -64,22 +93,32 @@ async function create(options) {
throw new Error(chalk`Please specify if you want the account to be fund by a faucet (--useFaucet) or through an existing --accountId)`);
}

if (options.useLedgerPK && options.publicKey) {
throw new Error('Please specify only one of --publicKeyFromLedger or --publicKey');
}

if (options.useFaucet) {
if (options.networkId === 'mainnet') throw new Error('Pre-funding accounts is not possible on mainnet');
} else {
if (!options.useAccount) throw new Error('Please specify an account to sign the transaction (--useAccount)');
await assertCredentials(options.useAccount, options.networkId, options.keyStore);
await assertCredentials(options.useAccount, options.networkId, options.keyStore, options.useLedgerKey);
}

// assert account being created does not exist
const newAccountId = options.newAccountId;
await assertAccountDoesNotExist(newAccountId, near);

// If no public key is specified, create a random one
let keyPair;
let publicKey = options.publicKey;
let publicKey = options.useLedgerPK ? await getPublicKeyForPath(options.PkLedgerPath) : options.publicKey;

if (options.seedPhrase) {
const parsed = parseSeedPhrase(options.seedPhrase);
keyPair = KeyPair.fromString(parsed.secretKey);
publicKey = keyPair.getPublicKey();
}

if (!publicKey) {
// If no public key is specified, create a random one
keyPair = KeyPair.fromRandom('ed25519');
publicKey = keyPair.getPublicKey();
}
Expand Down Expand Up @@ -120,6 +159,9 @@ async function create(options) {

if (keyPair) {
storeCredentials(newAccountId, options.networkId, options.keyStore, keyPair, true);
} else {
console.log('Public key was provided, so we are not storing credentials (since we don\'t have the private key)');
console.log('If you have the private key, you can import it using `near add-credentials`');
}

// The faucet does not throw on error, so we force it here
Expand Down
13 changes: 12 additions & 1 deletion commands/account/delete.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ module.exports = {
aliases: ['delete'],
desc: 'Delete account, sending remaining NEAR to a beneficiary',
builder: (yargs) => yargs
.option('signWithLedger', {
alias: ['useLedgerKey'],
desc: 'Use Ledger for signing',
type: 'boolean',
default: false
})
.option('ledgerPath', {
desc: 'HD key path',
type: 'string',
default: "44'/397'/0'/0'/1'"
})
.option('networkId', {
desc: 'Which network to use. Supports: mainnet, testnet, custom',
type: 'string',
Expand All @@ -30,7 +41,7 @@ const confirmDelete = function (accountId, beneficiaryId) {
};

async function deleteAccount(options) {
await assertCredentials(options.accountId, options.networkId, options.keyStore);
await assertCredentials(options.accountId, options.networkId, options.keyStore, options.useLedgerKey);
const near = await connect(options);
const beneficiaryAccount = await near.account(options.beneficiaryId);

Expand Down
13 changes: 12 additions & 1 deletion commands/contract/call.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ module.exports = {
desc: 'Account that will execute the actions',
type: 'string'
})
.option('signWithLedger', {
alias: ['useLedgerKey'],
desc: 'Use Ledger for signing',
type: 'boolean',
default: false
})
.option('ledgerPath', {
desc: 'HD key path',
type: 'string',
default: "44'/397'/0'/0'/1'"
})
.option('networkId', {
desc: 'Which network to use. Supports: mainnet, testnet, custom',
type: 'string',
Expand Down Expand Up @@ -58,7 +69,7 @@ async function scheduleFunctionCall(options) {
options.keyStore.setKey(options.networkId, options.accountId, keyPair);
}

await assertCredentials(options.accountId, options.networkId, options.keyStore);
await assertCredentials(options.accountId, options.networkId, options.keyStore, options.useLedgerKey);

const deposit = options.depositYocto != null ? options.depositYocto : utils.format.parseNearAmount(options.deposit);
console.log(`Scheduling a call: ${options.contractName}.${options.methodName}(${options.args || ''})` +
Expand Down
7 changes: 3 additions & 4 deletions commands/contract/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ module.exports = {
handler: deploy
};

const checkExistingContract = async function (prevCodeHash) {
const askOverrideContract = async function (prevCodeHash) {
if (prevCodeHash !== '11111111111111111111111111111111') {
return await askYesNoQuestion(
chalk`{bold.white This account already has a deployed contract [ {bold.blue ${prevCodeHash}} ]. Do you want to proceed? {bold.green (y/n) }}`,
Expand All @@ -58,15 +58,14 @@ const checkExistingContract = async function (prevCodeHash) {
};

async function deploy(options) {
await assertCredentials(options.accountId, options.networkId, options.keyStore);
await assertCredentials(options.accountId, options.networkId, options.keyStore, options.useLedgerKey);

const near = await connect(options);
const account = await near.account(options.accountId);
let prevState = await account.state();
let prevCodeHash = prevState.code_hash;

const answeredYes = await checkExistingContract(prevCodeHash);
if (!options.force && !answeredYes) return;
if(!options.force && !(await askOverrideContract(prevCodeHash))) return;

console.log(`Deploying contract ${options.wasmFile} in ${options.accountId}`);

Expand Down
Loading

0 comments on commit e570a2d

Please sign in to comment.