From 4c1b27ccce0abd1bcdd54c44498b924864a1836b Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Wed, 23 Aug 2023 17:29:38 +0200
Subject: [PATCH 1/3] feat!: seperate signer from provider in apis
---
package.json | 2 +-
src/agents/coordinator.ts | 16 ++++---
src/agents/subscription-manager.ts | 12 ++---
src/characters/alice.ts | 21 ++++-----
src/characters/cbd-recipient.ts | 11 +++--
src/characters/pre-recipient.ts | 18 +++++---
src/conditions/condition-expr.ts | 5 ++-
src/conditions/context/context.ts | 17 ++++++--
src/conditions/context/providers.ts | 23 ++++++----
src/dkg.ts | 20 +++++----
src/policies/policy.ts | 20 +++++----
src/sdk/strategy/cbd-strategy.ts | 8 ++--
src/sdk/strategy/pre-strategy.ts | 6 ++-
test/acceptance/alice-grants.test.ts | 8 ++--
test/acceptance/delay-enact.test.ts | 10 +++--
test/docs/cbd.test.ts | 16 +++----
test/integration/dkg-client.test.ts | 6 +--
test/unit/cbd-strategy.test.ts | 9 ++--
test/unit/conditions/base/contract.test.ts | 9 ++--
test/unit/conditions/context.test.ts | 16 +++----
test/unit/pre-strategy.test.ts | 20 ++++++---
test/utils.ts | 51 ++++++++++++----------
yarn.lock | 2 +-
23 files changed, 192 insertions(+), 134 deletions(-)
diff --git a/package.json b/package.json
index 8af1b16fe..13e51c8dd 100644
--- a/package.json
+++ b/package.json
@@ -55,7 +55,7 @@
"@nucypher/nucypher-core": "^0.12.0",
"axios": "^0.21.1",
"deep-equal": "^2.2.1",
- "ethers": "^5.4.1",
+ "ethers": "^5.7.2",
"joi": "^17.7.0",
"qs": "^6.10.1",
"semver": "^7.5.2"
diff --git a/src/agents/coordinator.ts b/src/agents/coordinator.ts
index 830376c98..a69e1d85b 100644
--- a/src/agents/coordinator.ts
+++ b/src/agents/coordinator.ts
@@ -57,10 +57,11 @@ export class DkgCoordinatorAgent {
}
public static async initializeRitual(
- provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer,
providers: ChecksumAddress[]
): Promise {
- const Coordinator = await this.connectReadWrite(provider);
+ const Coordinator = await this.connectReadWrite(provider, signer);
const tx = await Coordinator.initiateRitual(providers);
const txReceipt = await tx.wait(DEFAULT_WAIT_N_CONFIRMATIONS);
const [ritualStartEvent] = txReceipt.events ?? [];
@@ -79,7 +80,7 @@ export class DkgCoordinatorAgent {
}
public static async getRitualState(
- provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
ritualId: number
): Promise {
const Coordinator = await this.connectReadOnly(provider);
@@ -87,7 +88,7 @@ export class DkgCoordinatorAgent {
}
public static async onRitualEndEvent(
- provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
ritualId: number,
callback: (successful: boolean) => void
): Promise {
@@ -109,14 +110,15 @@ export class DkgCoordinatorAgent {
}
private static async connectReadWrite(
- web3Provider: ethers.providers.Web3Provider
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer
) {
- return await this.connect(web3Provider, web3Provider.getSigner());
+ return await this.connect(provider, signer);
}
private static async connect(
provider: ethers.providers.Provider,
- signer?: ethers.providers.JsonRpcSigner
+ signer: ethers.Signer | undefined = undefined
): Promise {
const network = await provider.getNetwork();
const contractAddress = getContract(network.chainId, 'COORDINATOR');
diff --git a/src/agents/subscription-manager.ts b/src/agents/subscription-manager.ts
index ac8a5a3c5..da8236588 100644
--- a/src/agents/subscription-manager.ts
+++ b/src/agents/subscription-manager.ts
@@ -15,7 +15,8 @@ import { DEFAULT_WAIT_N_CONFIRMATIONS, getContract } from './contracts';
export class PreSubscriptionManagerAgent {
public static async createPolicy(
- web3Provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer,
valueInWei: BigNumber,
policyId: Uint8Array,
size: number,
@@ -23,7 +24,7 @@ export class PreSubscriptionManagerAgent {
endTimestamp: number,
ownerAddress: ChecksumAddress
): Promise {
- const SubscriptionManager = await this.connectReadWrite(web3Provider);
+ const SubscriptionManager = await this.connectReadWrite(provider, signer);
const overrides = {
value: valueInWei.toString(),
};
@@ -66,14 +67,15 @@ export class PreSubscriptionManagerAgent {
}
private static async connectReadWrite(
- web3Provider: ethers.providers.Web3Provider
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer
) {
- return await this.connect(web3Provider, web3Provider.getSigner());
+ return await this.connect(provider, signer);
}
private static async connect(
provider: ethers.providers.Provider,
- signer?: ethers.providers.JsonRpcSigner
+ signer?: ethers.Signer
): Promise {
const network = await provider.getNetwork();
const contractAddress = getContract(
diff --git a/src/characters/alice.ts b/src/characters/alice.ts
index dfe06d02f..579d4a338 100644
--- a/src/characters/alice.ts
+++ b/src/characters/alice.ts
@@ -42,7 +42,8 @@ export class Alice {
}
public async grant(
- web3Provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer,
porterUri: string,
policyParameters: BlockchainPolicyParameters,
includeUrsulas?: readonly ChecksumAddress[],
@@ -54,12 +55,12 @@ export class Alice {
excludeUrsulas,
includeUrsulas
);
- const policy = await this.createPolicy(web3Provider, policyParameters);
- return await policy.enact(web3Provider, ursulas);
+ const policy = await this.createPolicy(provider, policyParameters);
+ return await policy.enact(provider, signer, ursulas);
}
public async generatePreEnactedPolicy(
- web3Provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
porterUri: string,
policyParameters: BlockchainPolicyParameters,
includeUrsulas?: readonly ChecksumAddress[],
@@ -71,7 +72,7 @@ export class Alice {
excludeUrsulas,
includeUrsulas
);
- const policy = await this.createPolicy(web3Provider, policyParameters);
+ const policy = await this.createPolicy(provider, policyParameters);
return await policy.generatePreEnactedPolicy(ursulas);
}
@@ -94,11 +95,11 @@ export class Alice {
}
private async createPolicy(
- web3Provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
rawParameters: BlockchainPolicyParameters
): Promise {
const { bob, label, threshold, shares, startDate, endDate } =
- await this.validatePolicyParameters(web3Provider, rawParameters);
+ await this.validatePolicyParameters(provider, rawParameters);
const { delegatingKey, verifiedKFrags } = this.generateKFrags(
bob,
label,
@@ -119,7 +120,7 @@ export class Alice {
}
private async validatePolicyParameters(
- web3Provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
rawParams: BlockchainPolicyParameters
): Promise {
const startDate = rawParams.startDate ?? new Date();
@@ -141,8 +142,8 @@ export class Alice {
);
}
- const blockNumber = await web3Provider.getBlockNumber();
- const block = await web3Provider.getBlock(blockNumber);
+ const blockNumber = await provider.getBlockNumber();
+ const block = await provider.getBlock(blockNumber);
const blockTime = new Date(block.timestamp * 1000);
if (endDate < blockTime) {
throw new Error(
diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts
index 9a977665d..1d915dcf0 100644
--- a/src/characters/cbd-recipient.ts
+++ b/src/characters/cbd-recipient.ts
@@ -45,7 +45,8 @@ export class ThresholdDecrypter {
// Retrieve and decrypt ciphertext using provider and condition expression
public async retrieveAndDecrypt(
- provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer,
conditionExpr: ConditionExpression,
ciphertext: Ciphertext
): Promise {
@@ -53,6 +54,7 @@ export class ThresholdDecrypter {
const decryptionShares = await this.retrieve(
provider,
+ signer,
conditionExpr,
ciphertext,
acp
@@ -88,7 +90,8 @@ export class ThresholdDecrypter {
// Retrieve decryption shares
public async retrieve(
- provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer,
conditionExpr: ConditionExpression,
ciphertext: Ciphertext,
acp: AccessControlPolicy
@@ -97,7 +100,9 @@ export class ThresholdDecrypter {
provider,
this.ritualId
);
- const contextStr = await conditionExpr.buildContext(provider).toJson();
+ const contextStr = await conditionExpr
+ .buildContext(provider, signer)
+ .toJson();
const { sharedSecrets, encryptedRequests } = this.makeDecryptionRequests(
this.ritualId,
ciphertext,
diff --git a/src/characters/pre-recipient.ts b/src/characters/pre-recipient.ts
index f3ae24f5a..0e3cbd0e1 100644
--- a/src/characters/pre-recipient.ts
+++ b/src/characters/pre-recipient.ts
@@ -63,10 +63,15 @@ export class PreDecrypter {
}
public async retrieveAndDecrypt(
- messageKits: readonly MessageKit[],
- provider: ethers.providers.Web3Provider
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer,
+ messageKits: readonly MessageKit[]
): Promise {
- const policyMessageKits = await this.retrieve(messageKits, provider);
+ const policyMessageKits = await this.retrieve(
+ provider,
+ signer,
+ messageKits
+ );
policyMessageKits.forEach((mk: PolicyMessageKit) => {
if (!mk.isDecryptableByReceiver()) {
@@ -90,8 +95,9 @@ export class PreDecrypter {
}
public async retrieve(
- messageKits: readonly MessageKit[],
- provider: ethers.providers.Web3Provider
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer,
+ messageKits: readonly MessageKit[]
): Promise {
const treasureMap = this.encryptedTreasureMap.decrypt(
this.keyring.secretKey,
@@ -106,7 +112,7 @@ export class PreDecrypter {
.reduce((acc: ConditionExpression[], val) => acc.concat(val), [])
.map((condExpr: ConditionExpression) => condExpr.condition);
- const conditionContext = new ConditionContext(conditions, provider);
+ const conditionContext = new ConditionContext(provider, signer, conditions);
const policyMessageKits = messageKits.map((mk) =>
PolicyMessageKit.fromMessageKit(
diff --git a/src/conditions/condition-expr.ts b/src/conditions/condition-expr.ts
index 61c446e53..103f8c2a9 100644
--- a/src/conditions/condition-expr.ts
+++ b/src/conditions/condition-expr.ts
@@ -85,9 +85,10 @@ export class ConditionExpression {
}
public buildContext(
- provider: ethers.providers.Web3Provider
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer
): ConditionContext {
- return new ConditionContext([this.condition], provider);
+ return new ConditionContext(provider, signer, [this.condition]);
}
public asAad(): Uint8Array {
diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts
index fcf9939c3..4f9c478f2 100644
--- a/src/conditions/context/context.ts
+++ b/src/conditions/context/context.ts
@@ -17,10 +17,11 @@ export class ConditionContext {
private readonly walletAuthProvider: WalletAuthenticationProvider;
constructor(
- private readonly conditions: ReadonlyArray,
// TODO: We don't always need a web3 provider, only in cases where some specific context parameters are used
// TODO: Consider making this optional or introducing a different pattern to handle that
- private readonly web3Provider: ethers.providers.Web3Provider,
+ private readonly provider: ethers.providers.Provider,
+ private readonly signer: ethers.Signer,
+ private readonly conditions: ReadonlyArray,
public readonly customParameters: Record = {}
) {
Object.keys(customParameters).forEach((key) => {
@@ -35,7 +36,10 @@ export class ConditionContext {
);
}
});
- this.walletAuthProvider = new WalletAuthenticationProvider(web3Provider);
+ this.walletAuthProvider = new WalletAuthenticationProvider(
+ provider,
+ signer
+ );
}
public toObj = async (): Promise> => {
@@ -103,6 +107,11 @@ export class ConditionContext {
public withCustomParams = (
params: Record
): ConditionContext => {
- return new ConditionContext(this.conditions, this.web3Provider, params);
+ return new ConditionContext(
+ this.provider,
+ this.signer,
+ this.conditions,
+ params
+ );
};
}
diff --git a/src/conditions/context/providers.ts b/src/conditions/context/providers.ts
index 9b8db518e..c58951c32 100644
--- a/src/conditions/context/providers.ts
+++ b/src/conditions/context/providers.ts
@@ -1,3 +1,4 @@
+import type { TypedDataSigner } from '@ethersproject/abstract-signer';
import { ethers } from 'ethers';
import { utils as ethersUtils } from 'ethers/lib/ethers';
@@ -18,10 +19,13 @@ interface ChainData {
export class WalletAuthenticationProvider {
private walletSignature?: Record;
- constructor(private readonly web3Provider: ethers.providers.Web3Provider) {}
+ constructor(
+ private readonly provider: ethers.providers.Provider,
+ private readonly signer: ethers.Signer
+ ) {}
public async getOrCreateWalletSignature(): Promise {
- const address = await this.web3Provider.getSigner().getAddress();
+ const address = await this.signer.getAddress();
const storageKey = `wallet-signature-${address}`;
// If we have a signature in localStorage, return it
@@ -59,7 +63,7 @@ export class WalletAuthenticationProvider {
private async createWalletSignature(): Promise {
// Ensure freshness of the signature
const { blockNumber, blockHash, chainId } = await this.getChainData();
- const address = await this.web3Provider.getSigner().getAddress();
+ const address = await this.signer.getAddress();
const signatureText = `I'm the owner of address ${address} as of block number ${blockNumber}`;
const salt = ethersUtils.hexlify(ethersUtils.randomBytes(32));
@@ -85,9 +89,10 @@ export class WalletAuthenticationProvider {
blockHash,
},
};
- const signature = await this.web3Provider
- .getSigner()
- ._signTypedData(typedData.domain, typedData.types, typedData.message);
+ // https://github.com/ethers-io/ethers.js/issues/1431#issuecomment-813950552
+ const signature = await (
+ this.signer as unknown as TypedDataSigner
+ )._signTypedData(typedData.domain, typedData.types, typedData.message);
const formattedTypedData: FormattedTypedData = {
...typedData,
@@ -118,9 +123,9 @@ export class WalletAuthenticationProvider {
}
private async getChainData(): Promise {
- const blockNumber = await this.web3Provider.getBlockNumber();
- const blockHash = (await this.web3Provider.getBlock(blockNumber)).hash;
- const chainId = (await this.web3Provider.getNetwork()).chainId;
+ const blockNumber = await this.provider.getBlockNumber();
+ const blockHash = (await this.provider.getBlock(blockNumber)).hash;
+ const chainId = (await this.provider.getNetwork()).chainId;
return { blockNumber, blockHash, chainId };
}
}
diff --git a/src/dkg.ts b/src/dkg.ts
index e65095838..c1ab06499 100644
--- a/src/dkg.ts
+++ b/src/dkg.ts
@@ -65,23 +65,25 @@ const assumedThreshold = (sharesNum: number): number =>
export class DkgClient {
public static async initializeRitual(
- web3Provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer,
ursulas: ChecksumAddress[],
waitUntilEnd = false
): Promise {
const ritualId = await DkgCoordinatorAgent.initializeRitual(
- web3Provider,
+ provider,
+ signer,
ursulas.sort()
);
if (waitUntilEnd) {
const isSuccessful = await DkgClient.waitUntilRitualEnd(
- web3Provider,
+ provider,
ritualId
);
if (!isSuccessful) {
const ritualState = await DkgCoordinatorAgent.getRitualState(
- web3Provider,
+ provider,
ritualId
);
throw new Error(
@@ -94,7 +96,7 @@ export class DkgClient {
}
private static waitUntilRitualEnd = async (
- web3Provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
ritualId: number
): Promise => {
return new Promise((resolve, reject) => {
@@ -105,19 +107,19 @@ export class DkgClient {
reject();
}
};
- DkgCoordinatorAgent.onRitualEndEvent(web3Provider, ritualId, callback);
+ DkgCoordinatorAgent.onRitualEndEvent(provider, ritualId, callback);
});
};
public static async getExistingRitual(
- web3Provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
ritualId: number
): Promise {
const ritualState = await DkgCoordinatorAgent.getRitualState(
- web3Provider,
+ provider,
ritualId
);
- const ritual = await DkgCoordinatorAgent.getRitual(web3Provider, ritualId);
+ const ritual = await DkgCoordinatorAgent.getRitual(provider, ritualId);
const dkgPkBytes = new Uint8Array([
...fromHexString(ritual.publicKey.word0),
...fromHexString(ritual.publicKey.word1),
diff --git a/src/policies/policy.ts b/src/policies/policy.ts
index 00c62003a..2dc9dec07 100644
--- a/src/policies/policy.ts
+++ b/src/policies/policy.ts
@@ -42,9 +42,10 @@ export class PreEnactedPolicy implements IPreEnactedPolicy {
) {}
public async enact(
- web3Provider: ethers.providers.Web3Provider
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer
): Promise {
- const txHash = await this.publish(web3Provider);
+ const txHash = await this.publish(provider, signer);
return {
...this,
txHash,
@@ -52,19 +53,21 @@ export class PreEnactedPolicy implements IPreEnactedPolicy {
}
private async publish(
- web3Provider: ethers.providers.Web3Provider
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer
): Promise {
const startTimestamp = toEpoch(this.startTimestamp);
const endTimestamp = toEpoch(this.endTimestamp);
- const ownerAddress = await web3Provider.getSigner().getAddress();
+ const ownerAddress = await signer.getAddress();
const value = await PreSubscriptionManagerAgent.getPolicyCost(
- web3Provider,
+ provider,
this.size,
startTimestamp,
endTimestamp
);
const tx = await PreSubscriptionManagerAgent.createPolicy(
- web3Provider,
+ provider,
+ signer,
value,
this.id.toBytes(),
this.size,
@@ -107,11 +110,12 @@ export class BlockchainPolicy {
}
public async enact(
- web3Provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer,
ursulas: readonly Ursula[]
): Promise {
const preEnacted = await this.generatePreEnactedPolicy(ursulas);
- return await preEnacted.enact(web3Provider);
+ return await preEnacted.enact(provider, signer);
}
public async generatePreEnactedPolicy(
diff --git a/src/sdk/strategy/cbd-strategy.ts b/src/sdk/strategy/cbd-strategy.ts
index f48accc9f..56a91c6c3 100644
--- a/src/sdk/strategy/cbd-strategy.ts
+++ b/src/sdk/strategy/cbd-strategy.ts
@@ -29,13 +29,13 @@ export class CbdStrategy {
}
public async deploy(
- web3Provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
ritualId: number
): Promise {
// TODO(#264): Enable ritual initialization
// if (ritualId === undefined) {
// ritualId = await DkgClient.initializeRitual(
- // web3Provider,
+ // provider,
// this.cohort.ursulaAddresses,
// true
// );
@@ -44,7 +44,7 @@ export class CbdStrategy {
// // Given that we just initialized the ritual, this should never happen
// throw new Error('Ritual ID is undefined');
// }
- const dkgRitual = await DkgClient.getExistingRitual(web3Provider, ritualId);
+ const dkgRitual = await DkgClient.getExistingRitual(provider, ritualId);
return DeployedCbdStrategy.create(dkgRitual, this.cohort.porterUri);
}
@@ -84,7 +84,7 @@ export class DeployedCbdStrategy {
// TODO: This is analogous to create() above, is it useful?
public static async fromRitualId(
- provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
porterUri: string,
ritualId: number
): Promise {
diff --git a/src/sdk/strategy/pre-strategy.ts b/src/sdk/strategy/pre-strategy.ts
index 267f0480d..734d10dfc 100644
--- a/src/sdk/strategy/pre-strategy.ts
+++ b/src/sdk/strategy/pre-strategy.ts
@@ -62,7 +62,8 @@ export class PreStrategy {
}
public async deploy(
- web3Provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer,
label: string,
threshold = Math.floor(this.cohort.numUrsulas / 2) + 1,
shares = this.cohort.numUrsulas
@@ -85,7 +86,8 @@ export class PreStrategy {
endDate: this.endDate,
};
const policy = await alice.grant(
- web3Provider,
+ provider,
+ signer,
porterUri,
policyParams,
this.cohort.ursulaAddresses
diff --git a/test/acceptance/alice-grants.test.ts b/test/acceptance/alice-grants.test.ts
index 9600e7582..1fe6ccad5 100644
--- a/test/acceptance/alice-grants.test.ts
+++ b/test/acceptance/alice-grants.test.ts
@@ -14,9 +14,10 @@ import {
fakeAlice,
fakeBob,
fakePorterUri,
+ fakeProvider,
fakeRemoteBob,
+ fakeSigner,
fakeUrsulas,
- fakeWeb3Provider,
fromBytes,
mockEncryptTreasureMap,
mockGenerateKFrags,
@@ -34,7 +35,8 @@ describe('story: alice shares message with bob through policy', () => {
const startDate = new Date();
const endDate = new Date(Date.now() + 60 * 1000);
const mockedUrsulas = fakeUrsulas(shares);
- const web3Provider = fakeWeb3Provider();
+ const provider = fakeProvider();
+ const signer = fakeSigner();
// Intermediate variables used for mocking
let encryptedTreasureMap: EncryptedTreasureMap;
@@ -65,7 +67,7 @@ describe('story: alice shares message with bob through policy', () => {
startDate,
endDate,
};
- policy = await alice.grant(web3Provider, fakePorterUri, policyParams);
+ policy = await alice.grant(provider, signer, fakePorterUri, policyParams);
expect(
bytesEqual(
diff --git a/test/acceptance/delay-enact.test.ts b/test/acceptance/delay-enact.test.ts
index ac0183072..8ad674208 100644
--- a/test/acceptance/delay-enact.test.ts
+++ b/test/acceptance/delay-enact.test.ts
@@ -2,9 +2,10 @@ import {
bytesEqual,
fakeAlice,
fakePorterUri,
+ fakeProvider,
fakeRemoteBob,
+ fakeSigner,
fakeUrsulas,
- fakeWeb3Provider,
mockEncryptTreasureMap,
mockGenerateKFrags,
mockGetUrsulas,
@@ -18,7 +19,8 @@ describe('story: alice creates a policy but someone else enacts it', () => {
const endDate = new Date(Date.now() + 60 * 1000); // 60s later
const mockedUrsulas = fakeUrsulas(shares);
const label = 'fake-data-label';
- const web3Provider = fakeWeb3Provider();
+ const provider = fakeProvider();
+ const signer = fakeSigner();
it('alice generates a new policy', async () => {
const getUrsulasSpy = mockGetUrsulas(mockedUrsulas);
@@ -38,7 +40,7 @@ describe('story: alice creates a policy but someone else enacts it', () => {
};
const preEnactedPolicy = await alice.generatePreEnactedPolicy(
- web3Provider,
+ provider,
fakePorterUri,
policyParams
);
@@ -50,7 +52,7 @@ describe('story: alice creates a policy but someone else enacts it', () => {
).toBeTruthy();
expect(preEnactedPolicy.label).toBe(label);
- const enacted = await preEnactedPolicy.enact(web3Provider);
+ const enacted = await preEnactedPolicy.enact(provider, signer);
expect(enacted.txHash).toBeDefined();
expect(getUrsulasSpy).toHaveBeenCalled();
diff --git a/test/docs/cbd.test.ts b/test/docs/cbd.test.ts
index e657827c7..e0e0c40af 100644
--- a/test/docs/cbd.test.ts
+++ b/test/docs/cbd.test.ts
@@ -11,8 +11,8 @@ import {
import { Ursula } from '../../src/porter';
import { toBytes } from '../../src/utils';
import {
+ fakeProvider,
fakeUrsulas,
- fakeWeb3Provider,
mockDetectEthereumProvider,
mockEncryptTreasureMap,
mockGenerateKFrags,
@@ -57,9 +57,7 @@ describe('Get Started (CBD PoC)', () => {
jest
.spyOn(providers, 'Web3Provider')
- .mockImplementation(() =>
- fakeWeb3Provider(SecretKey.random().toBEBytes())
- );
+ .mockImplementation(() => fakeProvider(SecretKey.random().toBEBytes()));
//
// Start of the code example
@@ -88,8 +86,9 @@ describe('Get Started (CBD PoC)', () => {
const MMprovider = await detectEthereumProvider();
const mumbai = providers.getNetwork(80001);
- const web3Provider = new providers.Web3Provider(MMprovider, mumbai);
- const newDeployed = await newStrategy.deploy(web3Provider, 'test');
+ const provider = new providers.Web3Provider(MMprovider, mumbai);
+ const signer = provider.getSigner();
+ const newDeployed = await newStrategy.deploy(provider, signer, 'test');
// 5. Encrypt the plaintext & update conditions
const NFTBalanceConfig = {
@@ -117,8 +116,9 @@ describe('Get Started (CBD PoC)', () => {
// 6. Request decryption rights
const decryptedMessage = await newDeployed.decrypter.retrieveAndDecrypt(
- [encryptedMessageKit],
- web3Provider
+ provider,
+ signer,
+ [encryptedMessageKit]
);
//
diff --git a/test/integration/dkg-client.test.ts b/test/integration/dkg-client.test.ts
index acb8337b6..c3eed8e8a 100644
--- a/test/integration/dkg-client.test.ts
+++ b/test/integration/dkg-client.test.ts
@@ -4,8 +4,8 @@ import { DkgCoordinatorAgent } from '../../src/agents/coordinator';
import {
fakeCoordinatorRitual,
fakeDkgParticipants,
+ fakeProvider,
fakeRitualId,
- fakeWeb3Provider,
mockGetParticipants,
} from '../utils';
@@ -22,13 +22,13 @@ describe('DkgCoordinatorAgent', () => {
});
it('fetches transcripts from the coordinator', async () => {
- const provider = fakeWeb3Provider(SecretKey.random().toBEBytes());
+ const provider = fakeProvider(SecretKey.random().toBEBytes());
const ritual = await DkgCoordinatorAgent.getRitual(provider, fakeRitualId);
expect(ritual).toBeDefined();
});
it('fetches participants from the coordinator', async () => {
- const provider = fakeWeb3Provider(SecretKey.random().toBEBytes());
+ const provider = fakeProvider(SecretKey.random().toBEBytes());
const fakeParticipants = fakeDkgParticipants(fakeRitualId);
const getParticipantsSpy = mockGetParticipants(
fakeParticipants.participants
diff --git a/test/unit/cbd-strategy.test.ts b/test/unit/cbd-strategy.test.ts
index d38aeeb45..093dba61c 100644
--- a/test/unit/cbd-strategy.test.ts
+++ b/test/unit/cbd-strategy.test.ts
@@ -9,9 +9,10 @@ import {
fakeDkgFlow,
fakeDkgParticipants,
fakeDkgRitual,
+ fakeProvider,
+ fakeSigner,
fakeTDecFlow,
fakeUrsulas,
- fakeWeb3Provider,
makeCohort,
mockCbdDecrypt,
mockGetExistingRitual,
@@ -29,7 +30,8 @@ const {
// Shared test variables
const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes);
-const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes());
+const aliceSigner = fakeSigner(aliceSecretKey.toBEBytes());
+const aliceProvider = fakeProvider(aliceSecretKey.toBEBytes());
const ownsNFT = new ERC721Ownership({
contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77',
parameters: [3591],
@@ -52,7 +54,7 @@ async function makeDeployedCbdStrategy() {
const mockedDkg = fakeDkgFlow(variant, 0, 4, 4);
const mockedDkgRitual = fakeDkgRitual(mockedDkg);
- const web3Provider = fakeWeb3Provider(aliceSecretKey.toBEBytes());
+ const web3Provider = fakeProvider(aliceSecretKey.toBEBytes());
const getUrsulasSpy = mockGetUrsulas(ursulas);
const getExistingRitualSpy = mockGetExistingRitual(mockedDkgRitual);
const deployedStrategy = await strategy.deploy(web3Provider, ritualId);
@@ -131,6 +133,7 @@ describe('CbdDeployedStrategy', () => {
const decryptedMessage =
await deployedStrategy.decrypter.retrieveAndDecrypt(
aliceProvider,
+ aliceSigner,
conditionExpr,
ciphertext
);
diff --git a/test/unit/conditions/base/contract.test.ts b/test/unit/conditions/base/contract.test.ts
index ee988bbb6..5a2b26b7b 100644
--- a/test/unit/conditions/base/contract.test.ts
+++ b/test/unit/conditions/base/contract.test.ts
@@ -1,12 +1,10 @@
-import { SecretKey } from '@nucypher/nucypher-core';
-
import {
ConditionExpression,
CustomContextParam,
} from '../../../../src/conditions';
import { ContractCondition } from '../../../../src/conditions/base';
import { USER_ADDRESS_PARAM } from '../../../../src/conditions/const';
-import { fakeWeb3Provider } from '../../../utils';
+import { fakeProvider, fakeSigner } from '../../../utils';
import { testContractConditionObj, testFunctionAbi } from '../../testVariables';
describe('validation', () => {
@@ -116,9 +114,10 @@ describe('supports custom function abi', () => {
},
};
const contractCondition = new ContractCondition(contractConditionObj);
- const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes());
+ const provider = fakeProvider();
+ const signer = fakeSigner();
const conditionExpr = new ConditionExpression(contractCondition);
- const conditionContext = conditionExpr.buildContext(web3Provider);
+ const conditionContext = conditionExpr.buildContext(provider, signer);
const myCustomParam = ':customParam';
const customParams: Record = {};
customParams[myCustomParam] = 1234;
diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts
index 035b704d6..eb254ea62 100644
--- a/test/unit/conditions/context.test.ts
+++ b/test/unit/conditions/context.test.ts
@@ -1,11 +1,9 @@
-import { SecretKey } from '@nucypher/nucypher-core';
-
import { CustomContextParam } from '../../../src';
import { ConditionExpression } from '../../../src/conditions';
import { ContractCondition, RpcCondition } from '../../../src/conditions/base';
import { USER_ADDRESS_PARAM } from '../../../src/conditions/const';
import { RESERVED_CONTEXT_PARAMS } from '../../../src/conditions/context/context';
-import { fakeWeb3Provider } from '../../utils';
+import { fakeProvider, fakeSigner } from '../../utils';
import {
testContractConditionObj,
testFunctionAbi,
@@ -13,7 +11,8 @@ import {
testRpcConditionObj,
} from '../testVariables';
-const web3Provider = fakeWeb3Provider(SecretKey.random().toBEBytes());
+const provider = fakeProvider();
+const signer = fakeSigner();
describe('serialization', () => {
it('serializes to json', async () => {
@@ -27,7 +26,8 @@ describe('serialization', () => {
},
});
const conditionContext = new ConditionExpression(rpcCondition).buildContext(
- web3Provider
+ provider,
+ signer
);
const asJson = await conditionContext.toJson();
expect(asJson).toBeDefined();
@@ -49,7 +49,7 @@ describe('context parameters', () => {
};
const contractCondition = new ContractCondition(contractConditionObj);
const conditionExpr = new ConditionExpression(contractCondition);
- const conditionContext = conditionExpr.buildContext(web3Provider);
+ const conditionContext = conditionExpr.buildContext(provider, signer);
describe('return value test', () => {
it('accepts on a custom context parameters', async () => {
@@ -96,7 +96,7 @@ describe('context parameters', () => {
});
const conditionContext = new ConditionExpression(
customContractCondition
- ).buildContext(web3Provider);
+ ).buildContext(provider, signer);
await expect(async () => conditionContext.toObj()).rejects.toThrow(
`Missing custom context parameter(s): ${customParamKey}`
@@ -110,7 +110,7 @@ describe('context parameters', () => {
});
const conditionContext = new ConditionExpression(
customContractCondition
- ).buildContext(web3Provider);
+ ).buildContext(provider, signer);
const asObj = await conditionContext.toObj();
expect(asObj).toBeDefined();
diff --git a/test/unit/pre-strategy.test.ts b/test/unit/pre-strategy.test.ts
index daee95dae..c4f775061 100644
--- a/test/unit/pre-strategy.test.ts
+++ b/test/unit/pre-strategy.test.ts
@@ -9,8 +9,9 @@ import {
import { Ursula } from '../../src/porter';
import { toBytes } from '../../src/utils';
import {
+ fakeProvider,
+ fakeSigner,
fakeUrsulas,
- fakeWeb3Provider,
makeCohort,
mockEncryptTreasureMap,
mockGenerateKFrags,
@@ -30,8 +31,10 @@ const {
// Shared test variables
const aliceSecretKey = SecretKey.fromBEBytes(aliceSecretKeyBytes);
const bobSecretKey = SecretKey.fromBEBytes(bobSecretKeyBytes);
-const aliceProvider = fakeWeb3Provider(aliceSecretKey.toBEBytes());
-const bobProvider = fakeWeb3Provider(bobSecretKey.toBEBytes());
+const aliceSigner = fakeSigner(aliceSecretKey.toBEBytes());
+const aliceProvider = fakeProvider(aliceSecretKey.toBEBytes());
+const bobSigner = fakeSigner(bobSecretKey.toBEBytes());
+const bobProvider = fakeProvider(bobSecretKey.toBEBytes());
const ownsNFT = new ERC721Ownership({
contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77',
parameters: [3591],
@@ -54,7 +57,11 @@ const makeDeployedPreStrategy = async () => {
const makeTreasureMapSpy = mockMakeTreasureMap();
const encryptTreasureMapSpy = mockEncryptTreasureMap();
- const deployedStrategy = await strategy.deploy(aliceProvider, 'test');
+ const deployedStrategy = await strategy.deploy(
+ aliceProvider,
+ aliceSigner,
+ 'test'
+ );
expect(generateKFragsSpy).toHaveBeenCalled();
expect(publishToBlockchainSpy).toHaveBeenCalled();
@@ -126,8 +133,9 @@ describe('PreDeployedStrategy', () => {
const decryptedMessage =
await deployedStrategy.decrypter.retrieveAndDecrypt(
- [encryptedMessageKit],
- bobProvider
+ bobProvider,
+ bobSigner,
+ [encryptedMessageKit]
);
expect(getUrsulasSpy).toHaveBeenCalled();
expect(retrieveCFragsSpy).toHaveBeenCalled();
diff --git a/test/utils.ts b/test/utils.ts
index eec92f4cb..dad688591 100644
--- a/test/utils.ts
+++ b/test/utils.ts
@@ -81,28 +81,45 @@ export const fakeAlice = (aliceKey = 'fake-secret-key-32-bytes-alice-x') => {
return Alice.fromSecretKey(secretKey);
};
-export const fakeWeb3Provider = (
- secretKeyBytes = SecretKey.random().toBEBytes(),
- blockNumber?: number,
- blockTimestamp?: number
-): ethers.providers.Web3Provider => {
- const block = { timestamp: blockTimestamp ?? 1000 };
- const provider = {
- getBlockNumber: () => Promise.resolve(blockNumber ?? 1000),
+const makeFakeProvider = (timestamp: number, blockNumber: number) => {
+ const block = { timestamp };
+ return {
+ getBlockNumber: () => Promise.resolve(blockNumber),
getBlock: () => Promise.resolve(block as Block),
_isProvider: true,
getNetwork: () => Promise.resolve({ name: 'mockNetwork', chainId: -1 }),
};
- const fakeSignerWithProvider = {
+};
+
+export const fakeSigner = (
+ secretKeyBytes = SecretKey.random().toBEBytes(),
+ blockNumber = 1000,
+ blockTimestamp = 1000
+) => {
+ const provider = makeFakeProvider(blockNumber, blockTimestamp);
+ return {
...new Wallet(secretKeyBytes),
- provider,
+ provider: provider,
_signTypedData: () => Promise.resolve('fake-typed-signature'),
signMessage: () => Promise.resolve('fake-signature'),
getAddress: () =>
Promise.resolve('0x0000000000000000000000000000000000000000'),
} as unknown as ethers.providers.JsonRpcSigner;
+};
+
+export const fakeProvider = (
+ secretKeyBytes = SecretKey.random().toBEBytes(),
+ blockNumber = 1000,
+ blockTimestamp = 1000
+): ethers.providers.Web3Provider => {
+ const fakeProvider = makeFakeProvider(blockTimestamp, blockNumber);
+ const fakeSignerWithProvider = fakeSigner(
+ secretKeyBytes,
+ blockNumber,
+ blockTimestamp
+ );
return {
- ...provider,
+ ...fakeProvider,
getSigner: () => fakeSignerWithProvider,
} as unknown as ethers.providers.Web3Provider;
};
@@ -497,12 +514,6 @@ export const fakeDkgRitual = (ritual: {
);
};
-export const mockInitializeRitual = (ritualId: number) => {
- return jest.spyOn(DkgClient, 'initializeRitual').mockImplementation(() => {
- return Promise.resolve(ritualId);
- });
-};
-
export const mockGetExistingRitual = (dkgRitual: DkgRitual) => {
return jest.spyOn(DkgClient, 'getExistingRitual').mockImplementation(() => {
return Promise.resolve(dkgRitual);
@@ -517,9 +528,3 @@ export const makeCohort = async (ursulas: Ursula[]) => {
expect(getUrsulasSpy).toHaveBeenCalled();
return cohort;
};
-
-export const mockGetRitualState = (state = DkgRitualState.FINALIZED) => {
- return jest
- .spyOn(DkgCoordinatorAgent, 'getRitualState')
- .mockImplementation((_provider, _ritualId) => Promise.resolve(state));
-};
diff --git a/yarn.lock b/yarn.lock
index 13117376f..24fe06151 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3439,7 +3439,7 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
-ethers@^5.4.1:
+ethers@^5.7.2:
version "5.7.2"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e"
integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==
From ea8fff4679bd4aa12ba9fd7436e8f67697f92f2a Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Wed, 30 Aug 2023 13:12:12 +0200
Subject: [PATCH 2/3] apply pr suggestions
---
src/agents/coordinator.ts | 2 +-
src/characters/cbd-recipient.ts | 17 ++++-----
src/characters/pre-recipient.ts | 7 +++-
src/conditions/condition-expr.ts | 12 +++++--
src/conditions/context/context.ts | 41 ++++++++++++++++------
test/unit/conditions/base/contract.test.ts | 2 +-
test/unit/conditions/context.test.ts | 7 ++--
7 files changed, 60 insertions(+), 28 deletions(-)
diff --git a/src/agents/coordinator.ts b/src/agents/coordinator.ts
index a69e1d85b..db8378d8d 100644
--- a/src/agents/coordinator.ts
+++ b/src/agents/coordinator.ts
@@ -118,7 +118,7 @@ export class DkgCoordinatorAgent {
private static async connect(
provider: ethers.providers.Provider,
- signer: ethers.Signer | undefined = undefined
+ signer?: ethers.Signer
): Promise {
const network = await provider.getNetwork();
const contractAddress = getContract(network.chainId, 'COORDINATOR');
diff --git a/src/characters/cbd-recipient.ts b/src/characters/cbd-recipient.ts
index 1d915dcf0..b7cba3aaa 100644
--- a/src/characters/cbd-recipient.ts
+++ b/src/characters/cbd-recipient.ts
@@ -50,14 +50,14 @@ export class ThresholdDecrypter {
conditionExpr: ConditionExpression,
ciphertext: Ciphertext
): Promise {
- const acp = await this.makeAcp(provider, conditionExpr, ciphertext);
+ const acp = await this.makeAcp(provider, signer, conditionExpr, ciphertext);
const decryptionShares = await this.retrieve(
provider,
- signer,
conditionExpr,
ciphertext,
- acp
+ acp,
+ signer
);
const sharedSecret = combineDecryptionSharesSimple(decryptionShares);
@@ -69,7 +69,8 @@ export class ThresholdDecrypter {
}
private async makeAcp(
- provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer,
conditionExpr: ConditionExpression,
ciphertext: Ciphertext
) {
@@ -83,7 +84,7 @@ export class ThresholdDecrypter {
);
const headerHash = keccak256(ciphertext.header.toBytes());
- const authorization = await provider.getSigner().signMessage(headerHash);
+ const authorization = await signer.signMessage(headerHash);
return new AccessControlPolicy(authData, toBytes(authorization));
}
@@ -91,17 +92,17 @@ export class ThresholdDecrypter {
// Retrieve decryption shares
public async retrieve(
provider: ethers.providers.Provider,
- signer: ethers.Signer,
conditionExpr: ConditionExpression,
ciphertext: Ciphertext,
- acp: AccessControlPolicy
+ acp: AccessControlPolicy,
+ signer?: ethers.Signer
): Promise {
const dkgParticipants = await DkgCoordinatorAgent.getParticipants(
provider,
this.ritualId
);
const contextStr = await conditionExpr
- .buildContext(provider, signer)
+ .buildContext(provider, {}, signer)
.toJson();
const { sharedSecrets, encryptedRequests } = this.makeDecryptionRequests(
this.ritualId,
diff --git a/src/characters/pre-recipient.ts b/src/characters/pre-recipient.ts
index 0e3cbd0e1..964584218 100644
--- a/src/characters/pre-recipient.ts
+++ b/src/characters/pre-recipient.ts
@@ -112,7 +112,12 @@ export class PreDecrypter {
.reduce((acc: ConditionExpression[], val) => acc.concat(val), [])
.map((condExpr: ConditionExpression) => condExpr.condition);
- const conditionContext = new ConditionContext(provider, signer, conditions);
+ const conditionContext = new ConditionContext(
+ provider,
+ conditions,
+ {},
+ signer
+ );
const policyMessageKits = messageKits.map((mk) =>
PolicyMessageKit.fromMessageKit(
diff --git a/src/conditions/condition-expr.ts b/src/conditions/condition-expr.ts
index 103f8c2a9..4c5636e2e 100644
--- a/src/conditions/condition-expr.ts
+++ b/src/conditions/condition-expr.ts
@@ -12,7 +12,7 @@ import {
} from './base';
import { BLOCKTIME_METHOD } from './base/time';
import { CompoundCondition } from './compound-condition';
-import { ConditionContext } from './context';
+import { ConditionContext, CustomContextParam } from './context';
export type ConditionExpressionJSON = {
version: string;
@@ -86,9 +86,15 @@ export class ConditionExpression {
public buildContext(
provider: ethers.providers.Provider,
- signer: ethers.Signer
+ customParameters: Record = {},
+ signer?: ethers.Signer
): ConditionContext {
- return new ConditionContext(provider, signer, [this.condition]);
+ return new ConditionContext(
+ provider,
+ [this.condition],
+ customParameters,
+ signer
+ );
}
public asAad(): Uint8Array {
diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts
index 4f9c478f2..76f1bfb93 100644
--- a/src/conditions/context/context.ts
+++ b/src/conditions/context/context.ts
@@ -14,15 +14,27 @@ export const RESERVED_CONTEXT_PARAMS = [USER_ADDRESS_PARAM];
export const CONTEXT_PARAM_PREFIX = ':';
export class ConditionContext {
- private readonly walletAuthProvider: WalletAuthenticationProvider;
+ private readonly walletAuthProvider?: WalletAuthenticationProvider;
constructor(
- // TODO: We don't always need a web3 provider, only in cases where some specific context parameters are used
- // TODO: Consider making this optional or introducing a different pattern to handle that
private readonly provider: ethers.providers.Provider,
- private readonly signer: ethers.Signer,
private readonly conditions: ReadonlyArray,
- public readonly customParameters: Record = {}
+ public readonly customParameters: Record = {},
+ private readonly signer?: ethers.Signer
+ ) {
+ this.validateContextParams(customParameters, signer);
+
+ if (signer) {
+ this.walletAuthProvider = new WalletAuthenticationProvider(
+ provider,
+ signer
+ );
+ }
+ }
+
+ private validateContextParams(
+ customParameters: Record = {},
+ signer?: ethers.Signer
) {
Object.keys(customParameters).forEach((key) => {
if (RESERVED_CONTEXT_PARAMS.includes(key)) {
@@ -35,11 +47,13 @@ export class ConditionContext {
`Custom parameter ${key} must start with ${CONTEXT_PARAM_PREFIX}`
);
}
+
+ if (key === USER_ADDRESS_PARAM && !signer) {
+ throw new Error(
+ `Cannot use ${USER_ADDRESS_PARAM} as custom parameter without a signer`
+ );
+ }
});
- this.walletAuthProvider = new WalletAuthenticationProvider(
- provider,
- signer
- );
}
public toObj = async (): Promise> => {
@@ -74,6 +88,11 @@ export class ConditionContext {
// Fill in predefined context parameters
if (requestedParameters.has(USER_ADDRESS_PARAM)) {
+ if (!this.walletAuthProvider) {
+ throw new Error(
+ `Cannot use ${USER_ADDRESS_PARAM} as custom parameter without a signer`
+ );
+ }
parameters[USER_ADDRESS_PARAM] =
await this.walletAuthProvider.getOrCreateWalletSignature();
// Remove from requested parameters
@@ -109,9 +128,9 @@ export class ConditionContext {
): ConditionContext => {
return new ConditionContext(
this.provider,
- this.signer,
this.conditions,
- params
+ params,
+ this.signer
);
};
}
diff --git a/test/unit/conditions/base/contract.test.ts b/test/unit/conditions/base/contract.test.ts
index 5a2b26b7b..ef926e640 100644
--- a/test/unit/conditions/base/contract.test.ts
+++ b/test/unit/conditions/base/contract.test.ts
@@ -117,7 +117,7 @@ describe('supports custom function abi', () => {
const provider = fakeProvider();
const signer = fakeSigner();
const conditionExpr = new ConditionExpression(contractCondition);
- const conditionContext = conditionExpr.buildContext(provider, signer);
+ const conditionContext = conditionExpr.buildContext(provider, {}, signer);
const myCustomParam = ':customParam';
const customParams: Record = {};
customParams[myCustomParam] = 1234;
diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts
index eb254ea62..5ddf37e5d 100644
--- a/test/unit/conditions/context.test.ts
+++ b/test/unit/conditions/context.test.ts
@@ -27,6 +27,7 @@ describe('serialization', () => {
});
const conditionContext = new ConditionExpression(rpcCondition).buildContext(
provider,
+ {},
signer
);
const asJson = await conditionContext.toJson();
@@ -49,7 +50,7 @@ describe('context parameters', () => {
};
const contractCondition = new ContractCondition(contractConditionObj);
const conditionExpr = new ConditionExpression(contractCondition);
- const conditionContext = conditionExpr.buildContext(provider, signer);
+ const conditionContext = conditionExpr.buildContext(provider, {}, signer);
describe('return value test', () => {
it('accepts on a custom context parameters', async () => {
@@ -96,7 +97,7 @@ describe('context parameters', () => {
});
const conditionContext = new ConditionExpression(
customContractCondition
- ).buildContext(provider, signer);
+ ).buildContext(provider, {}, signer);
await expect(async () => conditionContext.toObj()).rejects.toThrow(
`Missing custom context parameter(s): ${customParamKey}`
@@ -110,7 +111,7 @@ describe('context parameters', () => {
});
const conditionContext = new ConditionExpression(
customContractCondition
- ).buildContext(provider, signer);
+ ).buildContext(provider, {}, signer);
const asObj = await conditionContext.toObj();
expect(asObj).toBeDefined();
From 0c28fa5f0e071f75d8f7d5a6776ca8121b86d122 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Wed, 30 Aug 2023 17:31:01 +0200
Subject: [PATCH 3/3] feat: add a helper function to detect a signer
requirement
---
src/conditions/base/condition.ts | 5 ++++
src/conditions/condition-expr.ts | 4 ++++
src/conditions/context/context.ts | 34 +++++++++++++++-------------
test/unit/conditions/context.test.ts | 26 +++++++++++++++++++++
4 files changed, 53 insertions(+), 16 deletions(-)
diff --git a/src/conditions/base/condition.ts b/src/conditions/base/condition.ts
index f4caa12c2..d77fb587e 100644
--- a/src/conditions/base/condition.ts
+++ b/src/conditions/base/condition.ts
@@ -1,6 +1,7 @@
import Joi from 'joi';
import { objectEquals } from '../../utils';
+import { USER_ADDRESS_PARAM } from '../const';
type Map = Record;
@@ -19,6 +20,10 @@ export class Condition {
return this.schema.validate(newValue);
}
+ public requiresSigner(): boolean {
+ return JSON.stringify(this.value).includes(USER_ADDRESS_PARAM);
+ }
+
public toObj(): Map {
const { error, value } = this.validate(this.value);
if (error) {
diff --git a/src/conditions/condition-expr.ts b/src/conditions/condition-expr.ts
index 4c5636e2e..a8fcb6be8 100644
--- a/src/conditions/condition-expr.ts
+++ b/src/conditions/condition-expr.ts
@@ -97,6 +97,10 @@ export class ConditionExpression {
);
}
+ public contextRequiresSigner(): boolean {
+ return this.condition.requiresSigner();
+ }
+
public asAad(): Uint8Array {
return toBytes(this.toJson());
}
diff --git a/src/conditions/context/context.ts b/src/conditions/context/context.ts
index 76f1bfb93..0f1f7fc04 100644
--- a/src/conditions/context/context.ts
+++ b/src/conditions/context/context.ts
@@ -22,21 +22,21 @@ export class ConditionContext {
public readonly customParameters: Record = {},
private readonly signer?: ethers.Signer
) {
- this.validateContextParams(customParameters, signer);
-
- if (signer) {
+ if (this.signer) {
this.walletAuthProvider = new WalletAuthenticationProvider(
- provider,
- signer
+ this.provider,
+ this.signer
);
}
+ this.validate();
}
- private validateContextParams(
- customParameters: Record = {},
- signer?: ethers.Signer
- ) {
- Object.keys(customParameters).forEach((key) => {
+ public requiresSigner(): boolean {
+ return this.conditions.some((cond) => cond.requiresSigner());
+ }
+
+ private validate() {
+ Object.keys(this.customParameters).forEach((key) => {
if (RESERVED_CONTEXT_PARAMS.includes(key)) {
throw new Error(
`Cannot use reserved parameter name ${key} as custom parameter`
@@ -47,13 +47,15 @@ export class ConditionContext {
`Custom parameter ${key} must start with ${CONTEXT_PARAM_PREFIX}`
);
}
-
- if (key === USER_ADDRESS_PARAM && !signer) {
- throw new Error(
- `Cannot use ${USER_ADDRESS_PARAM} as custom parameter without a signer`
- );
- }
});
+
+ if (this.requiresSigner() && !this.signer) {
+ throw new Error(
+ `Cannot use ${USER_ADDRESS_PARAM} as custom parameter without a signer`
+ );
+ }
+
+ return this;
}
public toObj = async (): Promise> => {
diff --git a/test/unit/conditions/context.test.ts b/test/unit/conditions/context.test.ts
index 5ddf37e5d..126f05df3 100644
--- a/test/unit/conditions/context.test.ts
+++ b/test/unit/conditions/context.test.ts
@@ -78,6 +78,32 @@ describe('context parameters', () => {
});
});
+ it('detects if a signer is required', () => {
+ const conditionObj = {
+ ...testContractConditionObj,
+ returnValueTest: {
+ ...testReturnValueTest,
+ value: USER_ADDRESS_PARAM,
+ },
+ };
+ const condition = new ContractCondition(conditionObj);
+ const conditionExpr = new ConditionExpression(condition);
+ const conditionContext = conditionExpr.buildContext(provider, {}, signer);
+ expect(conditionExpr.contextRequiresSigner()).toBe(true);
+ expect(conditionContext.requiresSigner()).toBe(true);
+ });
+
+ it('detects if a signer is not required', () => {
+ const condition = new RpcCondition(testRpcConditionObj);
+ const conditionExpr = new ConditionExpression(condition);
+ const conditionContext = conditionExpr.buildContext(provider, {}, signer);
+ expect(JSON.stringify(condition.toObj()).includes(USER_ADDRESS_PARAM)).toBe(
+ false
+ );
+ expect(conditionExpr.contextRequiresSigner()).toBe(false);
+ expect(conditionContext.requiresSigner()).toBe(false);
+ });
+
describe('custom method parameters', () => {
const contractConditionObj = {
...testContractConditionObj,