Skip to content
This repository has been archived by the owner on May 24, 2022. It is now read-only.

Rewrite post$ to take a password and not use signer #93

Merged
merged 12 commits into from
Jan 22, 2019
5 changes: 5 additions & 0 deletions packages/api/src/rpc/personal/personal.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ class Personal {
.then(outAddress);
}

signTransaction(options, password) {
return this._provider
.send('personal_signTransaction', inOptions(options), password);
}

sendTransaction (options, password) {
return this._provider
.send('personal_sendTransaction', inOptions(options), password);
Expand Down
16 changes: 10 additions & 6 deletions packages/light.js/docs/guides/tutorial4-send-a-transaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,19 @@ Creating a new transaction is a simple affair. The function `post$` takes as fir
- `gasPrice` The price (in Wei) per unit of gas. Will default to something sensible (in Parity's case, this is the median of the distribution formed from the last several hundred transactions).
- `nonce` The sender nonce of the transaction. Will default to the latest known value.

The second (optional) argument describes options to pass to this function. It is an object with the following keys:
The second argument describes options to pass to this function. It is an object with the following keys:

- `estimate`: Will estimate the amount of gas required for this transaction first.
- `passphrase`: The Parity Ethereum passphrase for your `from` account. If you prefer to sign the transaction yourself, you can use the `postRaw$` function instead.
- `estimate` (optional): Will estimate the amount of gas required for this transaction first.

### Return Value

The value to which the RpcObservable evaluates reflects the ongoing status of the transaction as it moves through the process of getting finalised. It is always an object with a single key/value. It can be:

- `{"estimating": true}` The amount of gas required for this transaction to execute is being determined (only fired if `estimate` is set to `true`).
- `{"estimated": value}` The amount of gas required for this transaction to execute has been determined as value; the user is about to be asked (only fired if `estimate` is set to `true`).
- `{"requested": id}` The user has been asked for approval of the transaction; id is the (numeric) identity of the request.
- `{"signed": hash}` The user has approved the transaction and the transaction has been signed to form a final transaction hash of hash. It is now ready to be sent to the network for inclusion in a new block.
- `{"signed": hash}` The transaction has been signed with the given passphrase. It is now ready to be sent to the network for inclusion in a new block.
- `{"sent": hash}` This is the hash of the transaction.
- `{"confirmed": receipt}` The transaction has been confirmed into a block. The receipt of the transaction is provided as receipt, giving information concerning its operation, including any logs.
- `{"failed": error}` The transaction has failed. Generally this is because the user did not approve the transaction or otherwise signing could not take place. error is a string which contains any details regarding the situation.

Expand All @@ -37,12 +38,15 @@ The value to which the RpcObservable evaluates reflects the ongoing status of th
import { post$ } from '@parity/light.js';

post$({
from: '0x921ceff422ef827110ac9dde154fbae2ac4eec9d',
to: '0x180fbce524fd79b4af8dccf83809acd9bc95fd1a',
value: 100 * 1e15 // value in wei
}, {
passphrase: 'mypassphrase'
}).subscribe(console.log);

// Logs:
// { requested: '0x1' }
// { signed: '0x123...ff' }
// { sent: '0x456...ff' }
// { confirmed: {/* receipt object */} }
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,17 @@ gavcoinContract
'0x407d73d8a49eeb85d32cf465507dd71d507100c1', // The "to" address
new BigNumber(2.01) // The amount to transfer
],
// This 2nd argument is optional, and is an options object containing the following fields:
// This 2nd argument is an options object containing the following fields:
{
from: "0x...",
passphrase: "mypassphrase",
gasPrice: ...
}
)
.subscribe(transfer => console.log('transfer$', transfer));

