-
Notifications
You must be signed in to change notification settings - Fork 249
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
Extend wallet-account to support signAndSendTransactionAsync, signTransaction, sendTransaction & signDelegateAction #1449
base: master
Are you sure you want to change the base?
Changes from all commits
946a51e
f0e4c72
b6c9c50
9110ee4
2baaba4
519c09d
ce2076d
ea7b0de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
"@near-js/accounts": minor | ||
"@near-js/wallet-account": minor | ||
"@near-js/providers": minor | ||
--- | ||
|
||
Extend wallet-account |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ import { KeyStore } from '@near-js/keystores'; | |
import { InMemorySigner } from '@near-js/signers'; | ||
import { FinalExecutionOutcome } from '@near-js/types'; | ||
import { baseDecode } from '@near-js/utils'; | ||
import { Transaction, Action, SCHEMA, createTransaction } from '@near-js/transactions'; | ||
import { Transaction, Action, SCHEMA, createTransaction, SignedTransaction, SignedDelegate } from '@near-js/transactions'; | ||
import { serialize } from 'borsh'; | ||
|
||
import { Near } from './near'; | ||
|
@@ -451,4 +451,193 @@ export class ConnectedWalletAccount extends Account { | |
|
||
return null; | ||
} | ||
|
||
/** | ||
* Sign and send a transaction asynchronously by redirecting to the NEAR Wallet and waiting for the result | ||
* @param options An optional options object | ||
* @param options.receiverId The NEAR account ID of the transaction receiver. | ||
* @param options.actions An array of transaction actions to be performed. | ||
* @param options.walletMeta Additional metadata to be included in the wallet signing request. | ||
* @param options.walletCallbackUrl URL to redirect upon completion of the wallet signing process. Default: current URL. | ||
*/ | ||
async signAndSendTransactionAsync({ receiverId, actions, walletMeta, walletCallbackUrl = window.location.href }: SignAndSendTransactionOptions): Promise<string> { | ||
const localKey = await this.connection.signer.getPublicKey(this.accountId, this.connection.networkId); | ||
let accessKey = await this.accessKeyForTransaction(receiverId, actions, localKey); | ||
if (!accessKey) { | ||
throw new Error(`Cannot find matching key for transaction sent to ${receiverId}`); | ||
} | ||
|
||
if (localKey && localKey.toString() === accessKey.public_key) { | ||
try { | ||
return await super.signAndSendTransactionAsync({ receiverId, actions }); | ||
} catch (e) { | ||
if (e.type === 'NotEnoughAllowance') { | ||
accessKey = await this.accessKeyForTransaction(receiverId, actions); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
} | ||
|
||
const block = await this.connection.provider.block({ finality: 'final' }); | ||
const blockHash = baseDecode(block.header.hash); | ||
const publicKey = PublicKey.from(accessKey.public_key); | ||
const nonce = Number(accessKey.access_key.nonce) + 1; | ||
const transaction = createTransaction(this.accountId, publicKey, receiverId, nonce, actions, blockHash); | ||
|
||
this.walletConnection.requestSignTransactions({ | ||
transactions: [transaction], | ||
meta: walletMeta, | ||
callbackUrl: walletCallbackUrl | ||
}); | ||
|
||
return new Promise((resolve, reject) => { | ||
setTimeout(() => { | ||
reject(new Error('Failed to redirect to sign transaction')); | ||
}, 1000); | ||
}); | ||
} | ||
|
||
/** | ||
* Sign a transaction by redirecting to the NEAR Wallet | ||
* @param options An optional options object | ||
* @param options.receiverId The NEAR account ID of the transaction receiver. | ||
* @param options.actions An array of transaction actions to be performed. | ||
* @param options.walletMeta Additional metadata to be included in the wallet signing request. | ||
* @param options.walletCallbackUrl URL to redirect upon completion of the wallet signing process. Default: current URL. | ||
*/ | ||
async signTransaction(receiverId: string, actions: Action[], walletMeta?: string, walletCallbackUrl?: string): Promise<[Uint8Array, SignedTransaction]> { | ||
const localKey = await this.connection.signer.getPublicKey(this.accountId, this.connection.networkId); | ||
let accessKey = await this.accessKeyForTransaction(receiverId, actions, localKey); | ||
if (!accessKey) { | ||
throw new Error(`Cannot find matching key for transaction sent to ${receiverId}`); | ||
} | ||
|
||
if (localKey && localKey.toString() === accessKey.public_key) { | ||
try { | ||
return await super.signTransaction(receiverId, actions); | ||
} catch (e) { | ||
if (e.type === 'NotEnoughAllowance') { | ||
accessKey = await this.accessKeyForTransaction(receiverId, actions); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
} | ||
|
||
const block = await this.connection.provider.block({ finality: 'final' }); | ||
const blockHash = baseDecode(block.header.hash); | ||
const publicKey = PublicKey.from(accessKey.public_key); | ||
const nonce = Number(accessKey.access_key.nonce) + 1; | ||
const transaction = createTransaction(this.accountId, publicKey, receiverId, nonce, actions, blockHash); | ||
|
||
this.walletConnection.requestSignTransactions({ | ||
transactions: [transaction], | ||
meta: walletMeta, | ||
callbackUrl: walletCallbackUrl | ||
}); | ||
|
||
return new Promise((resolve, reject) => { | ||
setTimeout(() => { | ||
reject(new Error('Failed to redirect to sign transaction')); | ||
}, 1000); | ||
}); | ||
} | ||
|
||
/** | ||
* Send a signed transaction to the network and wait for its completion | ||
* | ||
* @param hash The hash of the transaction to be sent | ||
* @param signedTx The signed transaction to be sent | ||
* @param walletMeta Additional metadata to be included in the wallet signing request. | ||
* @param walletCallbackUrl URL to redirect upon completion of the wallet signing process. Default: current URL. | ||
* | ||
* @returns {Promise<FinalExecutionOutcome>} A promise that resolves to the final execution outcome of the transaction | ||
*/ | ||
async sendTransaction(hash: Uint8Array, signedTx: SignedTransaction, walletMeta?: string, walletCallbackUrl?: string): Promise<FinalExecutionOutcome> { | ||
const localKey = await this.connection.signer.getPublicKey(this.accountId, this.connection.networkId); | ||
let accessKey = await this.accessKeyForTransaction(signedTx.transaction.receiverId, signedTx.transaction.actions, localKey); | ||
if (!accessKey) { | ||
throw new Error(`Cannot find matching key for transaction sent to ${signedTx.transaction.receiverId}`); | ||
} | ||
|
||
if (localKey && localKey.toString() === accessKey.public_key) { | ||
try { | ||
return await super.sendTransaction(hash, signedTx); | ||
} catch (e) { | ||
if (e.type === 'NotEnoughAllowance') { | ||
accessKey = await this.accessKeyForTransaction(signedTx.transaction.receiverId, signedTx.transaction.actions); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
} | ||
|
||
const block = await this.connection.provider.block({ finality: 'final' }); | ||
const blockHash = baseDecode(block.header.hash); | ||
const publicKey = PublicKey.from(accessKey.public_key); | ||
const nonce = Number(accessKey.access_key.nonce) + 1; | ||
const transaction = createTransaction(this.accountId, publicKey, signedTx.transaction.receiverId, nonce, signedTx.transaction.actions, blockHash); | ||
|
||
this.walletConnection.requestSignTransactions({ | ||
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. if you are sending a transaction to the network, there is no need to request another signature on it |
||
transactions: [transaction], | ||
meta: walletMeta, | ||
callbackUrl: walletCallbackUrl | ||
}); | ||
|
||
return new Promise((resolve, reject) => { | ||
setTimeout(() => { | ||
reject(new Error('Failed to redirect to sign transaction')); | ||
}, 1000); | ||
}); | ||
} | ||
|
||
/** | ||
* Sign a delegate action to be executed in a meta transaction on behalf of this account | ||
* | ||
* @param options Options for the delegate action | ||
* @param options.actions Actions to be included in the meta transaction | ||
* @param options.blockHeightTtl Number of blocks past the current block height for which the SignedDelegate action may be included in a meta transaction | ||
* @param options.receiverId Receiver account of the meta transaction | ||
* @param walletMeta Additional metadata to be included in the wallet signing request | ||
* @param walletCallbackUrl URL to redirect upon completion of the wallet signing process. Default: current URL | ||
*/ | ||
async signDelegateAction(actions: Action[], blockHeightTtl: number, receiverId: string, walletMeta?: string, walletCallbackUrl?: string): Promise<SignedDelegate> { | ||
const localKey = await this.connection.signer.getPublicKey(this.accountId, this.connection.networkId); | ||
let accessKey = await this.accessKeyForTransaction(receiverId, actions, localKey); | ||
|
||
if (!accessKey) { | ||
throw new Error(`Cannot find matching key for transaction sent to ${receiverId}`); | ||
} | ||
|
||
if (localKey && localKey.toString() === accessKey.public_key) { | ||
try { | ||
return await super.signedDelegate({ actions, blockHeightTtl, receiverId }); | ||
} catch (e) { | ||
if (e.type === 'NotEnoughAllowance') { | ||
accessKey = await this.accessKeyForTransaction(receiverId, actions); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
} | ||
|
||
const block = await this.connection.provider.block({ finality: 'final' }); | ||
const blockHash = baseDecode(block.header.hash); | ||
const publicKey = PublicKey.from(accessKey.public_key); | ||
const nonce = Number(accessKey.access_key.nonce) + 1; | ||
const transaction = createTransaction(this.accountId, publicKey, receiverId, nonce, actions, blockHash); | ||
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. don't you need to create a Delegate Transaction? |
||
|
||
this.walletConnection.requestSignTransactions({ | ||
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. is this command valid for signing delegate transactions? |
||
transactions: [transaction], | ||
meta: walletMeta, | ||
callbackUrl: walletCallbackUrl | ||
}); | ||
|
||
return new Promise((resolve, reject) => { | ||
setTimeout(() => { | ||
reject(new Error('Failed to redirect to sign delegate action')); | ||
}, 1000); | ||
}); | ||
} | ||
} |
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.
don't we need to be async as well? otherwise I think you are only making it async if there is a function call key available