diff --git a/CHANGELOG.md b/CHANGELOG.md index fedd5e94..920885d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Changed - Use `feeRecipient` as given by the provider instead of zeroAddress for meta-tx +- Use random none generation at the identity wallet for meta transactions ### Added @@ -17,13 +18,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). where the fee payer is the loaded user - Add `extraData` to `TransferDetails`, it contains the raw `extraData` that was present in the transfer for which details are returned -- Add `paymentRequestId` and `transferId` to `TransferDetails` corresponding to the decoded `extraData` -- Add option to `Payment.preparePayment` function `options.addTransferId: boolean = true` that signals whether - a `transferId` should be generated and added to the payment's extraData. -- Add `transferId` to the returned values of `Payment.preparePayment` +- Add `paymentRequestId` and `messageId` to `TransferDetails` corresponding to the decoded `extraData` +- Add option to `Payment.preparePayment` function `options.addMessageId: boolean = true` that signals whether + a messageId should be generated and added to the payment's extraData. +- Add `messageId` to the returned values of `Payment.preparePayment` - Add function `Payment.confirmPayment` to confirm any `PaymentTxObject` returned by `prepare` and potentially send a payment message along with the payment -- Add function `Messaging.paymentMessage` to send a payment message for a `transferId` to a counterparty address +- Add function `Messaging.paymentMessage` to send a payment message for a messageId to a counterparty address +- Add optional field to the `TLNetworkConfig` for the nonce mechanism to use ### Deprecated diff --git a/src/TLNetwork.ts b/src/TLNetwork.ts index 03467c59..a71c326c 100644 --- a/src/TLNetwork.ts +++ b/src/TLNetwork.ts @@ -27,7 +27,7 @@ import { import utils from './utils' -import { ProviderUrl, TLNetworkConfig } from './typings' +import { NonceMechanism, ProviderUrl, TLNetworkConfig } from './typings' import { IdentityWallet } from './wallets/IdentityWallet' /** @@ -141,7 +141,8 @@ export class TLNetwork { identityFactoryAddress, identityImplementationAddress, walletType = WALLET_TYPE_ETHERS, - chainId + chainId, + nonceMechanism = NonceMechanism.Random } = config const defaultUrlParameters: ProviderUrl = { @@ -162,10 +163,14 @@ export class TLNetwork { ) ) - this.setWallet(walletType, this.relayProvider, chainId, { + this.setWallet( + walletType, + this.relayProvider, + chainId, identityFactoryAddress, - identityImplementationAddress - }) + identityImplementationAddress, + nonceMechanism + ) this.setSigner(web3Provider, this.wallet) this.currencyNetwork = new CurrencyNetwork(this.relayProvider) @@ -264,15 +269,20 @@ export class TLNetwork { walletType: string, provider: TLProvider, chainId: number, - { identityFactoryAddress, identityImplementationAddress } + identityFactoryAddress: string, + identityImplementationAddress: string, + nonceMechanism: NonceMechanism ): void { let wallet: TLWallet if (walletType === WALLET_TYPE_IDENTITY) { - wallet = new IdentityWallet(provider, chainId, { + wallet = new IdentityWallet( + provider, + chainId, identityFactoryAddress, - identityImplementationAddress - }) + identityImplementationAddress, + nonceMechanism + ) } else if (walletType === WALLET_TYPE_ETHERS) { wallet = new EthersWallet(provider) } else { diff --git a/src/typings.ts b/src/typings.ts index d2b0ab4b..253785db 100644 --- a/src/typings.ts +++ b/src/typings.ts @@ -33,6 +33,10 @@ export interface TLNetworkConfig { * Chain id used in the signature of meta-tx */ chainId?: number + /** + * Mechanism how to generate nonces for identity meta-tx + */ + nonceMechanism?: NonceMechanism } export interface ProviderUrl { @@ -586,6 +590,11 @@ export interface IdentityWalletData extends TLWalletData { meta: TLWalletDataMeta } +export enum NonceMechanism { + Random = 'random', + Counting = 'counting' +} + // TRUSTLINE export interface TrustlineObject { id: string diff --git a/src/wallets/IdentityWallet.ts b/src/wallets/IdentityWallet.ts index 865602c4..22b57534 100644 --- a/src/wallets/IdentityWallet.ts +++ b/src/wallets/IdentityWallet.ts @@ -16,6 +16,7 @@ import { IdentityWalletData, MetaTransaction, MetaTransactionFees, + NonceMechanism, RawTxObject, Signature, TransactionStatusObject, @@ -43,16 +44,20 @@ export class IdentityWallet implements TLWallet { private identityAddress: string private identityFactoryAddress: string private identityImplementationAddress: string + private nonceMechanism: NonceMechanism private chainId: number private identityVersion = 1 constructor( provider: TLProvider, chainId: number, - { identityFactoryAddress, identityImplementationAddress } + identityFactoryAddress: string, + identityImplementationAddress: string, + nonceMechanism: NonceMechanism ) { this.identityFactoryAddress = identityFactoryAddress this.identityImplementationAddress = identityImplementationAddress + this.nonceMechanism = nonceMechanism this.provider = provider this.chainId = chainId } @@ -337,7 +342,7 @@ export class IdentityWallet implements TLWallet { } public async prepareTransaction(rawTx: RawTxObject): Promise { - rawTx.nonce = getRandomNonce() + rawTx.nonce = await this.getNonce() // Must take place before the fee calculation! const metaTxFees = await this.getMetaTxFees(rawTx) @@ -425,6 +430,22 @@ export class IdentityWallet implements TLWallet { } } + public async getNonce(): Promise { + switch (this.nonceMechanism) { + case NonceMechanism.Random: + return getRandomNonce() + + case NonceMechanism.Counting: + const nonce = await this.provider.getIdentityNonce(this.address) + return nonce.toString() + + default: + throw new Error( + `Can not generate nonce for unknown mechanism: ${this.nonceMechanism}` + ) + } + } + /** * Takes a string hash and signs it using the loaded wallet without appending `\x19Ethereum Signed Message:\n` to it * and hashing it again, contrary to what ethers.sign or ethers.signMessage does. diff --git a/tests/e2e/Identity.test.ts b/tests/e2e/Identity.test.ts index 12539b2d..9a7d8661 100644 --- a/tests/e2e/Identity.test.ts +++ b/tests/e2e/Identity.test.ts @@ -19,7 +19,7 @@ import { wait } from '../Fixtures' -import { FeePayer, RawTxObject } from '../../src/typings' +import { FeePayer, NonceMechanism, RawTxObject } from '../../src/typings' import { TLNetwork } from '../../src/TLNetwork' @@ -141,10 +141,9 @@ describe('e2e', () => { const secondWallet = new IdentityWallet( relayProvider, tlNetworkConfigIdentity.chainId, - { - identityFactoryAddress, - identityImplementationAddress - } + identityFactoryAddress, + identityImplementationAddress, + NonceMechanism.Random ) const walletData = await secondWallet.create() await secondWallet.loadFrom(walletData) diff --git a/tests/unit/IdentityWallet.test.ts b/tests/unit/IdentityWallet.test.ts index a60bf018..d50e898e 100644 --- a/tests/unit/IdentityWallet.test.ts +++ b/tests/unit/IdentityWallet.test.ts @@ -23,7 +23,7 @@ import { USER_1_IDENTITY_WALLET_V1 } from '../Fixtures' -import { MetaTransaction } from '../../src/typings' +import { MetaTransaction, NonceMechanism } from '../../src/typings' import { calculateIdentityAddress, getRandomNonce, @@ -43,10 +43,13 @@ describe('unit', () => { const init = () => { fakeTLProvider = new FakeTLProvider() - identityWallet = new IdentityWallet(fakeTLProvider, FAKE_CHAIN_ID, { - identityFactoryAddress: IDENTITY_FACTORY_ADDRESS, - identityImplementationAddress: IDENTITY_IMPLEMENTATION_ADDRESS - }) + identityWallet = new IdentityWallet( + fakeTLProvider, + FAKE_CHAIN_ID, + IDENTITY_FACTORY_ADDRESS, + IDENTITY_IMPLEMENTATION_ADDRESS, + NonceMechanism.Random + ) } describe('#create()', () => { @@ -230,6 +233,34 @@ describe('unit', () => { ) }) }) + + describe('#getNonce', () => { + beforeEach(() => init()) + + it('should generate nonce with random mechanism', async () => { + const randomIdentityWallet = new IdentityWallet( + fakeTLProvider, + FAKE_CHAIN_ID, + IDENTITY_FACTORY_ADDRESS, + IDENTITY_IMPLEMENTATION_ADDRESS, + NonceMechanism.Random + ) + assert.isString(await randomIdentityWallet.getNonce()) + }) + + it('should generate nonce with counting mechanism', async () => { + const countingIdentityWallet = new IdentityWallet( + fakeTLProvider, + FAKE_CHAIN_ID, + IDENTITY_FACTORY_ADDRESS, + IDENTITY_IMPLEMENTATION_ADDRESS, + NonceMechanism.Counting + ) + const walletData = await countingIdentityWallet.create() + await countingIdentityWallet.loadFrom(walletData) + assert.isString(await countingIdentityWallet.getNonce()) + }) + }) }) describe('#getRandomNonce', () => {