From 3e750c09c90a02a22bc81faad496160abd035298 Mon Sep 17 00:00:00 2001 From: Papa Smurf Date: Wed, 18 May 2022 09:12:44 +0100 Subject: [PATCH 1/8] fix: fixed memory access and initial refactor --- ...Compatible.sol => AccessTokenConsumer.sol} | 21 ++- ...thVerifier.sol => AccessTokenVerifier.sol} | 15 +- contracts/EtherMail.sol | 69 -------- ...hVerifier.sol => IAccessTokenVerifier.sol} | 8 +- contracts/KeyInfrastructure.sol | 2 +- contracts/{ => examples}/DummyDapp.sol | 8 +- contracts/mocks/ConsumerMock.sol | 162 ++++++++++++++++++ hardhat.config.ts | 11 +- src/messages/index.ts | 1 - src/messages/mail.ts | 14 -- src/utils/index.ts | 1 - ...{signAuthMessage.ts => signAccessToken.ts} | 8 +- src/utils/signMailMessage.ts | 19 -- tasks/deploy/auth.ts | 10 +- .../AccessTokenConsumer.behaviour.ts} | 78 ++------- .../AccessTokenConsumer.test.ts | 52 ++++++ .../AccessTokenVerifier.behaviour.ts} | 78 +++------ .../AccessTokenVerifier.test.ts | 50 ++++++ .../KeyInfrastructure.behaviour.ts} | 27 +-- .../KeyInfrastructure.test.ts | 30 ++++ test/mail.ts | 63 ------- test/types.ts | 6 +- 22 files changed, 387 insertions(+), 346 deletions(-) rename contracts/{AuthCompatible.sol => AccessTokenConsumer.sol} (74%) rename contracts/{AuthVerifier.sol => AccessTokenVerifier.sol} (80%) delete mode 100644 contracts/EtherMail.sol rename contracts/{IAuthVerifier.sol => IAccessTokenVerifier.sol} (86%) rename contracts/{ => examples}/DummyDapp.sol (63%) create mode 100644 contracts/mocks/ConsumerMock.sol delete mode 100644 src/messages/mail.ts rename src/utils/{signAuthMessage.ts => signAccessToken.ts} (79%) delete mode 100644 src/utils/signMailMessage.ts rename test/{authCompatible.ts => AccessTokenConsumer/AccessTokenConsumer.behaviour.ts} (62%) create mode 100644 test/AccessTokenConsumer/AccessTokenConsumer.test.ts rename test/{authVerifier.ts => AccessTokenVerifier/AccessTokenVerifier.behaviour.ts} (54%) create mode 100644 test/AccessTokenVerifier/AccessTokenVerifier.test.ts rename test/{keyInfrastructure.ts => KeyInfrastructure/KeyInfrastructure.behaviour.ts} (76%) create mode 100644 test/KeyInfrastructure/KeyInfrastructure.test.ts delete mode 100644 test/mail.ts diff --git a/contracts/AuthCompatible.sol b/contracts/AccessTokenConsumer.sol similarity index 74% rename from contracts/AuthCompatible.sol rename to contracts/AccessTokenConsumer.sol index 9f14202..c44695e 100644 --- a/contracts/AuthCompatible.sol +++ b/contracts/AccessTokenConsumer.sol @@ -1,14 +1,14 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.4; +pragma solidity >=0.8.13; import "hardhat/console.sol"; -import "./IAuthVerifier.sol"; +import "./IAccessTokenVerifier.sol"; -contract AuthCompatible { - IAuthVerifier private _verifier; +contract AccessTokenConsumer { + IAccessTokenVerifier private _verifier; constructor(address authVerifier) { - _verifier = IAuthVerifier(authVerifier); + _verifier = IAccessTokenVerifier(authVerifier); } modifier requiresAuth( @@ -17,7 +17,7 @@ contract AuthCompatible { bytes32 s, uint256 expiry ) { - require(verify(v, r, s, expiry), "AuthToken: verification failure"); + require(verify(v, r, s, expiry), "AccessToken: verification failure"); _; } @@ -27,11 +27,11 @@ contract AuthCompatible { bytes32 s, uint256 expiry ) internal view returns (bool) { - AuthToken memory token = constructToken(expiry); + AccessToken memory token = constructToken(expiry); return _verifier.verify(token, v, r, s); } - function constructToken(uint256 expiry) internal view returns (AuthToken memory token) { + function constructToken(uint256 expiry) internal view returns (AccessToken memory token) { FunctionCall memory functionCall; functionCall.functionSignature = msg.sig; functionCall.target = address(this); @@ -55,9 +55,8 @@ contract AuthCompatible { let endOfSigExp := add(startPos, 0x80) let totalInputSize := sub(calldatasize(), endOfSigExp) - // disgusting dirty putrid abomination of a detestable drivelous hack because - // for some reason byte array pointers are being assigned the same address as another causing overwrite - inputs := add(inputs, mul(calldatasize(), 2)) + // Overwrite data to calldata pointer + inputs := ptr // Store expected length of total byte array as first value mstore(inputs, totalInputSize) diff --git a/contracts/AuthVerifier.sol b/contracts/AccessTokenVerifier.sol similarity index 80% rename from contracts/AuthVerifier.sol rename to contracts/AccessTokenVerifier.sol index 512b538..f25cadd 100644 --- a/contracts/AuthVerifier.sol +++ b/contracts/AccessTokenVerifier.sol @@ -1,10 +1,11 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.4; +pragma solidity >=0.8.13; -import "./IAuthVerifier.sol"; +import "hardhat/console.sol"; +import "./IAccessTokenVerifier.sol"; import "./KeyInfrastructure.sol"; -contract AuthVerifier is IAuthVerifier, KeyInfrastructure { +contract AccessTokenVerifier is IAccessTokenVerifier, KeyInfrastructure { bytes32 private constant EIP712DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); @@ -15,7 +16,7 @@ contract AuthVerifier is IAuthVerifier, KeyInfrastructure { // solhint-disable max-line-length bytes32 private constant TOKEN_TYPEHASH = keccak256( - "AuthToken(uint256 expiry,FunctionCall functionCall)FunctionCall(bytes4 functionSignature,address target,address caller,bytes parameters)" + "AccessToken(uint256 expiry,FunctionCall functionCall)FunctionCall(bytes4 functionSignature,address target,address caller,bytes parameters)" ); // solhint-disable var-name-mixedcase @@ -58,19 +59,19 @@ contract AuthVerifier is IAuthVerifier, KeyInfrastructure { ); } - function hash(AuthToken memory token) internal pure returns (bytes32) { + function hash(AccessToken memory token) internal pure returns (bytes32) { return keccak256(abi.encode(TOKEN_TYPEHASH, token.expiry, hash(token.functionCall))); } function verify( - AuthToken memory token, + AccessToken memory token, uint8 v, bytes32 r, bytes32 s ) public view override returns (bool) { bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, hash(token))); - require(token.expiry > block.timestamp, "AuthToken: has expired"); + require(token.expiry > block.timestamp, "AccessToken: has expired"); return ecrecover(digest, v, r, s) == _issuer; } } diff --git a/contracts/EtherMail.sol b/contracts/EtherMail.sol deleted file mode 100644 index 7f009f1..0000000 --- a/contracts/EtherMail.sol +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.4; - -contract EtherMail { - struct EIP712Domain { - string name; - string version; - uint256 chainId; - address verifyingContract; - } - - struct Person { - string name; - address wallet; - } - - struct Mail { - Person from; - Person to; - string contents; - } - - bytes32 constant EIP712DOMAIN_TYPEHASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - - bytes32 constant PERSON_TYPEHASH = keccak256("Person(string name,address wallet)"); - - bytes32 constant MAIL_TYPEHASH = - keccak256("Mail(Person from,Person to,string contents)Person(string name,address wallet)"); - - bytes32 DOMAIN_SEPARATOR; - - constructor() { - DOMAIN_SEPARATOR = hash( - EIP712Domain({ name: "Ether Mail", version: "1", chainId: block.chainid, verifyingContract: address(this) }) - ); - } - - function hash(EIP712Domain memory eip712Domain) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - EIP712DOMAIN_TYPEHASH, - keccak256(bytes(eip712Domain.name)), - keccak256(bytes(eip712Domain.version)), - eip712Domain.chainId, - eip712Domain.verifyingContract - ) - ); - } - - function hash(Person memory person) internal pure returns (bytes32) { - return keccak256(abi.encode(PERSON_TYPEHASH, keccak256(bytes(person.name)), person.wallet)); - } - - function hash(Mail memory mail) internal pure returns (bytes32) { - return keccak256(abi.encode(MAIL_TYPEHASH, hash(mail.from), hash(mail.to), keccak256(bytes(mail.contents)))); - } - - function verify( - Mail memory mail, - uint8 v, - bytes32 r, - bytes32 s - ) public view returns (bool) { - bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, hash(mail))); - return ecrecover(digest, v, r, s) == mail.from.wallet; - } -} diff --git a/contracts/IAuthVerifier.sol b/contracts/IAccessTokenVerifier.sol similarity index 86% rename from contracts/IAuthVerifier.sol rename to contracts/IAccessTokenVerifier.sol index 4a3b826..c67677d 100644 --- a/contracts/IAuthVerifier.sol +++ b/contracts/IAccessTokenVerifier.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.4; +pragma solidity >=0.8.13; struct EIP712Domain { string name; @@ -22,14 +22,14 @@ struct FunctionCall { // FunctionParam[] parameters; // array of input parameters to the function call } -struct AuthToken { +struct AccessToken { uint256 expiry; FunctionCall functionCall; } -interface IAuthVerifier { +interface IAccessTokenVerifier { function verify( - AuthToken memory token, + AccessToken memory token, uint8 v, bytes32 r, bytes32 s diff --git a/contracts/KeyInfrastructure.sol b/contracts/KeyInfrastructure.sol index 080464b..ae34c62 100644 --- a/contracts/KeyInfrastructure.sol +++ b/contracts/KeyInfrastructure.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity >=0.8.13; contract KeyInfrastructure { address internal _root; diff --git a/contracts/DummyDapp.sol b/contracts/examples/DummyDapp.sol similarity index 63% rename from contracts/DummyDapp.sol rename to contracts/examples/DummyDapp.sol index bd9531e..3fa8156 100644 --- a/contracts/DummyDapp.sol +++ b/contracts/examples/DummyDapp.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.8.4; +pragma solidity >=0.8.13; import "hardhat/console.sol"; -import "./AuthCompatible.sol"; +import "../AccessTokenConsumer.sol"; -contract DummyDapp is AuthCompatible { - constructor(address verifier) AuthCompatible(verifier) {} +contract DummyDapp is AccessTokenConsumer { + constructor(address verifier) AccessTokenConsumer(verifier) {} function lend( uint8 v, diff --git a/contracts/mocks/ConsumerMock.sol b/contracts/mocks/ConsumerMock.sol new file mode 100644 index 0000000..e29dfff --- /dev/null +++ b/contracts/mocks/ConsumerMock.sol @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.13; + +import "hardhat/console.sol"; +import "../AccessTokenConsumer.sol"; + +contract ConsumerMock is AccessTokenConsumer { + constructor(address verifier) AccessTokenConsumer(verifier) {} + + function singleAddress( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + address addr + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + + function singleUint256( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + uint256 num + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + + function singleStringCalldata( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + string calldata str + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + + function singleStringMemory( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + string memory str + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + + function singleByte( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + bytes1 b1 + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + + function singleBytesCalldata( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + bytes calldata b1 + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + + function singleBytesMemory( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + bytes memory b1 + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + + function doubleAddressUint( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + address addr, + uint256 num + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + + function doubleUint256String( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + uint256 num, + string memory str + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + + function doubleStringBytesCalldata( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + string calldata str, + bytes calldata b + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + + function doubleStringBytesMemory( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + string calldata str, + bytes calldata b + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + + function multipleStringBytesAddress( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + string calldata str, + bytes calldata b, + address addr + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + + function multipleStringBytesAddressUint256( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + string calldata str, + bytes calldata b, + address addr, + uint256 num + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + + function multipleStringBytesAddressUint256Bytes( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry, + string calldata str, + bytes calldata b, + address addr, + uint256 num, + bytes calldata b2 + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } +} diff --git a/hardhat.config.ts b/hardhat.config.ts index 2a8f70d..a7f7c29 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -59,6 +59,9 @@ const config: HardhatUserConfig = { excludeContracts: [], src: "./contracts", }, + etherscan: { + apiKey: process.env.ETHERSCAN_API_KEY, + }, networks: { hardhat: { accounts: { @@ -73,7 +76,11 @@ const config: HardhatUserConfig = { optimism: getChainConfig("optimism"), polygon: getChainConfig("polygon"), rinkeby: getChainConfig("rinkeby"), - kovan: getChainConfig("kovan"), + kovan: { + accounts: [`0x${process.env.PRIVATE_KEY}`], + chainId: 42, + url: "https://kovan.infura.io/v3/27b5f204226a4ceeb06fc5761a8e7fa0", + }, }, paths: { artifacts: "./artifacts", @@ -82,7 +89,7 @@ const config: HardhatUserConfig = { tests: "./test", }, solidity: { - version: "0.8.9", + version: "0.8.13", settings: { metadata: { // Not including the metadata hash diff --git a/src/messages/index.ts b/src/messages/index.ts index 24a12cf..bc57e0c 100644 --- a/src/messages/index.ts +++ b/src/messages/index.ts @@ -1,3 +1,2 @@ export { AuthMessageTypes } from "./auth"; -export { MailMessageTypes } from "./mail"; export { Domain } from "./erc712"; diff --git a/src/messages/mail.ts b/src/messages/mail.ts deleted file mode 100644 index 6c99274..0000000 --- a/src/messages/mail.ts +++ /dev/null @@ -1,14 +0,0 @@ -// The named list of all type definitions -const MailMessageTypes = { - Person: [ - { name: "name", type: "string" }, - { name: "wallet", type: "address" }, - ], - Mail: [ - { name: "from", type: "Person" }, - { name: "to", type: "Person" }, - { name: "contents", type: "string" }, - ], -}; - -export { MailMessageTypes }; diff --git a/src/utils/index.ts b/src/utils/index.ts index 0b76cc8..d781b06 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,4 +1,3 @@ export { signAuthMessage } from "./signAuthMessage"; -export { signMailMessage } from "./signMailMessage"; export { getSignerFromMnemonic, getSignerFromPrivateKey } from "./signer"; export { packParameters } from "./packParameters"; diff --git a/src/utils/signAuthMessage.ts b/src/utils/signAccessToken.ts similarity index 79% rename from src/utils/signAuthMessage.ts rename to src/utils/signAccessToken.ts index 21c6ec2..3c80b47 100644 --- a/src/utils/signAuthMessage.ts +++ b/src/utils/signAccessToken.ts @@ -3,17 +3,17 @@ import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { Wallet } from "ethers"; import { AuthMessageTypes } from "../messages/auth"; import { Domain } from "../messages/erc712"; -import { AuthTokenStruct } from "../types/AuthVerifier"; +import { AccessTokenStruct } from "../types/IAccessTokenVerifier"; // Returns a 65-byte signature composed of v, r, s components concatenated: // https://docs.ethers.io/v5/api/utils/bytes/#signature-raw -const signAuthMessage = async ( +const signAccessToken = async ( signer: Wallet | JsonRpcSigner | SignerWithAddress, domain: Domain, - value: AuthTokenStruct, + value: AccessTokenStruct, ): Promise => { const signature = await signer._signTypedData(domain, AuthMessageTypes, value); return signature; }; -export { signAuthMessage }; +export { signAccessToken }; diff --git a/src/utils/signMailMessage.ts b/src/utils/signMailMessage.ts deleted file mode 100644 index 6241533..0000000 --- a/src/utils/signMailMessage.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { JsonRpcSigner } from "@ethersproject/providers"; -import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { VoidSigner, Wallet, Signature } from "ethers"; -import { Domain } from "../messages/erc712"; -import { MailMessageTypes } from "../messages/mail"; -import { EtherMail } from "../types/EtherMail"; - -// Returns a 65-byte signature composed of v, r, s components concatenated: -// https://docs.ethers.io/v5/api/utils/bytes/#signature-raw -const signMailMessage = async ( - signer: Wallet | JsonRpcSigner | SignerWithAddress, - domain: Domain, - value: EtherMail.MailStruct, -): Promise => { - const signature = await signer._signTypedData(domain, MailMessageTypes, value); - return signature; -}; - -export { signMailMessage }; diff --git a/tasks/deploy/auth.ts b/tasks/deploy/auth.ts index b6711c9..818ce34 100644 --- a/tasks/deploy/auth.ts +++ b/tasks/deploy/auth.ts @@ -1,14 +1,16 @@ import { task } from "hardhat/config"; import { TaskArguments } from "hardhat/types"; -import { AuthVerifier } from "../../src/types/AuthVerifier"; -import { AuthVerifier__factory } from "../../src/types/factories/AuthVerifier__factory"; +import { AccessTokenVerifier } from "../../src/types/AccessTokenVerifier"; +import { AccessTokenVerifier__factory } from "../../src/types/factories/AccessTokenVerifier__factory"; task("deploy:Auth") .addParam("root", "Root key") .setAction(async function (taskArguments: TaskArguments, { ethers }) { - const authFactory: AuthVerifier__factory = await ethers.getContractFactory("AuthVerifier"); - const auth: AuthVerifier = await authFactory.deploy(taskArguments.root); + const authFactory: AccessTokenVerifier__factory = ( + await ethers.getContractFactory("AccessTokenVerifier") + ); + const auth: AccessTokenVerifier = await authFactory.deploy(taskArguments.root); await auth.deployed(); console.log("Auth deployed to: ", auth.address); }); diff --git a/test/authCompatible.ts b/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts similarity index 62% rename from test/authCompatible.ts rename to test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts index 02599d5..6f023a4 100644 --- a/test/authCompatible.ts +++ b/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts @@ -1,12 +1,10 @@ import { artifacts, ethers, waffle } from "hardhat"; import type { Artifact } from "hardhat/types"; import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address"; -import { Signers } from "./types"; -import { AuthVerifier } from "../src/types/AuthVerifier"; -import { AuthTokenStruct } from "../src/types/IAuthVerifier"; -import { DummyDapp } from "../src/types/DummyDapp"; -import { signAuthMessage } from "../src/utils/signAuthMessage"; -import { packParameters } from "../src/utils/packParameters"; +import { Signers } from "../types"; +import { DummyDapp } from "../../src/types/DummyDapp"; +import { signAccessToken } from "../../src/utils/signAccessToken"; +import { packParameters } from "../../src/utils/packParameters"; import { splitSignature } from "@ethersproject/bytes"; const chai = require("chai"); @@ -15,54 +13,10 @@ chai.use(solidity); const { expect } = chai; const { BigNumber } = ethers; -describe("AuthCompatible", function () { - before(async function () { - this.signers = {} as Signers; - - const signers: SignerWithAddress[] = await ethers.getSigners(); - this.signers.admin = signers[0]; - this.signers.user0 = signers[1]; - this.signers.user1 = signers[2]; - this.signers.user2 = signers[3]; - }); - - before("deploy new", async function () { - const authArtifact: Artifact = await artifacts.readArtifact("AuthVerifier"); - const dappArtifact: Artifact = await artifacts.readArtifact("DummyDapp"); - this.auth = ( - await waffle.deployContract(this.signers.admin, authArtifact, [this.signers.admin.address]) - ); - this.dapp = await waffle.deployContract(this.signers.admin, dappArtifact, [this.auth.address]); - await this.auth.rotateIntermediate(this.signers.admin.address); - await this.auth.rotateIssuer(this.signers.admin.address); - }); - - before("construct test values", async function () { - this.domain = { - name: "Ethereum Access Token", - version: "1", - chainId: await this.signers.admin.getChainId(), - verifyingContract: this.auth.address, - }; - - this.amount = 5; - this.testTokenAddress = "0x25af0cca791baee922d9fa0744880ae6e0422021"; - - this.value = { - expiry: Math.floor(new Date().getTime() / 1000) + 50, - functionCall: { - functionSignature: "0xdbbfb456", - target: this.dapp.address.toLowerCase(), - caller: this.signers.admin.address.toLowerCase(), - // Parameters are hexadecimally represented, left-padded with 0 to multiples of 64-characters (32-bytes), and concatenated together - parameters: packParameters(this.dapp.interface, "lend", [this.testTokenAddress, this.amount]), - }, - }; - }); - +const shouldBehaveLikeAccessTokenConsumer = function () { describe("sign and verify", async () => { it("should succeed", async function () { - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, this.value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); expect( await this.dapp.lend( @@ -80,7 +34,7 @@ describe("AuthCompatible", function () { // The data to sign const value = { ...this.value, expiry: this.value.expiry - 50 }; - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, this.value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); await expect( this.dapp.lend( @@ -95,7 +49,7 @@ describe("AuthCompatible", function () { }); it("should fail with wrong signer", async function () { - const signature = splitSignature(await signAuthMessage(this.signers.user0, this.domain, this.value)); + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); await expect( this.dapp.lend( @@ -110,7 +64,7 @@ describe("AuthCompatible", function () { }); it("should fail with incorrect expiry", async function () { - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, this.value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); await expect( this.dapp.lend( @@ -128,7 +82,7 @@ describe("AuthCompatible", function () { // The data to sign const value = { ...this.value, functionCall: { ...this.value.functionCall, functionSignature: "0xb50e969c" } }; - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, value)); await expect( this.dapp.lend( @@ -146,7 +100,7 @@ describe("AuthCompatible", function () { // The data to sign const value = { ...this.value, functionCall: { ...this.value.functionCall, target: this.auth.address } }; - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, value)); await expect( this.dapp.lend( @@ -164,7 +118,7 @@ describe("AuthCompatible", function () { // The data to sign const value = { ...this.value, functionCall: { ...this.value.functionCall, caller: this.auth.address } }; - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, value)); await expect( this.dapp.lend( @@ -179,7 +133,7 @@ describe("AuthCompatible", function () { }); it("should fail with incorrect token address", async function () { - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, this.value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); await expect( this.dapp.lend( @@ -194,7 +148,7 @@ describe("AuthCompatible", function () { }); it("should fail with incorrect amount", async function () { - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, this.value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); await expect( this.dapp.lend( @@ -208,4 +162,6 @@ describe("AuthCompatible", function () { ).to.be.revertedWith("AuthToken: verification failure"); }); }); -}); +}; + +export { shouldBehaveLikeAccessTokenConsumer }; diff --git a/test/AccessTokenConsumer/AccessTokenConsumer.test.ts b/test/AccessTokenConsumer/AccessTokenConsumer.test.ts new file mode 100644 index 0000000..a967446 --- /dev/null +++ b/test/AccessTokenConsumer/AccessTokenConsumer.test.ts @@ -0,0 +1,52 @@ +import { artifacts, ethers, waffle } from "hardhat"; +import type { Artifact } from "hardhat/types"; +import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address"; + +import { DummyDapp } from "../../src/types/DummyDapp"; +import { packParameters } from "../../src/utils/packParameters"; +import { splitSignature } from "@ethersproject/bytes"; +import { shouldBehaveLikeAccessTokenConsumer } from "./AccessTokenConsumer.behaviour"; +import { AccessTokenVerifier, ConsumerMock } from "../../src/types"; +import { Signers } from "../types"; + +const chai = require("chai"); +const { solidity } = waffle; +chai.use(solidity); +const { expect } = chai; +const { BigNumber } = ethers; + +describe("AccessTokenConsumer", function () { + before("setup accounts", async function () { + this.signers = {} as Signers; + + const signers: SignerWithAddress[] = await ethers.getSigners(); + this.signers.admin = signers[0]; + this.signers.user0 = signers[1]; + this.signers.user1 = signers[2]; + this.signers.user2 = signers[3]; + }); + + before("deploy new", async function () { + const authArtifact: Artifact = await artifacts.readArtifact("AccessTokenVerifier"); + const dappArtifact: Artifact = await artifacts.readArtifact("DummyDapp"); + const mockArtifact: Artifact = await artifacts.readArtifact("ConsumerMock"); + this.auth = ( + await waffle.deployContract(this.signers.admin, authArtifact, [this.signers.admin.address]) + ); + this.dapp = await waffle.deployContract(this.signers.admin, dappArtifact, [this.auth.address]); + await this.auth.rotateIntermediate(this.signers.admin.address); + await this.auth.rotateIssuer(this.signers.admin.address); + this.mock = await waffle.deployContract(this.signers.admin, mockArtifact, [this.auth.address]); + }); + + before("construct test values", async function () { + this.domain = { + name: "Ethereum Access Token", + version: "1", + chainId: await this.signers.admin.getChainId(), + verifyingContract: this.auth.address, + }; + }); + + // shouldBehaveLikeAccessTokenConsumer(); +}); diff --git a/test/authVerifier.ts b/test/AccessTokenVerifier/AccessTokenVerifier.behaviour.ts similarity index 54% rename from test/authVerifier.ts rename to test/AccessTokenVerifier/AccessTokenVerifier.behaviour.ts index 1e61272..a4dea88 100644 --- a/test/authVerifier.ts +++ b/test/AccessTokenVerifier/AccessTokenVerifier.behaviour.ts @@ -1,10 +1,9 @@ import { artifacts, ethers, waffle } from "hardhat"; import type { Artifact } from "hardhat/types"; import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address"; -import { Signers } from "./types"; -import { AuthVerifier } from "../src/types/AuthVerifier"; -import { AuthTokenStruct } from "../src/types/IAuthVerifier"; -import { signAuthMessage } from "../src/utils/signAuthMessage"; +import { Signers } from "../types"; +import { AccessTokenStruct } from "../../src/types/IAccessTokenVerifier"; +import { signAccessToken } from "../../src/utils/signAccessToken"; import { splitSignature } from "@ethersproject/bytes"; const chai = require("chai"); @@ -13,49 +12,12 @@ chai.use(solidity); const { expect } = chai; const { BigNumber } = ethers; -describe("Auth", function () { - before(async function () { - this.signers = {} as Signers; - - const signers: SignerWithAddress[] = await ethers.getSigners(); - this.signers.admin = signers[0]; - this.signers.user0 = signers[1]; - this.signers.user1 = signers[2]; - this.signers.user2 = signers[3]; - }); - - before("deploy new", async function () { - const authArtifact: Artifact = await artifacts.readArtifact("AuthVerifier"); - this.auth = ( - await waffle.deployContract(this.signers.admin, authArtifact, [this.signers.admin.address]) - ); - await this.auth.rotateIntermediate(this.signers.admin.address); - await this.auth.rotateIssuer(this.signers.admin.address); - }); - - before("construct test values", async function () { - this.domain = { - name: "Ethereum Access Token", - version: "1", - chainId: await this.signers.admin.getChainId(), - verifyingContract: this.auth.address, - }; - - this.value = { - expiry: Math.floor(new Date().getTime() / 1000) + 10, - functionCall: { - functionSignature: "0x0f694584", - target: this.auth.address, - caller: this.signers.admin.address, - parameters: "0xff", - }, - }; - }); - +const shouldBehaveLikeAccessTokenVerifier = function () { describe("sign and verify", async () => { it("should succeed", async function () { - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, this.value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + await expect(this.auth.verify(this.value, signature.v, signature.r, signature.s)).to.not.be.reverted; expect(await this.auth.callStatic.verify(this.value, signature.v, signature.r, signature.s)).to.be.true; }); @@ -63,25 +25,25 @@ describe("Auth", function () { // The data to sign const value = { ...this.value, expiry: this.value.expiry - 10 }; - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, value)); await expect(this.auth.verify(value, signature.v, signature.r, signature.s)).to.be.revertedWith( - "AuthToken: has expired", + "AccessToken: has expired", ); }); it("should fail with incorrect expiry", async function () { - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, this.value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); - const badToken: AuthTokenStruct = { ...this.value, expiry: BigNumber.from(this.value.expiry + 10) }; + const badToken: AccessTokenStruct = { ...this.value, expiry: BigNumber.from(this.value.expiry + 10) }; expect(await this.auth.callStatic.verify(badToken, signature.v, signature.r, signature.s)).to.equal(false); }); it("should fail with incorrect function signature", async function () { - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, this.value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); - const badToken: AuthTokenStruct = { + const badToken: AccessTokenStruct = { ...this.value, functionCall: { ...this.value.functionCall, functionSignature: "0xdeadbeef" }, }; @@ -90,9 +52,9 @@ describe("Auth", function () { }); it("should fail with incorrect target address", async function () { - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, this.value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); - const badToken: AuthTokenStruct = { + const badToken: AccessTokenStruct = { ...this.value, functionCall: { ...this.value.functionCall, target: "0x25AF0ccA791baEe922D9fa0744880ae6E0422021" }, }; @@ -101,9 +63,9 @@ describe("Auth", function () { }); it("should fail with incorrect caller address", async function () { - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, this.value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); - const badToken: AuthTokenStruct = { + const badToken: AccessTokenStruct = { ...this.value, functionCall: { ...this.value.functionCall, caller: "0x25AF0ccA791baEe922D9fa0744880ae6E0422021" }, }; @@ -112,9 +74,9 @@ describe("Auth", function () { }); it("should fail with incorrect function parameters", async function () { - const signature = splitSignature(await signAuthMessage(this.signers.admin, this.domain, this.value)); + const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); - const badToken: AuthTokenStruct = { + const badToken: AccessTokenStruct = { ...this.value, functionCall: { ...this.value.functionCall, parameters: "0xdd" }, }; @@ -122,4 +84,6 @@ describe("Auth", function () { expect(await this.auth.callStatic.verify(badToken, signature.v, signature.r, signature.s)).to.equal(false); }); }); -}); +}; + +export { shouldBehaveLikeAccessTokenVerifier }; diff --git a/test/AccessTokenVerifier/AccessTokenVerifier.test.ts b/test/AccessTokenVerifier/AccessTokenVerifier.test.ts new file mode 100644 index 0000000..a82bcc6 --- /dev/null +++ b/test/AccessTokenVerifier/AccessTokenVerifier.test.ts @@ -0,0 +1,50 @@ +import { artifacts, ethers, waffle } from "hardhat"; +import type { Artifact } from "hardhat/types"; +import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address"; +import { Signers } from "../types"; +import { shouldBehaveLikeAccessTokenVerifier } from "./AccessTokenVerifier.behaviour"; +import { AccessTokenVerifier } from "../../src/types"; + +const chai = require("chai"); +const { solidity } = waffle; +chai.use(solidity); +const { expect } = chai; +const { BigNumber } = ethers; + +describe("AccessTokenVerifier", function () { + before(async function () { + this.signers = {} as Signers; + + const signers: SignerWithAddress[] = await ethers.getSigners(); + this.signers.admin = signers[0]; + this.signers.user0 = signers[1]; + this.signers.user1 = signers[2]; + this.signers.user2 = signers[3]; + + const authArtifact: Artifact = await artifacts.readArtifact("AccessTokenVerifier"); + this.auth = ( + await waffle.deployContract(this.signers.admin, authArtifact, [this.signers.admin.address]) + ); + await this.auth.rotateIntermediate(this.signers.admin.address); + await this.auth.rotateIssuer(this.signers.admin.address); + + this.domain = { + name: "Ethereum Access Token", + version: "1", + chainId: await this.signers.admin.getChainId(), + verifyingContract: this.auth.address, + }; + + this.value = { + expiry: Math.floor(new Date().getTime() / 1000) + 10, + functionCall: { + functionSignature: "0x0f694584", + target: this.auth.address, + caller: this.signers.admin.address, + parameters: "0xff", + }, + }; + }); + + shouldBehaveLikeAccessTokenVerifier(); +}); diff --git a/test/keyInfrastructure.ts b/test/KeyInfrastructure/KeyInfrastructure.behaviour.ts similarity index 76% rename from test/keyInfrastructure.ts rename to test/KeyInfrastructure/KeyInfrastructure.behaviour.ts index 75dd6d1..48d9972 100644 --- a/test/keyInfrastructure.ts +++ b/test/KeyInfrastructure/KeyInfrastructure.behaviour.ts @@ -1,32 +1,15 @@ import { artifacts, ethers, waffle } from "hardhat"; import type { Artifact } from "hardhat/types"; import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address"; -import { Signers } from "./types"; -import { KeyInfrastructure } from "../src/types/KeyInfrastructure"; +import { Signers } from "../types"; +import { KeyInfrastructure } from "../../src/types/KeyInfrastructure"; const chai = require("chai"); const { solidity } = waffle; chai.use(solidity); const { expect } = chai; -describe("Key Infrastructure", function () { - before(async function () { - this.signers = {} as Signers; - - const signers: SignerWithAddress[] = await ethers.getSigners(); - this.signers.admin = signers[0]; - this.signers.user0 = signers[1]; - this.signers.user1 = signers[2]; - this.signers.user2 = signers[3]; - }); - - before("deploy new", async function () { - const keyInfraArtifact: Artifact = await artifacts.readArtifact("KeyInfrastructure"); - this.keyInfrastructure = ( - await waffle.deployContract(this.signers.admin, keyInfraArtifact, [this.signers.admin.address]) - ); - }); - +const shouldBehaveLikeKeyInfrastructure = function () { it("key infrastructure should have been initialised correctly", async function () { expect(await this.keyInfrastructure.callStatic.getRootKey()).to.equal(this.signers.admin.address); }); @@ -71,4 +54,6 @@ describe("Key Infrastructure", function () { expect(await this.keyInfrastructure.callStatic.getIssuerKey()).to.equal(this.signers.user0.address); }); }); -}); +}; + +export { shouldBehaveLikeKeyInfrastructure }; diff --git a/test/KeyInfrastructure/KeyInfrastructure.test.ts b/test/KeyInfrastructure/KeyInfrastructure.test.ts new file mode 100644 index 0000000..39678ac --- /dev/null +++ b/test/KeyInfrastructure/KeyInfrastructure.test.ts @@ -0,0 +1,30 @@ +import { artifacts, ethers, waffle } from "hardhat"; +import type { Artifact } from "hardhat/types"; +import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address"; +import { Signers } from "../types"; +import { KeyInfrastructure } from "../../src/types/KeyInfrastructure"; +import { shouldBehaveLikeKeyInfrastructure } from "./KeyInfrastructure.behaviour"; + +const chai = require("chai"); +const { solidity } = waffle; +chai.use(solidity); +const { expect } = chai; + +describe("Key Infrastructure", function () { + before(async function () { + this.signers = {} as Signers; + + const signers: SignerWithAddress[] = await ethers.getSigners(); + this.signers.admin = signers[0]; + this.signers.user0 = signers[1]; + this.signers.user1 = signers[2]; + this.signers.user2 = signers[3]; + + const keyInfraArtifact: Artifact = await artifacts.readArtifact("KeyInfrastructure"); + this.keyInfrastructure = ( + await waffle.deployContract(this.signers.admin, keyInfraArtifact, [this.signers.admin.address]) + ); + }); + + shouldBehaveLikeKeyInfrastructure(); +}); diff --git a/test/mail.ts b/test/mail.ts deleted file mode 100644 index b39c263..0000000 --- a/test/mail.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { artifacts, ethers, waffle } from "hardhat"; -import type { Artifact } from "hardhat/types"; -import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address"; -import { Signers } from "./types"; -import { EtherMail } from "../src/types/EtherMail"; -import { signMailMessage } from "../src/utils/signMailMessage"; - -const chai = require("chai"); -const web3 = require("web3"); -const { solidity } = waffle; -chai.use(solidity); -const { expect } = chai; -const { BigNumber } = ethers; - -describe("EtherMail", function () { - before(async function () { - this.signers = {} as Signers; - - const signers: SignerWithAddress[] = await ethers.getSigners(); - this.signers.admin = signers[0]; - this.signers.user0 = signers[1]; - this.signers.user1 = signers[2]; - this.signers.user2 = signers[3]; - }); - - before("deploy new", async function () { - const mailArtifact: Artifact = await artifacts.readArtifact("EtherMail"); - this.mail = await waffle.deployContract(this.signers.admin, mailArtifact); - }); - - it("sign and verify", async function () { - const domain = { - name: "Ether Mail", - version: "1", - chainId: await this.signers.admin.getChainId(), - verifyingContract: this.mail.address, - }; - - // The data to sign - const value = { - from: { - name: "Cow", - wallet: web3.utils.toChecksumAddress(this.signers.admin.address), - }, - to: { - name: "Bob", - wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", - }, - contents: "Hello, Bob!", - }; - - const signature = await signMailMessage(this.signers.admin, domain, value); - - expect( - await this.mail.callStatic.verify( - value, - BigNumber.from("0x".concat(signature.substring(130, 132))), // v uint8 - "0x".concat(signature.substring(2, 66)), // r bytes32 - "0x".concat(signature.substring(66, 130)), // s bytes32 - ), - ).to.be.true; - }); -}); diff --git a/test/types.ts b/test/types.ts index e545a01..a805b45 100644 --- a/test/types.ts +++ b/test/types.ts @@ -1,15 +1,15 @@ import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address"; import type { Fixture } from "ethereum-waffle"; -import { AuthVerifier } from "../src/types/AuthVerifier"; -import { DummyDapp } from "../src/types/DummyDapp"; +import { AccessTokenVerifier, ConsumerMock, DummyDapp } from "../src/types"; import type { KeyInfrastructure } from "../src/types/KeyInfrastructure"; declare module "mocha" { export interface Context { keyInfrastructure: KeyInfrastructure; - auth: AuthVerifier; + verifier: AccessTokenVerifier; dapp: DummyDapp; + mock: ConsumerMock; loadFixture: (fixture: Fixture) => Promise; signers: Signers; } From 9c1e647a70ca61e22ff7d3bd557475e87b2ca527 Mon Sep 17 00:00:00 2001 From: Papa Smurf Date: Wed, 18 May 2022 09:20:40 +0100 Subject: [PATCH 2/8] fix: test now passing due to old naming --- src/messages/{auth.ts => accessToken.ts} | 2 +- src/utils/index.ts | 2 +- src/utils/signAccessToken.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/messages/{auth.ts => accessToken.ts} (96%) diff --git a/src/messages/auth.ts b/src/messages/accessToken.ts similarity index 96% rename from src/messages/auth.ts rename to src/messages/accessToken.ts index 0363db9..8824b89 100644 --- a/src/messages/auth.ts +++ b/src/messages/accessToken.ts @@ -8,7 +8,7 @@ const AuthMessageTypes = { { name: "caller", type: "address" }, { name: "parameters", type: "bytes" }, ], - AuthToken: [ + AccessToken: [ { name: "expiry", type: "uint256" }, { name: "functionCall", type: "FunctionCall" }, ], diff --git a/src/utils/index.ts b/src/utils/index.ts index d781b06..e101d91 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,3 +1,3 @@ -export { signAuthMessage } from "./signAuthMessage"; +export { signAccessToken } from "./signAccessToken"; export { getSignerFromMnemonic, getSignerFromPrivateKey } from "./signer"; export { packParameters } from "./packParameters"; diff --git a/src/utils/signAccessToken.ts b/src/utils/signAccessToken.ts index 3c80b47..82579f1 100644 --- a/src/utils/signAccessToken.ts +++ b/src/utils/signAccessToken.ts @@ -1,7 +1,7 @@ import { JsonRpcSigner } from "@ethersproject/providers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { Wallet } from "ethers"; -import { AuthMessageTypes } from "../messages/auth"; +import { AuthMessageTypes } from "../messages/accessToken"; import { Domain } from "../messages/erc712"; import { AccessTokenStruct } from "../types/IAccessTokenVerifier"; From e10ca00b4e8fd940a18bde31ec32c5a8e29f59cb Mon Sep 17 00:00:00 2001 From: Papa Smurf Date: Wed, 18 May 2022 12:31:42 +0100 Subject: [PATCH 3/8] test: some happy paths not passing, all tests written --- contracts/mocks/ConsumerMock.sol | 9 + src/messages/index.ts | 2 +- .../AccessTokenConsumer.behaviour.ts | 1941 +++++++++++++++-- .../AccessTokenConsumer.test.ts | 3 +- test/types.ts | 1 + 5 files changed, 1817 insertions(+), 139 deletions(-) diff --git a/contracts/mocks/ConsumerMock.sol b/contracts/mocks/ConsumerMock.sol index e29dfff..04aee1b 100644 --- a/contracts/mocks/ConsumerMock.sol +++ b/contracts/mocks/ConsumerMock.sol @@ -7,6 +7,15 @@ import "../AccessTokenConsumer.sol"; contract ConsumerMock is AccessTokenConsumer { constructor(address verifier) AccessTokenConsumer(verifier) {} + function noParams( + uint8 v, + bytes32 r, + bytes32 s, + uint256 expiry + ) public view requiresAuth(v, r, s, expiry) returns (bool) { + return true; + } + function singleAddress( uint8 v, bytes32 r, diff --git a/src/messages/index.ts b/src/messages/index.ts index bc57e0c..7d46b8b 100644 --- a/src/messages/index.ts +++ b/src/messages/index.ts @@ -1,2 +1,2 @@ -export { AuthMessageTypes } from "./auth"; +export { AuthMessageTypes } from "./accessToken"; export { Domain } from "./erc712"; diff --git a/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts b/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts index 6f023a4..9a0ca50 100644 --- a/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts +++ b/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts @@ -15,151 +15,1818 @@ const { BigNumber } = ethers; const shouldBehaveLikeAccessTokenConsumer = function () { describe("sign and verify", async () => { - it("should succeed", async function () { - const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); - - expect( - await this.dapp.lend( - signature.v, - signature.r, - signature.s, - BigNumber.from(this.value.expiry), - this.testTokenAddress, - this.amount, - ), - ).to.be.true; - }); + context("when calling function", async function () { + context("with parameters", async function () { + describe("address", async function () { + before("construct token", async function () { + this.params = [this.signers.user0.address]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("singleAddress"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "singleAddress", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); - it("should fail with expired token", async function () { - // The data to sign - const value = { ...this.value, expiry: this.value.expiry - 50 }; - - const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); - - await expect( - this.dapp.lend( - signature.v, - signature.r, - signature.s, - BigNumber.from(value.expiry - 50), - this.testTokenAddress, - this.amount, - ), - ).to.be.revertedWith("AuthToken: has expired"); - }); + it("with correct values should succeed", async function () { + expect( + await this.mock.singleAddress( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.true; + }); - it("should fail with wrong signer", async function () { - const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); - - await expect( - this.dapp.lend( - signature.v, - signature.r, - signature.s, - BigNumber.from(this.value.expiry), - this.testTokenAddress, - this.amount, - ), - ).to.be.revertedWith("AuthToken: verification failure"); - }); + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .singleAddress(this.signature.v, this.signature.r, this.signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); - it("should fail with incorrect expiry", async function () { - const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); - - await expect( - this.dapp.lend( - signature.v, - signature.r, - signature.s, - BigNumber.from(this.value.expiry + 10), - this.testTokenAddress, - this.amount, - ), - ).to.be.revertedWith("AuthToken: verification failure"); - }); + it("with expired token should revert", async function () { + await expect( + this.mock.singleAddress( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); - it("should fail with incorrect function signature", async function () { - // The data to sign - const value = { ...this.value, functionCall: { ...this.value.functionCall, functionSignature: "0xb50e969c" } }; - - const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, value)); - - await expect( - this.dapp.lend( - signature.v, - signature.r, - signature.s, - BigNumber.from(value.expiry), - this.testTokenAddress, - this.amount, - ), - ).to.be.revertedWith("AuthToken: verification failure"); - }); + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.singleAddress( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); - it("should fail with incorrect target contract", async function () { - // The data to sign - const value = { ...this.value, functionCall: { ...this.value.functionCall, target: this.auth.address } }; - - const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, value)); - - await expect( - this.dapp.lend( - signature.v, - signature.r, - signature.s, - BigNumber.from(value.expiry), - this.testTokenAddress, - this.amount, - ), - ).to.be.revertedWith("AuthToken: verification failure"); - }); + it("with incorrect values should revert", async function () { + await expect( + this.mock.singleAddress( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.signers.user1.address, + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); - it("should fail with incorrect caller", async function () { - // The data to sign - const value = { ...this.value, functionCall: { ...this.value.functionCall, caller: this.auth.address } }; - - const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, value)); - - await expect( - this.dapp.lend( - signature.v, - signature.r, - signature.s, - BigNumber.from(value.expiry), - this.testTokenAddress, - this.amount, - ), - ).to.be.revertedWith("AuthToken: verification failure"); - }); + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); - it("should fail with incorrect token address", async function () { - const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); - - await expect( - this.dapp.lend( - signature.v, - signature.r, - signature.s, - BigNumber.from(this.value.expiry), - this.auth.address, - this.amount, - ), - ).to.be.revertedWith("AuthToken: verification failure"); - }); + await expect( + this.mock.singleAddress(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.singleAddress(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.singleAddress( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + + describe("uint256", async function () { + before("construct token", async function () { + this.params = [BigNumber.from(42)]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("singleUint256"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "singleUint256", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.singleUint256( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .singleUint256(this.signature.v, this.signature.r, this.signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.singleUint256( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.singleUint256( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect values should revert", async function () { + await expect( + this.mock.singleUint256(this.signature.v, this.signature.r, this.signature.s, this.value.expiry, 41), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect( + this.mock.singleUint256(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.singleUint256(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.singleUint256( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + + describe("string calldata", async function () { + before("construct token", async function () { + this.params = ["random string"]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("singleStringCalldata"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "singleStringCalldata", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.singleStringCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .singleStringCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.singleStringCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.singleStringCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect values should revert", async function () { + await expect( + this.mock.singleStringCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + "bad string", + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect( + this.mock.singleStringCalldata(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.singleStringCalldata(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.singleStringCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + + describe("string memory", async function () { + before("construct token", async function () { + this.params = ["random string"]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("singleStringMemory"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "singleStringMemory", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.singleStringMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params, + ), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .singleStringMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.singleStringMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.singleStringMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect values should revert", async function () { + await expect( + this.mock.singleStringMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + "bad string", + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect( + this.mock.singleStringMemory(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.singleStringMemory(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.singleStringMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + + describe("byte", async function () { + before("construct token", async function () { + this.params = ["0x42"]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("singleByte"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "singleByte", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.singleByte( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .singleByte(this.signature.v, this.signature.r, this.signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.singleByte( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.singleByte( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect values should revert", async function () { + await expect( + this.mock.singleByte(this.signature.v, this.signature.r, this.signature.s, this.value.expiry, "0x41"), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect( + this.mock.singleByte(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.singleByte(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.singleByte( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + + describe("bytes calldata", async function () { + before("construct token", async function () { + this.params = ["0xaaaaaaaaaaaaaaaa"]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("singleBytesCalldata"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "singleBytesCalldata", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.singleBytesCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .singleBytesCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.singleBytesCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.singleBytesCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect values should revert", async function () { + await expect( + this.mock.singleBytesCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + "0xbbbbbb", + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect( + this.mock.singleBytesCalldata(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.singleBytesCalldata(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.singleBytesCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + + describe("bytes memory", async function () { + before("construct token", async function () { + this.params = ["0xaaaaaaaaaaaaaaaa"]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("singleBytesMemory"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "singleBytesMemory", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.singleBytesMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .singleBytesMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.singleBytesMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.singleBytesMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect values should revert", async function () { + await expect( + this.mock.singleBytesMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + "0xbbbbbb", + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect( + this.mock.singleBytesMemory(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.singleBytesMemory(signature.v, signature.r, signature.s, this.value.expiry, this.params[0]), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.singleBytesMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + + describe("address, uint256", async function () { + before("construct token", async function () { + this.params = [this.signers.user0.address, 42]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("doubleAddressUint"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "doubleAddressUint", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.doubleAddressUint( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .doubleAddressUint( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.doubleAddressUint( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.doubleAddressUint( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect values should revert", async function () { + await expect( + this.mock.doubleAddressUint( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + 41, + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect( + this.mock.doubleAddressUint( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.doubleAddressUint( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.doubleAddressUint( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + + describe("uint256, string", async function () { + before("construct token", async function () { + this.params = [42, "some string"]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("doubleUint256String"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "doubleUint256String", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.doubleUint256String( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .doubleUint256String( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.doubleUint256String( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.doubleUint256String( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect values should revert", async function () { + await expect( + this.mock.doubleUint256String( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + "wrong string", + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect( + this.mock.doubleUint256String( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.doubleUint256String( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.doubleUint256String( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + + describe("string, bytes calldata", async function () { + before("construct token", async function () { + this.params = ["some string", "0xaaaaaaaaaaaaaa"]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("doubleStringBytesCalldata"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "doubleStringBytesCalldata", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.doubleStringBytesCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .doubleStringBytesCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.doubleStringBytesCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.doubleStringBytesCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect values should revert", async function () { + await expect( + this.mock.doubleStringBytesCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + "bad string", + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect( + this.mock.doubleStringBytesCalldata( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.doubleStringBytesCalldata( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.doubleStringBytesCalldata( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + + describe("string, bytes memory", async function () { + before("construct token", async function () { + this.params = ["some string", "0xaaaaaaaaaaaaaa"]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("doubleStringBytesMemory"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "doubleStringBytesMemory", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.doubleStringBytesMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .doubleStringBytesMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.doubleStringBytesMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.doubleStringBytesMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect values should revert", async function () { + await expect( + this.mock.doubleStringBytesMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + "bad string", + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect( + this.mock.doubleStringBytesMemory( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.doubleStringBytesMemory( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.doubleStringBytesMemory( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + + describe("string, bytes calldata, address", async function () { + before("construct token", async function () { + this.params = ["some string", "0xaaaaaaaaaaaaaa", this.signers.user0.address]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("multipleStringBytesAddress"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "multipleStringBytesAddress", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.multipleStringBytesAddress( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + ), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .multipleStringBytesAddress( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.multipleStringBytesAddress( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + this.params[1], + this.params[2], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.multipleStringBytesAddress( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + this.params[1], + this.params[2], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect values should revert", async function () { + await expect( + this.mock.multipleStringBytesAddress( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + "bad string", + this.params[1], + this.params[2], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect( + this.mock.multipleStringBytesAddress( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.multipleStringBytesAddress( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.multipleStringBytesAddress( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + + describe("string, bytes calldata, address, uint256", async function () { + before("construct token", async function () { + this.params = ["some string", "0xaaaaaaaaaaaaaa", this.signers.user0.address, 42]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("multipleStringBytesAddressUint256"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "multipleStringBytesAddressUint256", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.multipleStringBytesAddressUint256( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + this.params[3], + ), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .multipleStringBytesAddressUint256( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + this.params[3], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.multipleStringBytesAddressUint256( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + this.params[1], + this.params[2], + this.params[3], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.multipleStringBytesAddressUint256( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + this.params[1], + this.params[2], + this.params[3], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect values should revert", async function () { + await expect( + this.mock.multipleStringBytesAddressUint256( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + "bad string", + this.params[1], + this.params[2], + this.params[3], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect( + this.mock.multipleStringBytesAddressUint256( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + this.params[3], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.multipleStringBytesAddressUint256( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + this.params[3], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.multipleStringBytesAddressUint256( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + this.params[3], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + + describe("string, bytes calldata, address, uint256, bytes calldata", async function () { + before("construct token", async function () { + this.params = ["some string", "0xaaaaaaaaaaaaaa", this.signers.user0.address, 42, "0xbbbbbbbbbbbb"]; + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("multipleStringBytesAddressUint256Bytes"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: packParameters(this.mock.interface, "multipleStringBytesAddressUint256Bytes", this.params), + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.multipleStringBytesAddressUint256Bytes( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + this.params[3], + this.params[4], + ), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .multipleStringBytesAddressUint256Bytes( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + this.params[3], + this.params[4], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.multipleStringBytesAddressUint256Bytes( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.sub(50), + this.params[0], + this.params[1], + this.params[2], + this.params[3], + this.params[4], + ), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.multipleStringBytesAddressUint256Bytes( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry.add(50), + this.params[0], + this.params[1], + this.params[2], + this.params[3], + this.params[4], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect values should revert", async function () { + await expect( + this.mock.multipleStringBytesAddressUint256Bytes( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + "bad string", + this.params[1], + this.params[2], + this.params[3], + this.params[4], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect( + this.mock.multipleStringBytesAddressUint256Bytes( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + this.params[3], + this.params[4], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect function signature should revert", async function () { + const signature = splitSignature( + await signAccessToken(this.signers.admin, this.domain, { + ...this.value, + functionCall: { + ...this.value.functionCall, + functionSignature: "0xdeadbeef", + }, + }), + ); + + await expect( + this.mock.multipleStringBytesAddressUint256Bytes( + signature.v, + signature.r, + signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + this.params[3], + this.params[4], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.multipleStringBytesAddressUint256Bytes( + this.signature.v, + this.signature.r, + this.signature.s, + this.value.expiry, + this.params[0], + this.params[1], + this.params[2], + this.params[3], + this.params[4], + ), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); + }); + + context("without parameters", async function () { + before("construct token", async function () { + this.params = BigNumber.from(42); + this.value = { + expiry: BigNumber.from(Math.floor(new Date().getTime() / 1000) + 10), + functionCall: { + functionSignature: this.mock.interface.getSighash("noParams"), + target: this.mock.address, + caller: this.signers.admin.address, + parameters: [], + }, + }; + this.signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); + }); + + it("with correct values should succeed", async function () { + expect( + await this.mock.noParams(this.signature.v, this.signature.r, this.signature.s, this.value.expiry), + ).to.be.true; + }); + + it("with incorrect caller should revert", async function () { + await expect( + this.mock + .connect(this.signers.user1) + .noParams(this.signature.v, this.signature.r, this.signature.s, this.value.expiry), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with expired token should revert", async function () { + await expect( + this.mock.noParams(this.signature.v, this.signature.r, this.signature.s, this.value.expiry.sub(50)), + ).to.be.revertedWith("AccessToken: has expired"); + }); + + it("with incorrect expiry should revert", async function () { + await expect( + this.mock.noParams(this.signature.v, this.signature.r, this.signature.s, this.value.expiry.add(50)), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + + it("with incorrect signer should revert", async function () { + const signature = splitSignature(await signAccessToken(this.signers.user0, this.domain, this.value)); + + await expect(this.mock.noParams(signature.v, signature.r, signature.s, this.value.expiry)).to.be.revertedWith( + "AccessToken: verification failure", + ); + }); - it("should fail with incorrect amount", async function () { - const signature = splitSignature(await signAccessToken(this.signers.admin, this.domain, this.value)); - - await expect( - this.dapp.lend( - signature.v, - signature.r, - signature.s, - BigNumber.from(this.value.expiry), - this.testTokenAddress, - 6, - ), - ).to.be.revertedWith("AuthToken: verification failure"); + it("with incorrect target contract should revert", async function () { + await expect( + this.fakeMock.noParams(this.signature.v, this.signature.r, this.signature.s, this.value.expiry), + ).to.be.revertedWith("AccessToken: verification failure"); + }); + }); }); }); }; diff --git a/test/AccessTokenConsumer/AccessTokenConsumer.test.ts b/test/AccessTokenConsumer/AccessTokenConsumer.test.ts index a967446..83ee3d3 100644 --- a/test/AccessTokenConsumer/AccessTokenConsumer.test.ts +++ b/test/AccessTokenConsumer/AccessTokenConsumer.test.ts @@ -37,6 +37,7 @@ describe("AccessTokenConsumer", function () { await this.auth.rotateIntermediate(this.signers.admin.address); await this.auth.rotateIssuer(this.signers.admin.address); this.mock = await waffle.deployContract(this.signers.admin, mockArtifact, [this.auth.address]); + this.fakeMock = await waffle.deployContract(this.signers.admin, mockArtifact, [this.auth.address]); }); before("construct test values", async function () { @@ -48,5 +49,5 @@ describe("AccessTokenConsumer", function () { }; }); - // shouldBehaveLikeAccessTokenConsumer(); + shouldBehaveLikeAccessTokenConsumer(); }); diff --git a/test/types.ts b/test/types.ts index a805b45..d348e23 100644 --- a/test/types.ts +++ b/test/types.ts @@ -10,6 +10,7 @@ declare module "mocha" { verifier: AccessTokenVerifier; dapp: DummyDapp; mock: ConsumerMock; + fakeMock: ConsumerMock; loadFixture: (fixture: Fixture) => Promise; signers: Signers; } From 9c4ff12590008ca4522aa47ae26d9a4fa3052992 Mon Sep 17 00:00:00 2001 From: Papa Smurf Date: Wed, 18 May 2022 13:19:20 +0100 Subject: [PATCH 4/8] fix: fixed bug where packing subset of params caused layout of calldata to be shifted wrongly --- src/utils/packParameters.ts | 27 ++++++++++++++++--- .../AccessTokenConsumer.behaviour.ts | 2 +- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/utils/packParameters.ts b/src/utils/packParameters.ts index 657e73f..1c4f775 100644 --- a/src/utils/packParameters.ts +++ b/src/utils/packParameters.ts @@ -1,5 +1,10 @@ import { hexlify } from "@ethersproject/bytes"; -import { ethers } from "ethers"; +import { BigNumber, ethers } from "ethers"; + +const placeholderV = 27; +const placeholderR = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"; +const placeholderS = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"; +const placeholderExpiry = 1652875695; const packParameters = ( contractInterface: ethers.utils.Interface, @@ -11,13 +16,27 @@ const packParameters = ( // check if selected function fragment complies with auth compatible function format: // functionName(uint8 v, bytes32 r, bytes32 s, uint256 expiry, ...) - if (!isAuthCompatible(functionFragment)) throw "packParameters: specified function is not AuthCompatible"; + if (!isAccessTokenCompatible(functionFragment)) + throw "packParameters: specified function is not compatible with AccessTokenConsumer"; // hexlify function encoding from index 4 onwards with parameters - return hexlify(contractInterface._encodeParams(functionFragment.inputs.slice(4), params)); + return `0x${hexlify( + contractInterface._encodeParams(functionFragment.inputs, [ + placeholderV, + placeholderR, + placeholderS, + placeholderExpiry, + ...params, + ]), + ) + .slice(2) // remove 0x + .slice(64) // remove v + .slice(64) // remove r + .slice(64) // remove s + .slice(64)}`; // remove expiry }; -const isAuthCompatible = (functionFragment: ethers.utils.FunctionFragment): boolean => { +const isAccessTokenCompatible = (functionFragment: ethers.utils.FunctionFragment): boolean => { if (functionFragment.inputs[0].name != "v" || functionFragment.inputs[0].type != "uint8") return false; if (functionFragment.inputs[1].name != "r" || functionFragment.inputs[1].type != "bytes32") return false; if (functionFragment.inputs[2].name != "s" || functionFragment.inputs[2].type != "bytes32") return false; diff --git a/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts b/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts index 9a0ca50..cec4960 100644 --- a/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts +++ b/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts @@ -363,7 +363,7 @@ const shouldBehaveLikeAccessTokenConsumer = function () { this.signature.r, this.signature.s, this.value.expiry, - this.params, + this.params[0], ), ).to.be.true; }); From 982c89ff1fd50b3d683d7b1ae11bf9d46673aea4 Mon Sep 17 00:00:00 2001 From: Papa Smurf Date: Fri, 24 Jun 2022 11:30:21 +0100 Subject: [PATCH 5/8] fix: globalised renaming --- contracts/AccessTokenConsumer.sol | 4 ++-- src/README.md | 22 +++++++++---------- src/messages/accessToken.ts | 6 ++--- src/messages/index.ts | 2 +- src/utils/signAccessToken.ts | 4 ++-- tasks/deploy/auth.ts | 12 +++++----- .../AccessTokenConsumer.behaviour.ts | 6 +---- 7 files changed, 26 insertions(+), 30 deletions(-) diff --git a/contracts/AccessTokenConsumer.sol b/contracts/AccessTokenConsumer.sol index c44695e..57bbe48 100644 --- a/contracts/AccessTokenConsumer.sol +++ b/contracts/AccessTokenConsumer.sol @@ -7,8 +7,8 @@ import "./IAccessTokenVerifier.sol"; contract AccessTokenConsumer { IAccessTokenVerifier private _verifier; - constructor(address authVerifier) { - _verifier = IAccessTokenVerifier(authVerifier); + constructor(address accessTokenVerifier) { + _verifier = IAccessTokenVerifier(accessTokenVerifier); } modifier requiresAuth( diff --git a/src/README.md b/src/README.md index fde10b2..37d852a 100644 --- a/src/README.md +++ b/src/README.md @@ -2,7 +2,7 @@ Utilities for the Ethereum Access Token smart contract system. -Use these tools to help you generate and sign your EATs. First ensure that your smart contracts follow appropriate EAT interfaces by ensuring all functions that intend to be modified with `requiresAuth` to use the following parameters prepended before your usual function parameters: +Use these tools to help you generate and sign your Ethereum Access Tokens (EATs). First ensure that your smart contracts follow appropriate EAT interfaces by ensuring all functions that intend to be modified with `requiresAuth` to use the following parameters prepended before your usual function parameters: ```solidity function yourFunction(uint8 v, bytes32 r, bytes32 s, uint256 expiry, ...) {} @@ -23,7 +23,7 @@ Using yarn: ```typescript const { splitSignature } = require("@ethersproject/bytes"); const { - signAuthMessage, + signAccessToken, getSignerFromMnemonic, getSignerFromPrivateKey packParameters } = require("@violetprotocol/ethereum-access-token-helpers/utils"); @@ -33,25 +33,25 @@ const FUNCTION_SIGNATURE = "0xabcdefgh"; const CONTRACT: ethers.Contract = ...; // for example an ERC20 token contract const SIGNER: ethers.Signer = ...; const CALLER: ethers.Signer = ...; -const VERIFIER = "0x..."; // AuthVerifier contract address +const VERIFIER = "0x..."; // AccessTokenVerifier contract address const recipient = "0x123..."; const amount = 1; -// AuthToken domain for clear namespacing -const authDomain = { +// AccessToken domain for clear namespacing +const accessTokenDomain = { name: "Ethereum Access Token", version: "1", chainId: SIGNER.getChainId(), verifyingContract: VERIFIER, }; -// Construct AuthToken message with relevant data using ERC20 `transfer(address to, uint256 amount)` as the example tx -// In the Auth compatible case, the ERC20 transfer function actually looks like this: +// Construct AccessToken message with relevant data using ERC20 `transfer(address to, uint256 amount)` as the example tx +// In the AccessTokenConsumer case, the ERC20 transfer function actually looks like this: // `transfer(uint8 v, bytes32 r, bytes32 s, uint256 expiry, address to, uint256 amount)` // where we just augment the original function with the required parameters for auth // the `parameters` property takes a packed, abi-encoded set of original function parameters -const authMessage = { +const accessTokenMessage = { expiry: Math.floor(new Date().getTime() / 1000) + interval, functionCall: { functionSignature: FUNCTION_SIGNATURE, @@ -61,15 +61,15 @@ const authMessage = { }, }; -// Sign the AuthToken using the Signer -const signature = splitSignature(await signAuthMessage(SIGNER, authDomain, authMessage)); +// Sign the AccessToken using the Signer +const signature = splitSignature(await signAccessToken(SIGNER, accessTokenDomain, accessTokenMessage)); // Pass all signed data to a transaction function call await CONTRACT.functionName( signature.v, signature.r, signature.s, - authMessage.expiry, + accessTokenMessage.expiry, ...params ) ``` diff --git a/src/messages/accessToken.ts b/src/messages/accessToken.ts index 8824b89..ddcd479 100644 --- a/src/messages/accessToken.ts +++ b/src/messages/accessToken.ts @@ -1,7 +1,5 @@ -import { BigNumberish } from "ethers"; - // The named list of all type definitions -const AuthMessageTypes = { +const AccessTokenTypes = { FunctionCall: [ { name: "functionSignature", type: "bytes4" }, { name: "target", type: "address" }, @@ -14,4 +12,4 @@ const AuthMessageTypes = { ], }; -export { AuthMessageTypes }; +export { AccessTokenTypes }; diff --git a/src/messages/index.ts b/src/messages/index.ts index 7d46b8b..dd78237 100644 --- a/src/messages/index.ts +++ b/src/messages/index.ts @@ -1,2 +1,2 @@ -export { AuthMessageTypes } from "./accessToken"; +export { AccessTokenTypes } from "./accessToken"; export { Domain } from "./erc712"; diff --git a/src/utils/signAccessToken.ts b/src/utils/signAccessToken.ts index 82579f1..14307c6 100644 --- a/src/utils/signAccessToken.ts +++ b/src/utils/signAccessToken.ts @@ -1,7 +1,7 @@ import { JsonRpcSigner } from "@ethersproject/providers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { Wallet } from "ethers"; -import { AuthMessageTypes } from "../messages/accessToken"; +import { AccessTokenTypes } from "../messages/accessToken"; import { Domain } from "../messages/erc712"; import { AccessTokenStruct } from "../types/IAccessTokenVerifier"; @@ -12,7 +12,7 @@ const signAccessToken = async ( domain: Domain, value: AccessTokenStruct, ): Promise => { - const signature = await signer._signTypedData(domain, AuthMessageTypes, value); + const signature = await signer._signTypedData(domain, AccessTokenTypes, value); return signature; }; diff --git a/tasks/deploy/auth.ts b/tasks/deploy/auth.ts index 818ce34..7d7f08d 100644 --- a/tasks/deploy/auth.ts +++ b/tasks/deploy/auth.ts @@ -4,13 +4,15 @@ import { TaskArguments } from "hardhat/types"; import { AccessTokenVerifier } from "../../src/types/AccessTokenVerifier"; import { AccessTokenVerifier__factory } from "../../src/types/factories/AccessTokenVerifier__factory"; -task("deploy:Auth") +task("deploy:AccessTokenVerifier") .addParam("root", "Root key") .setAction(async function (taskArguments: TaskArguments, { ethers }) { - const authFactory: AccessTokenVerifier__factory = ( + const accessTokenVerifierFactory: AccessTokenVerifier__factory = ( await ethers.getContractFactory("AccessTokenVerifier") ); - const auth: AccessTokenVerifier = await authFactory.deploy(taskArguments.root); - await auth.deployed(); - console.log("Auth deployed to: ", auth.address); + const accessTokenVerifier: AccessTokenVerifier = ( + await accessTokenVerifierFactory.deploy(taskArguments.root) + ); + await accessTokenVerifier.deployed(); + console.log("AccessTokenVerifier deployed to: ", accessTokenVerifier.address); }); diff --git a/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts b/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts index cec4960..26108a8 100644 --- a/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts +++ b/test/AccessTokenConsumer/AccessTokenConsumer.behaviour.ts @@ -1,8 +1,4 @@ -import { artifacts, ethers, waffle } from "hardhat"; -import type { Artifact } from "hardhat/types"; -import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address"; -import { Signers } from "../types"; -import { DummyDapp } from "../../src/types/DummyDapp"; +import { ethers, waffle } from "hardhat"; import { signAccessToken } from "../../src/utils/signAccessToken"; import { packParameters } from "../../src/utils/packParameters"; import { splitSignature } from "@ethersproject/bytes"; From 3d918bd83fbd68f07b1e9e56b632e715a91d4712 Mon Sep 17 00:00:00 2001 From: Papa Smurf Date: Fri, 24 Jun 2022 11:45:21 +0100 Subject: [PATCH 6/8] fix: refactored hardhat config, removed console --- contracts/AccessTokenVerifier.sol | 1 - hardhat.config.ts | 27 ++++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/contracts/AccessTokenVerifier.sol b/contracts/AccessTokenVerifier.sol index f25cadd..a986a5d 100644 --- a/contracts/AccessTokenVerifier.sol +++ b/contracts/AccessTokenVerifier.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.13; -import "hardhat/console.sol"; import "./IAccessTokenVerifier.sol"; import "./KeyInfrastructure.sol"; diff --git a/hardhat.config.ts b/hardhat.config.ts index a7f7c29..568f077 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -11,14 +11,16 @@ import { resolve } from "path"; import { config as dotenvConfig } from "dotenv"; import { HardhatUserConfig } from "hardhat/config"; -import { NetworkUserConfig } from "hardhat/types"; +import { HDAccountsUserConfig, NetworkUserConfig } from "hardhat/types"; dotenvConfig({ path: resolve(__dirname, "./.env") }); // Ensure that we have all the environment variables we need. const mnemonic: string | undefined = process.env.MNEMONIC; -if (!mnemonic) { - throw new Error("Please set your MNEMONIC in a .env file"); +const privateKey: string | undefined = process.env.PRIVATE_KEY; + +if (!privateKey && !mnemonic) { + throw new Error("Please set your PRIVATE_KEY or MNEMONIC in a .env file"); } const infuraApiKey: string | undefined = process.env.INFURA_API_KEY; @@ -40,12 +42,19 @@ const chainIds = { function getChainConfig(network: keyof typeof chainIds): NetworkUserConfig { const url: string = "https://" + network + ".infura.io/v3/" + infuraApiKey; - return { - accounts: { + let accounts; + + // Prioritise private key if it is available + if (privateKey) accounts = [`0x${process.env.PRIVATE_KEY}`]; + else if (mnemonic) + accounts = { count: 20, mnemonic, path: "m/44'/60'/0'/0", - }, + }; + + return { + accounts, chainId: chainIds[network], url, }; @@ -76,11 +85,7 @@ const config: HardhatUserConfig = { optimism: getChainConfig("optimism"), polygon: getChainConfig("polygon"), rinkeby: getChainConfig("rinkeby"), - kovan: { - accounts: [`0x${process.env.PRIVATE_KEY}`], - chainId: 42, - url: "https://kovan.infura.io/v3/27b5f204226a4ceeb06fc5761a8e7fa0", - }, + kovan: getChainConfig("kovan"), }, paths: { artifacts: "./artifacts", From 22722a94356f9362ca6bc9f6dfb94fdc04dcb45e Mon Sep 17 00:00:00 2001 From: Papa Smurf Date: Tue, 28 Jun 2022 12:39:46 +0100 Subject: [PATCH 7/8] chore: bump version to 0.2.1 --- package.json | 2 +- src/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 40eb21a..31620ca 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@violetprotocol/ethereum-access-token", "description": "Smart contracts for on-chain token-based access", - "version": "0.1.1", + "version": "0.2.1", "author": { "name": "papasmurf" }, diff --git a/src/package.json b/src/package.json index 944d713..038a58c 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "@violetprotocol/ethereum-access-token-helpers", "description": "Typescript bindings and utilities for Ethereum Access Token", - "version": "0.1.1", + "version": "0.2.1", "main": "dist/index.js", "files": [ "dist/*" From 2d65c0b99e0e9d2dbfd944bf3134571f8d5dabe8 Mon Sep 17 00:00:00 2001 From: Papa Smurf Date: Thu, 30 Jun 2022 10:39:16 +0100 Subject: [PATCH 8/8] fix: correct version bump --- package.json | 2 +- src/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 31620ca..29419a2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@violetprotocol/ethereum-access-token", "description": "Smart contracts for on-chain token-based access", - "version": "0.2.1", + "version": "0.2.0", "author": { "name": "papasmurf" }, diff --git a/src/package.json b/src/package.json index 038a58c..2c1044b 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "@violetprotocol/ethereum-access-token-helpers", "description": "Typescript bindings and utilities for Ethereum Access Token", - "version": "0.2.1", + "version": "0.2.0", "main": "dist/index.js", "files": [ "dist/*"