// Logs:
// transfer$ { requested: '0x2' }
// transfer$ { signed: '0x123...ff' }
// transfer$ { sent: '0x456...ff' }
// transfer$ { confirmed: {/* receipt object */} }
```
8 changes: 5 additions & 3 deletions packages/light.js/src/rpc/other/makeContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const getContract = memoizee(
);

/**
* Create a contract, givan an api object.
* Create a contract, given an api object.
* Pure function version of {@link makeContract}.
*
* @ignore
Expand Down Expand Up @@ -89,15 +89,17 @@ const makeContractWithApi = memoizee(
]
})({ provider: api.provider })(...args);
} else {
const { estimate, passphrase, ...txFields } = options;

return post$({
to: address,
data: abiEncode(
method.name,
method.inputs.map(({ kind: { type } }: any) => type), // TODO Use @parity/api types
args
),
...options
});
...txFields
}, { estimate, passphrase });
}
};
});
Expand Down
45 changes: 21 additions & 24 deletions packages/light.js/src/rpc/other/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { RpcObservableOptions, Tx, TxStatus } from '../../types';

interface PostOptions extends RpcObservableOptions {
estimate?: boolean;
passphrase: String;
}

function getTransactionReceipt (transactionHash: string, api: any) {
Expand All @@ -37,15 +38,21 @@ function getTransactionReceipt (transactionHash: string, api: any) {
/**
* Post a transaction to the network.
*
* Calls, in this order, `eth_estimateGas`, `parity_postTransaction`,
* `parity_checkRequest` and `eth_getTransactionReceipt` to get the status of
* Calls, in this order, `eth_estimateGas`, `personal_signTransaction`,
* `eth_sendRawTransaction` and `eth_getTransactionReceipt` to get the status of
* the transaction.
*
* @param options? - Options to pass to the {@link RpcObservable}.
* @return - The status of the transaction.
* @param tx - Transaction object
* @param options - Options to pass to the {@link RpcObservable}.
* @param options.passphrase - Passphrase of the account
* @return - The status of the transaction: (estimated), signed, sent, confirmed
*/
export function post$ (tx: Tx, options: PostOptions = {}) {
const { estimate, provider } = options;
export function post$ (tx: Tx, options: PostOptions) {
if (!options || !options.passphrase) {
throw new Error('The passphrase is missing from the options');
}

const { estimate, passphrase, provider } = options;
const api = provider ? createApiFromProvider(provider) : getApi();

const source$ = Observable.create(async (observer: Observer<TxStatus>) => {
Expand All @@ -55,22 +62,11 @@ export function post$ (tx: Tx, options: PostOptions = {}) {
const gas = await api.eth.estimateGas(tx);
observer.next({ estimated: gas });
}
const signerRequestId = await api.parity.postTransaction(tx);
observer.next({ requested: signerRequestId });
const transactionHash = await api.pollMethod(
'parity_checkRequest',
signerRequestId
);
if (tx.condition) {
observer.next({ signed: transactionHash, schedule: tx.condition });
} else {
observer.next({ signed: transactionHash });

const receipt = await getTransactionReceipt(transactionHash, api);
observer.next({ confirmed: receipt });
}
const signedTransaction = await api.personal.signTransaction(tx, passphrase);
observer.next({ signed: signedTransaction.raw });
postRaw$(signedTransaction.raw).subscribe(observer);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a breaking change, what do you think of putting observer.next({ signed: ... }) here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good!


observer.complete();
} catch (error) {
observer.next({ failed: error });
observer.error(error);
Expand All @@ -87,17 +83,18 @@ export function post$ (tx: Tx, options: PostOptions = {}) {
* Calls, in this order, `eth_sendRawTransaction` and
* `eth_getTransactionReceipt` to get the status of the transaction.
*
* @param rawTx - Raw transaction
* @param options? - Options to pass to the {@link RpcObservable}.
* @return - The status of the transaction.
* @return - The status of the transaction: sent, confirmed
*/
export function postRaw$ (tx: string, options: PostOptions = {}) {
export function postRaw$ (rawTx: string, options: RpcObservableOptions = {}) {
const { provider } = options;
const api = provider ? createApiFromProvider(provider) : getApi();

const source$ = Observable.create(async (observer: Observer<TxStatus>) => {
try {
const transactionHash = await api.eth.sendRawTransaction(tx);
observer.next({ signed: transactionHash });
const transactionHash = await api.eth.sendRawTransaction(rawTx);
observer.next({ sent: transactionHash });

const receipt = await getTransactionReceipt(transactionHash, api);
observer.next({ confirmed: receipt });
Expand Down
8 changes: 5 additions & 3 deletions packages/light.js/src/rpc/rpc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,22 @@ const testRpc = (name: string, rpc$: RpcObservable<any, any>) =>
setApi(resolveApi());
});

const options = name === 'post$' ? { passphrase: 'passphrase' } : {};

it('should be a function', () => {
expect(typeof rpc$).toBe('function');
});

it('should return an Observable', () => {
expect(isObservable(rpc$({}))).toBe(true);
expect(isObservable(rpc$({}, options))).toBe(true);
});

it('result Observable should be subscribable', () => {
expect(() => rpc$({}).subscribe()).not.toThrow();
expect(() => rpc$({}, options).subscribe()).not.toThrow();
});

it('result Observable should return values', done => {
rpc$({}).subscribe(data => {
rpc$({}, options).subscribe(data => {
expect(data).not.toBeNull();
done();
});
Expand Down
3 changes: 1 addition & 2 deletions packages/light.js/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ export interface TxStatus {
estimating?: boolean;
estimated?: BigNumber;
failed?: Error;
requested?: string;
schedule?: any;
signed?: string;
sent?: string;
}
2 changes: 1 addition & 1 deletion scripts/lerna-publish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ set -e # Quits if there's an error

git remote set-url origin https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git > /dev/null 2>&1

lerna version patch --yes -m "[ci skip] Publish %s"
lerna version --conventional-commits --yes -m "[ci skip] Publish %s"
lerna publish from-git --yes