diff --git a/src.ts/contracts/contract.ts b/src.ts/contracts/contract.ts index 1678621de..265314116 100644 --- a/src.ts/contracts/contract.ts +++ b/src.ts/contracts/contract.ts @@ -8,7 +8,6 @@ import { BigNumber, bigNumberify } from '../utils/bignumber'; import { hexDataLength, hexDataSlice, isHexString } from '../utils/bytes'; import { Zero } from '../utils/constants'; import { defineReadOnly, deepCopy, shallowCopy } from '../utils/properties'; -import { poll } from '../utils/web'; import * as errors from '../utils/errors'; @@ -131,8 +130,10 @@ function runMethod(contract: Contract, functionName: string, estimateOnly: boole } }); - // Send to the contract address - tx.to = contract.addressPromise; + // Send to the contract address (after checking the contract is deployed) + tx.to = contract.deployed().then(() => { + return contract.addressPromise; + }); return resolveAddresses(contract.provider, params, method.inputs).then((params) => { tx.data = method.encode(params); @@ -271,6 +272,8 @@ export class Contract { // This is only set if the contract was created with a call to deploy readonly deployTransaction: TransactionResponse; + private _deployed: Promise; + // https://github.com/Microsoft/TypeScript/issues/5453 // Once this issue is resolved (there are open PR) we can do this nicer // by making addressOrName default to null for 2 operand calls. :) @@ -339,7 +342,7 @@ export class Contract { } Object.keys(this.interface.functions).forEach((name) => { - var run = runMethod(this, name, false); + let run = runMethod(this, name, false); if ((this)[name] == null) { defineReadOnly(this, name, run); @@ -356,21 +359,32 @@ export class Contract { // @TODO: Allow timeout? deployed(): Promise { + if (!this._deployed) { - // If we were just deployed, we know the transaction we should occur in - if (this.deployTransaction) { - return this.deployTransaction.wait().then(() => { - return this; - }); + // If we were just deployed, we know the transaction we should occur in + if (this.deployTransaction) { + this._deployed = this.deployTransaction.wait().then(() => { + return this; + }); + + } else { + // @TODO: Once we allow a timeout to be passed in, we will wait + // up to that many blocks for getCode + + // Otherwise, poll for our code to be deployed + this._deployed = this.provider.getCode(this.address).then((code) => { + if (code === '0x') { + errors.throwError('contract not deployed', errors.UNSUPPORTED_OPERATION, { + contractAddress: this.address, + operation: 'getDeployed' + }); + } + return this; + }); + } } - // Otherwise, poll for our code to be deployed - return poll(() => { - return this.provider.getCode(this.address).then((code) => { - if (code === '0x') { return undefined; } - return this; - }); - }, { onceBlock: this.provider }); + return this._deployed; } // @TODO: @@ -392,12 +406,18 @@ export class Contract { }); tx.to = this.addressPromise; - return this.signer.sendTransaction(tx); + return this.deployed().then(() => { + return this.signer.sendTransaction(tx); + }); } // Reconnect to a different signer or provider connect(signerOrProvider: Signer | Provider): Contract { - return new Contract(this.address, this.interface, signerOrProvider); + let contract = new Contract(this.address, this.interface, signerOrProvider); + if (this.deployTransaction) { + defineReadOnly(contract, 'deployTransaction', this.deployTransaction); + } + return contract; } // Re-attach to a different on=chain instance of this contract diff --git a/src.ts/utils/index.ts b/src.ts/utils/index.ts index 084d2326f..e6f87e2c6 100644 --- a/src.ts/utils/index.ts +++ b/src.ts/utils/index.ts @@ -14,7 +14,7 @@ import { randomBytes } from './random-bytes'; import { getNetwork } from './networks'; import { deepCopy, defineReadOnly, resolveProperties, shallowCopy } from './properties'; import * as RLP from './rlp'; -import { computePublicKey, verifyMessage } from './secp256k1'; +import { computePublicKey, computeSharedSecret, verifyMessage } from './secp256k1'; import { parse as parseTransaction, serialize as serializeTransaction } from './transaction'; import { formatBytes32String, parseBytes32String, toUtf8Bytes, toUtf8String } from './utf8'; import { formatEther, parseEther, formatUnits, parseUnits } from './units'; @@ -91,6 +91,7 @@ export { getJsonWalletAddress, computePublicKey, + computeSharedSecret, verifyMessage } diff --git a/src.ts/utils/secp256k1.ts b/src.ts/utils/secp256k1.ts index ef2407b0e..8635b98db 100644 --- a/src.ts/utils/secp256k1.ts +++ b/src.ts/utils/secp256k1.ts @@ -101,10 +101,14 @@ export function computeAddress(key: string): string { } -export function verifyMessage(message: Arrayish | string, signature: Signature | string): string { - let sig = splitSignature(signature); - let digest = hashMessage(message); +export function computeSharedSecret(privateKey: Arrayish, publicKey: Arrayish): string { + let privateKeyPair = getCurve().keyFromPrivate(arrayify(privateKey)); + let publicKeyPair = getCurve().keyFromPublic(arrayify(publicKey)); + return hexZeroPad('0x' + privateKeyPair.derive(publicKeyPair.getPublic()).toString(16), 32); +} +export function verifyDigest(digest: Arrayish | string, signature: Signature | string): string { + let sig = splitSignature(signature); return recoverAddress( digest, { @@ -114,3 +118,7 @@ export function verifyMessage(message: Arrayish | string, signature: Signature | } ); } + +export function verifyMessage(message: Arrayish | string, signature: Signature | string): string { + return verifyDigest(hashMessage(message), signature); +}