Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/gold-glasses-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@turnkey/ethers": minor
---

Implement signer.authorize for EIP-7702 transactions
2 changes: 1 addition & 1 deletion examples/deployer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"@turnkey/sdk-server": "workspace:*",
"@turnkey/ethers": "workspace:*",
"dotenv": "^16.0.3",
"ethers": "^6.10.0",
"ethers": "^6.14.4",
"solc": "0.8.13"
}
}
2 changes: 1 addition & 1 deletion examples/react-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"eslint": "8.56.0",
"eslint-config-next": "14.2.25",
"esm": "^3.2.25",
"ethers": "^6.10.0",
"ethers": "^6.14.4",
"install": "^0.13.0",
"jwt-decode": "^4.0.0",
"next": "^14.2.25",
Expand Down
2 changes: 1 addition & 1 deletion examples/rebalancer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
"@turnkey/ethers": "workspace:*",
"@turnkey/sdk-server": "workspace:*",
"dotenv": "^16.0.3",
"ethers": "^6.10.0"
"ethers": "^6.14.4"
}
}
2 changes: 1 addition & 1 deletion examples/sweeper/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"@turnkey/ethers": "workspace:*",
"@uniswap/sdk-core": "^3.1.1",
"dotenv": "^16.0.3",
"ethers": "^6.10.0",
"ethers": "^6.14.4",
"hardhat": "^2.12.7",
"prompts": "^2.4.2"
},
Expand Down
2 changes: 1 addition & 1 deletion examples/trading-runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@uniswap/v3-core": "^1.0.1",
"@uniswap/v3-sdk": "^3.9.0",
"dotenv": "^16.0.3",
"ethers": "^6.10.0",
"ethers": "^6.14.4",
"jsbi": "^3.2.5",
"prompts": "^2.4.2"
},
Expand Down
2 changes: 1 addition & 1 deletion examples/with-biconomy-aa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"@turnkey/sdk-server": "workspace:*",
"@turnkey/viem": "workspace:*",
"dotenv": "^16.0.3",
"ethers": "^6.10.0",
"ethers": "^6.14.4",
"prompts": "^2.4.2",
"viem": "^2.24.2"
},
Expand Down
2 changes: 1 addition & 1 deletion examples/with-eth-passkeys-galore/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"@types/react-dom": "18.2.6",
"axios": "^1.8.2",
"encoding": "^0.1.13",
"ethers": "^6.10.0",
"ethers": "^6.14.4",
"eslint": "8.56.0",
"eslint-config-next": "14.2.25",
"esm": "^3.2.25",
Expand Down
2 changes: 1 addition & 1 deletion examples/with-ethers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@turnkey/http": "workspace:*",
"@turnkey/sdk-server": "workspace:*",
"dotenv": "^16.0.3",
"ethers": "^6.10.0",
"ethers": "^6.14.4",
"prompts": "^2.4.2"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion examples/with-gnosis/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"@turnkey/sdk-server": "workspace:*",
"@turnkey/ethers": "workspace:*",
"dotenv": "^16.0.3",
"ethers": "^6.10.0"
"ethers": "^6.14.4"
},
"overrides": {
"@safe-global/protocol-kit": {
Expand Down
2 changes: 1 addition & 1 deletion examples/with-nonce-manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
"@turnkey/sdk-server": "workspace:*",
"@turnkey/ethers": "workspace:*",
"dotenv": "^16.0.3",
"ethers": "^6.10.0"
"ethers": "^6.14.4"
}
}
2 changes: 1 addition & 1 deletion examples/with-uniswap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@uniswap/v3-core": "^1.0.1",
"@uniswap/v3-sdk": "^3.9.0",
"dotenv": "^16.0.3",
"ethers": "^6.10.0",
"ethers": "^6.14.4",
"hardhat": "^2.12.7",
"jsbi": "^3.2.5"
},
Expand Down
2 changes: 1 addition & 1 deletion examples/with-zerodev-aa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@zerodev/ecdsa-validator": "^5.4.8",
"@zerodev/sdk": "^5.4.32",
"dotenv": "^16.0.3",
"ethers": "^6.10.0",
"ethers": "^6.14.4",
"permissionless": "^0.1.45",
"prompts": "^2.4.2",
"typescript": "5.4.3",
Expand Down
10 changes: 5 additions & 5 deletions packages/ethers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"typecheck": "tsc -p tsconfig.typecheck.json"
},
"peerDependencies": {
"ethers": "^6.10.0"
"ethers": "^6.14.4"
},
"dependencies": {
"@turnkey/api-key-stamper": "workspace:*",
Expand All @@ -59,13 +59,13 @@
"@turnkey/sdk-server": "workspace:*"
},
"devDependencies": {
"@nomicfoundation/hardhat-ethers": "3.0.5",
"@nomicfoundation/hardhat-network-helpers": "^1.0.8",
"@nomicfoundation/hardhat-ethers": "3.0.9",
"@nomicfoundation/hardhat-network-helpers": "^1.0.12",
"@openzeppelin/contracts": "^4.9.0",
"@typechain/ethers-v6": "^0.5.1",
"@typechain/hardhat": "^9.1.0",
"ethers": "^6.10.0",
"hardhat": "^2.19.4",
"ethers": "^6.14.4",
"hardhat": "^2.24.3",
"typechain": "^8.3.2"
},
"engines": {
Expand Down
208 changes: 207 additions & 1 deletion packages/ethers/src/__tests__/index-test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { setBalance } from "@nomicfoundation/hardhat-network-helpers";
import { parseEther, verifyMessage, verifyTypedData } from "ethers";
import { ethers, parseEther, verifyMessage, verifyTypedData } from "ethers";
import hre from "hardhat";
import { test, expect, beforeEach, describe } from "@jest/globals";
import { TurnkeySigner, TurnkeyActivityError } from "../";
Expand All @@ -18,6 +18,14 @@ const testCase: typeof test = (...argList) => {
return test(...argList);
};

// Custom type for EIP-7702 compliance
export interface EIP7702AuthorizationRequest {
address: string;
chainId: number | bigint | string;
nonce: number | bigint | string;
code?: string; // EIP-7702 requires code field
}

describe("TurnkeySigner", () => {
let connectedSigner: TurnkeySigner;
let signerWithProvider: TurnkeySigner;
Expand Down Expand Up @@ -345,6 +353,204 @@ describe("TurnkeySigner", () => {
expect(approveTx.from).toEqual(signingConfig.expectedEthAddress);
expect(approveTx.to).toEqual(deploymentAddress);
});
// testCase("it signs authorization (EIP-712 permit style)", async () => {
// // Define the EIP-712 domain for EIP-7702
// const domain = {
// name: "EIP7702Authorization",
// version: "1",
// chainId: chainId,
// verifyingContract: "0x0000000000000000000000000000000000000000",
// };

// // Define the EIP-7702 AuthorizationRequest
// const authRequest: ethers.AuthorizationRequest = {
// address: signingConfig.expectedEthAddress,
// chainId: chainId,
// nonce: 0,
// };

// try {
// // Sign the authorization
Comment on lines +356 to +373
Copy link

Copilot AI Jun 20, 2025

Choose a reason for hiding this comment

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

[nitpick] There’s a large block of commented-out test code above the real EIP-7702 test; remove it to keep the test file clean and focused.

Suggested change
// testCase("it signs authorization (EIP-712 permit style)", async () => {
// // Define the EIP-712 domain for EIP-7702
// const domain = {
// name: "EIP7702Authorization",
// version: "1",
// chainId: chainId,
// verifyingContract: "0x0000000000000000000000000000000000000000",
// };
// // Define the EIP-7702 AuthorizationRequest
// const authRequest: ethers.AuthorizationRequest = {
// address: signingConfig.expectedEthAddress,
// chainId: chainId,
// nonce: 0,
// };
// try {
// // Sign the authorization

Copilot uses AI. Check for mistakes.
// const signatureAuthorization =
// await connectedSigner.signAuthorization(authRequest);

// // Log for debugging
// console.log(
// "Signature Authorization:",
// JSON.stringify(
// signatureAuthorization,
// (key, value) => {
// if (typeof value === "bigint") return value.toString();
// return value;
// },
// 2
// )
// );
// console.log(
// "Expected Signer Address:",
// signingConfig.expectedEthAddress
// );

// // Extract signature components
// const { r, s, v } = signatureAuthorization.signature;
// expect(r).toMatch(/^0x/);
// expect(s).toMatch(/^0x/);
// expect(v).toBeGreaterThanOrEqual(27);
// expect(v).toBeLessThanOrEqual(28);

// // Get serialized signature
// const signatureToVerify = ethers.Signature.from(
// signatureAuthorization.signature
// ).serialized;
// expect(signatureToVerify).toMatch(/^0x/);

// // Define EIP-712 types
// const types = {
// Authorization: [
// { name: "contract", type: "address" },
// { name: "chainId", type: "uint256" },
// { name: "nonce", type: "uint256" },
// ],
// };

// // Define EIP-712 message
// const message = {
// contract: authRequest.address,
// chainId: authRequest.chainId,
// nonce: authRequest.nonce,
// };

// // Verify the signature
// const recoveredAddress = verifyTypedData(
// domain,
// types,
// message,
// signatureToVerify
// );
// console.log("Recovered Address (Test):", recoveredAddress);
// expect(recoveredAddress).toEqual(signingConfig.expectedEthAddress);

// // Verify the authorization object
// expect(signatureAuthorization.address).toEqual(
// signingConfig.expectedEthAddress
// );
// expect(signatureAuthorization.chainId).toEqual(chainId);
// expect(signatureAuthorization.nonce).toEqual(BigInt(0));
// } catch (error) {
// console.error(
// "Error:",
// JSON.stringify(
// {
// name: error instanceof Error ? error.name : "Unknown",
// message: error instanceof Error ? error.message : String(error),
// cause:
// error instanceof TurnkeyActivityError
// ? error.cause
// : undefined,
// stack: error instanceof Error ? error.stack : undefined,
// },
// (key, value) => {
// if (typeof value === "bigint") return value.toString();
// return value;
// },
// 2
// )
// );
// throw error; // Re-throw to inspect
// }
// });
testCase("it signs EIP-7702 authorization", async () => {
// Define the EIP-712 domain for EIP-7702
const domain = {
name: "EIP7702Authorization",
version: "1",
chainId: chainId,
verifyingContract: "0x0000000000000000000000000000000000000000",
};

// Define the EIP-7702 AuthorizationRequest
const authRequest: EIP7702AuthorizationRequest = {
address: signingConfig.expectedEthAddress,
chainId: chainId,
nonce: 0,
code: "0x1234", // Example bytecode
};

try {
// Sign the authorization
const signatureAuthorization =
await connectedSigner.signAuthorization(authRequest);

// Extract signature components
const { r, s, v } = signatureAuthorization.signature;
expect(r).toMatch(/^0x/);
expect(s).toMatch(/^0x/);
expect(v).toBeGreaterThanOrEqual(27);
expect(v).toBeLessThanOrEqual(28);

// Get serialized signature
const signatureToVerify = ethers.Signature.from(
signatureAuthorization.signature,
).serialized;
expect(signatureToVerify).toMatch(/^0x/);

// Define EIP-712 types
const types = {
Authorization: [
{ name: "contract", type: "address" },
{ name: "chainId", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "code", type: "bytes" },
],
};

// Define EIP-712 message
const message = {
contract: authRequest.address,
chainId: authRequest.chainId,
nonce: authRequest.nonce,
code: authRequest.code || "0x",
};

// Verify the signature
const recoveredAddress = verifyTypedData(
domain,
types,
message,
signatureToVerify,
);

expect(recoveredAddress).toEqual(signingConfig.expectedEthAddress);

// Verify the authorization object
expect(signatureAuthorization.address).toEqual(
signingConfig.expectedEthAddress,
);
expect(signatureAuthorization.chainId).toEqual(chainId);
expect(signatureAuthorization.nonce).toEqual(BigInt(0));
} catch (error) {
console.error(
"Error:",
JSON.stringify(
{
name: error instanceof Error ? error.name : "Unknown",
message: error instanceof Error ? error.message : String(error),
cause:
error instanceof TurnkeyActivityError
? String(error.cause)
: undefined,
stack: error instanceof Error ? error.stack : undefined,
},
(_, value) => {
if (typeof value === "bigint") return value.toString();
return value;
},
2,
),
);
throw error;
}
});
});
});
});
Expand Down
Loading
Loading