forked from near/near-api-js
-
Notifications
You must be signed in to change notification settings - Fork 0
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
Multi contract keystore #17
Open
gtsonevv
wants to merge
7
commits into
master
Choose a base branch
from
multi-contract-keystore
base: master
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.
+260
−1
Open
Changes from 2 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
aaa833a
feat: add multi_contract_keystore
gtsonevv 29f3fcc
fix: fix linter errors
gtsonevv 1024f1b
fix: fix var types
gtsonevv e5aa9b1
fix: add missing methid in MultiContractKeyStore
gtsonevv 82674d6
Merge branch 'master' into multi-contract-keystore
gtsonevv 0ad37fd
fix: prefix value
gtsonevv 5148b67
Merge branch 'master' into multi-contract-keystore
gtsonevv 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 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 @@ | ||
--- | ||
"@near-js/keystores": minor | ||
"@near-js/keystores-browser": minor | ||
--- | ||
|
||
Add multi_contract_keystore |
This file contains 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 |
---|---|---|
@@ -1 +1,2 @@ | ||
export { BrowserLocalStorageKeyStore } from './browser_local_storage_key_store'; | ||
export { MultiContractBrowserLocalStorageKeyStore } from './multi_contract_browser_local_storage_key_store'; |
159 changes: 159 additions & 0 deletions
159
packages/keystores-browser/src/multi_contract_browser_local_storage_key_store.ts
This file contains 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,159 @@ | ||||||
import { KeyPair } from '@near-js/crypto'; | ||||||
import { MultiContractKeyStore } from '@near-js/keystores'; | ||||||
|
||||||
const LOCAL_STORAGE_KEY_PREFIX = 'near-api-js:keystore:'; | ||||||
|
||||||
/** | ||||||
* This class is used to store keys in the browsers local storage. | ||||||
* | ||||||
* @see [https://docs.near.org/docs/develop/front-end/naj-quick-reference#key-store](https://docs.near.org/docs/develop/front-end/naj-quick-reference#key-store) | ||||||
* @example | ||||||
* ```js | ||||||
* import { connect, keyStores } from 'near-api-js'; | ||||||
* | ||||||
* const keyStore = new keyStores.MultiContractBrowserLocalStorageKeyStore(); | ||||||
* const config = { | ||||||
* keyStore, // instance of MultiContractBrowserLocalStorageKeyStore | ||||||
* networkId: 'testnet', | ||||||
* nodeUrl: 'https://rpc.testnet.near.org', | ||||||
* walletUrl: 'https://wallet.testnet.near.org', | ||||||
* helperUrl: 'https://helper.testnet.near.org', | ||||||
* explorerUrl: 'https://explorer.testnet.near.org' | ||||||
* }; | ||||||
* | ||||||
* // inside an async function | ||||||
* const near = await connect(config) | ||||||
* ``` | ||||||
*/ | ||||||
export class MultiContractBrowserLocalStorageKeyStore extends MultiContractKeyStore { | ||||||
/** @hidden */ | ||||||
private localStorage: any; | ||||||
/** @hidden */ | ||||||
private prefix: string; | ||||||
|
||||||
/** | ||||||
* @param localStorage defaults to window.localStorage | ||||||
* @param prefix defaults to `near-api-js:keystore:` | ||||||
*/ | ||||||
constructor(localStorage: any = window.localStorage, prefix = LOCAL_STORAGE_KEY_PREFIX) { | ||||||
super(); | ||||||
this.localStorage = localStorage; | ||||||
this.prefix = prefix; | ||||||
} | ||||||
|
||||||
/** | ||||||
* Stores a {@link utils/key_pair!KeyPair} in local storage. | ||||||
* @param networkId The targeted network. (ex. default, betanet, etc…) | ||||||
* @param accountId The NEAR account tied to the key pair | ||||||
* @param keyPair The key pair to store in local storage | ||||||
* @param contractId The contract to store in local storage | ||||||
*/ | ||||||
async setKey(networkId: string, accountId: string, keyPair: KeyPair, contractId: string): Promise<void> { | ||||||
this.localStorage.setItem(this.storageKeyForSecretKey(networkId, accountId, contractId), keyPair.toString()); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Gets a {@link utils/key_pair!KeyPair} from local storage | ||||||
* @param networkId The targeted network. (ex. default, betanet, etc…) | ||||||
* @param accountId The NEAR account tied to the key pair | ||||||
* @param contractId The NEAR contract tied to the key pair | ||||||
* @returns {Promise<KeyPair>} | ||||||
*/ | ||||||
async getKey(networkId: string, accountId: string, contractId: string): Promise<KeyPair | null> { | ||||||
const value = this.localStorage.getItem(this.storageKeyForSecretKey(networkId, accountId, contractId)); | ||||||
if (!value) { | ||||||
return null; | ||||||
} | ||||||
return KeyPair.fromString(value); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Removes a {@link utils/key_pair!KeyPair} from local storage | ||||||
* @param networkId The targeted network. (ex. default, betanet, etc…) | ||||||
* @param accountId The NEAR account tied to the key pair | ||||||
* @param contractId The NEAR contract tied to the key pair | ||||||
*/ | ||||||
async removeKey(networkId: string, accountId: string, contractId: string): Promise<void> { | ||||||
this.localStorage.removeItem(this.storageKeyForSecretKey(networkId, accountId, contractId)); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Removes all items that start with `prefix` from local storage | ||||||
*/ | ||||||
async clear(): Promise<void> { | ||||||
for (const key of this.storageKeys()) { | ||||||
if (key.startsWith(this.prefix)) { | ||||||
this.localStorage.removeItem(key); | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
/** | ||||||
* Get the network(s) from local storage | ||||||
* @returns {Promise<string[]>} | ||||||
*/ | ||||||
async getNetworks(): Promise<string[]> { | ||||||
const result = new Set<string>(); | ||||||
for (const key of this.storageKeys()) { | ||||||
if (key.startsWith(this.prefix)) { | ||||||
const parts = key.substring(this.prefix.length).split(':'); | ||||||
result.add(parts[1]); | ||||||
} | ||||||
} | ||||||
return Array.from(result.values()); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Gets the account(s) from local storage | ||||||
* @param networkId The targeted network. (ex. default, betanet, etc…) | ||||||
*/ | ||||||
async getAccounts(networkId: string): Promise<string[]> { | ||||||
const result = new Array<string>(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
for (const key of this.storageKeys()) { | ||||||
if (key.startsWith(this.prefix)) { | ||||||
const parts = key.substring(this.prefix.length).split(':'); | ||||||
if (parts[1] === networkId) { | ||||||
result.push(parts[0]); | ||||||
} | ||||||
} | ||||||
} | ||||||
return result; | ||||||
} | ||||||
|
||||||
/** | ||||||
* Gets the contract(s) from local storage | ||||||
* @param networkId The targeted network. (ex. default, betanet, etc…) | ||||||
* @param accountId The targeted account. | ||||||
*/ | ||||||
async getContracts(networkId: string, accountId: string): Promise<string[]> { | ||||||
const result = new Array<string>(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
for (const key of this.storageKeys()) { | ||||||
if (key.startsWith(this.prefix)) { | ||||||
const parts = key.substring(this.prefix.length).split(':'); | ||||||
if (parts[1] === networkId && parts[0] === accountId) { | ||||||
result.push(parts[2]); | ||||||
} | ||||||
} | ||||||
} | ||||||
return result; | ||||||
} | ||||||
|
||||||
/** | ||||||
* @hidden | ||||||
* Helper function to retrieve a local storage key | ||||||
* @param networkId The targeted network. (ex. default, betanet, etc…) | ||||||
* @param accountId The NEAR account tied to the storage keythat's sought | ||||||
* @param contractId The NEAR contract tied to the storage keythat's sought | ||||||
* @returns {string} An example might be: `near-api-js:keystore:near-friend:default` | ||||||
*/ | ||||||
private storageKeyForSecretKey(networkId: string, accountId: string, contractId: string): string { | ||||||
return `${this.prefix}${accountId}:${networkId}:${contractId}`; | ||||||
} | ||||||
|
||||||
/** @hidden */ | ||||||
private *storageKeys(): IterableIterator<string> { | ||||||
for (let i = 0; i < this.localStorage.length; i++) { | ||||||
yield this.localStorage.key(i); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
} | ||||||
} |
This file contains 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
64 changes: 64 additions & 0 deletions
64
packages/keystores-browser/test/multi_contract_browser_keystore_common.js
This file contains 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,64 @@ | ||
const { KeyPairEd25519 } = require('@near-js/crypto'); | ||
|
||
const NETWORK_ID = 'networkid'; | ||
const ACCOUNT_ID = 'accountid'; | ||
const CONTRACT_ID = 'contractid'; | ||
const KEYPAIR = new KeyPairEd25519('2wyRcSwSuHtRVmkMCGjPwnzZmQLeXLzLLyED1NDMt4BjnKgQL6tF85yBx6Jr26D2dUNeC716RBoTxntVHsegogYw'); | ||
|
||
module.exports.shouldStoreAndRetrieveKeys = ctx => { | ||
beforeEach(async () => { | ||
await ctx.keyStore.clear(); | ||
await ctx.keyStore.setKey(NETWORK_ID, ACCOUNT_ID, KEYPAIR, CONTRACT_ID); | ||
}); | ||
|
||
test('Get all keys with empty network returns empty list', async () => { | ||
const emptyList = await ctx.keyStore.getAccounts('emptynetwork'); | ||
expect(emptyList).toEqual([]); | ||
}); | ||
|
||
test('Get all keys with single key in keystore', async () => { | ||
const accountIds = await ctx.keyStore.getAccounts(NETWORK_ID); | ||
expect(accountIds).toEqual([ACCOUNT_ID]); | ||
}); | ||
|
||
test('Get not-existing account', async () => { | ||
expect(await ctx.keyStore.getKey('somenetwork', 'someaccount', 'somecontract')).toBeNull(); | ||
}); | ||
|
||
test('Get account id from a network with single key', async () => { | ||
const key = await ctx.keyStore.getKey(NETWORK_ID, ACCOUNT_ID, CONTRACT_ID); | ||
expect(key).toEqual(KEYPAIR); | ||
}); | ||
|
||
test('Get networks', async () => { | ||
const networks = await ctx.keyStore.getNetworks(); | ||
expect(networks).toEqual([NETWORK_ID]); | ||
}); | ||
|
||
test('Get accounts', async () => { | ||
const accounts = await ctx.keyStore.getAccounts(NETWORK_ID); | ||
expect(accounts).toEqual([ACCOUNT_ID]); | ||
}); | ||
|
||
test('Get contracts', async () => { | ||
const contracts = await ctx.keyStore.getContracts(NETWORK_ID, ACCOUNT_ID); | ||
expect(contracts).toEqual([CONTRACT_ID]); | ||
}); | ||
|
||
test('Add two contracts to account and retrieve them', async () => { | ||
const networkId = 'network'; | ||
const accountId = 'account'; | ||
const contract1 = 'contract1'; | ||
const contract2 = 'contract2'; | ||
const key1Expected = KeyPairEd25519.fromRandom(); | ||
const key2Expected = KeyPairEd25519.fromRandom(); | ||
await ctx.keyStore.setKey(networkId, accountId, key1Expected, contract1); | ||
await ctx.keyStore.setKey(networkId, accountId, key2Expected, contract2); | ||
const key1 = await ctx.keyStore.getKey(networkId, accountId, contract1); | ||
const key2 = await ctx.keyStore.getKey(networkId, accountId, contract2); | ||
expect(key1).toEqual(key1Expected); | ||
expect(key2).toEqual(key2Expected); | ||
const contractIds = await ctx.keyStore.getContracts(networkId, accountId); | ||
expect(contractIds).toEqual([contract1, contract2]); | ||
}); | ||
}; |
This file contains 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 |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export { InMemoryKeyStore } from './in_memory_key_store'; | ||
export { KeyStore } from './keystore'; | ||
export { MergeKeyStore } from './merge_key_store'; | ||
export { MultiContractKeyStore } from './multi_contract_keystore'; |
This file contains 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,16 @@ | ||
import { KeyPair } from '@near-js/crypto'; | ||
|
||
/** | ||
* KeyStores are passed to {@link near!Near} via {@link near!NearConfig} | ||
* and are used by the {@link signer!InMemorySigner} to sign transactions. | ||
* | ||
* @see {@link connect} | ||
*/ | ||
export abstract class MultiContractKeyStore { | ||
abstract setKey(networkId: string, accountId: string, keyPair: KeyPair, contractId: string): Promise<void>; | ||
abstract getKey(networkId: string, accountId: string, contractId: string): Promise<KeyPair | null>; | ||
abstract removeKey(networkId: string, accountId: string, contractId: string): Promise<void>; | ||
abstract clear(): Promise<void>; | ||
abstract getNetworks(): Promise<string[]>; | ||
abstract getAccounts(networkId: string, contractId: string): Promise<string[]>; | ||
} |
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.