Skip to content

Commit

Permalink
functional upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewkmin committed Oct 11, 2023
1 parent 1452f06 commit 5cbdce8
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 47 deletions.
2 changes: 1 addition & 1 deletion examples/with-ethers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
"@turnkey/http": "workspace:*",
"@turnkey/api-key-stamper": "workspace:*",
"dotenv": "^16.0.3",
"ethers": "^5.7.2"
"ethers": "^6.7.1"
}
}
28 changes: 14 additions & 14 deletions examples/with-ethers/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,23 @@ async function main() {

// Bring your own provider (such as Alchemy or Infura: https://docs.ethers.org/v5/api/providers/)
const network = "goerli";
const provider = new ethers.providers.InfuraProvider(network);
const provider = new ethers.InfuraProvider(network);
const connectedSigner = turnkeySigner.connect(provider);

const chainId = await connectedSigner.getChainId();
const chainId = await connectedSigner.provider!.getNetwork();
const address = await connectedSigner.getAddress();
const balance = await connectedSigner.getBalance();
const transactionCount = await connectedSigner.getTransactionCount();
const balance = await connectedSigner.provider!.getBalance(address);
const transactionCount = await connectedSigner.provider!.getTransactionCount(address);

print("Network:", `${network} (chain ID ${chainId})`);
print("Address:", address);
print("Balance:", `${ethers.utils.formatEther(balance)} Ether`);
print("Balance:", `${ethers.formatEther(balance)} Ether`);
print("Transaction count:", `${transactionCount}`);

// 1. Sign a raw payload (`eth_sign` style)
const message = "Hello Turnkey";
const signature = await connectedSigner.signMessage(message);
const recoveredAddress = ethers.utils.verifyMessage(message, signature);
const recoveredAddress = ethers.verifyMessage(message, signature);

print("Turnkey-powered signature:", `${signature}`);
print("Recovered address:", `${recoveredAddress}`);
Expand All @@ -67,15 +67,15 @@ async function main() {
const destinationAddress = "0x2Ad9eA1E677949a536A270CEC812D6e868C88108";
const transactionRequest = {
to: destinationAddress,
value: ethers.utils.parseEther(transactionAmount),
value: ethers.parseEther(transactionAmount),
type: 2,
};

const signedTx = await connectedSigner.signTransaction(transactionRequest);

print("Turnkey-signed transaction:", `${signedTx}`);

if (balance.isZero()) {
if (balance === 0n) {
let warningMessage =
"The transaction won't be broadcasted because your account balance is zero.\n";
if (network === "goerli") {
Expand All @@ -91,7 +91,7 @@ async function main() {
const sentTx = await connectedSigner.sendTransaction(transactionRequest);

print(
`Sent ${ethers.utils.formatEther(sentTx.value)} Ether to ${sentTx.to}:`,
`Sent ${ethers.formatEther(sentTx.value)} Ether to ${sentTx.to}:`,
`https://${network}.etherscan.io/tx/${sentTx.hash}`
);

Expand All @@ -104,17 +104,17 @@ async function main() {
);

// Read from contract
const wethBalance = await wethContract.balanceOf(address);
const wethBalance = await wethContract.balanceOf!(address);

print("WETH Balance:", `${ethers.utils.formatEther(wethBalance)} WETH`);
print("WETH Balance:", `${ethers.formatEther(wethBalance)} WETH`);

// 3. Wrap ETH -> WETH
const depositTx = await wethContract.deposit({
value: ethers.utils.parseEther(transactionAmount),
const depositTx = await wethContract.deposit!({
value: ethers.parseEther(transactionAmount),
});

print(
`Wrapped ${ethers.utils.formatEther(depositTx.value)} ETH:`,
`Wrapped ${ethers.formatEther(depositTx.value)} ETH:`,
`https://${network}.etherscan.io/tx/${depositTx.hash}`
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/ethers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"@nomicfoundation/hardhat-network-helpers": "^1.0.8",
"@nomiclabs/hardhat-ethers": "^2.2.3",
"@openzeppelin/contracts": "^4.9.0",
"ethers": "^5.0.0",
"ethers": "^6.7.1",
"hardhat": "^2.12.7"
},
"engines": {
Expand Down
48 changes: 24 additions & 24 deletions packages/ethers/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ethers } from "ethers";
import { TurnkeyActivityError, TurnkeyRequestError } from "@turnkey/http";
import type { TurnkeyClient } from "@turnkey/http";
import type { TypedDataSigner } from "@ethersproject/abstract-signer";
// import type { TypedDataSigner } from "@ethersproject/abstract-signer";
import type {
UnsignedTransaction,
Bytes,
// UnsignedTransaction,
BytesLike,
TypedDataDomain,
TypedDataField,
} from "ethers";
Expand All @@ -24,23 +24,26 @@ type TConfig = {
privateKeyId: string;
};

export class TurnkeySigner extends ethers.Signer implements TypedDataSigner {
export class TurnkeySigner extends ethers.AbstractSigner {
private readonly client: TurnkeyClient;

public readonly organizationId: string;
public readonly privateKeyId: string;

constructor(config: TConfig, provider?: ethers.providers.Provider) {
constructor(config: TConfig, provider?: ethers.Provider) {
super();

ethers.utils.defineReadOnly(this, "provider", provider);
if (provider != null) {
ethers.defineProperties(this, { provider: provider } as { [K in keyof this]?: this[K]; });
}

this.client = config.client;

this.organizationId = config.organizationId;
this.privateKeyId = config.privateKeyId;
}

connect(provider: ethers.providers.Provider): TurnkeySigner {
connect(provider: ethers.Provider): TurnkeySigner {
return new TurnkeySigner(
{
client: this.client,
Expand Down Expand Up @@ -119,9 +122,11 @@ export class TurnkeySigner extends ethers.Signer implements TypedDataSigner {
}

async signTransaction(
transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>
transaction: ethers.TransactionRequest
): Promise<string> {
const unsignedTx = await ethers.utils.resolveProperties(transaction);
const unsignedTx = (await ethers.resolveProperties(
transaction
)) as ethers.Transaction;

// Mimic the behavior of ethers' `Wallet`:
// - You don't need to pass in `tx.from`
Expand All @@ -130,18 +135,17 @@ export class TurnkeySigner extends ethers.Signer implements TypedDataSigner {
// https://github.com/ethers-io/ethers.js/blob/f97b92bbb1bde22fcc44100af78d7f31602863ab/packages/wallet/src.ts/index.ts#L117-L121
if (unsignedTx.from != null) {
const selfAddress = await this.getAddress();
if (ethers.utils.getAddress(unsignedTx.from) !== selfAddress) {
if (ethers.getAddress(unsignedTx.from.toString()) !== selfAddress) {
throw new Error(
`Transaction \`tx.from\` address mismatch. Self address: ${selfAddress}; \`tx.from\` address: ${unsignedTx.from}`
);
}

delete unsignedTx.from;
// maybe we no longer need to drop this
// delete unsignedTx.from;
}

const serializedTx = ethers.utils.serializeTransaction(
unsignedTx as UnsignedTransaction
);
const serializedTx = ethers.Transaction.from(unsignedTx).unsignedSerialized;
const nonHexPrefixedSerializedTx = serializedTx.replace(/^0x/, "");
const signedTx = await this._signTransactionWithErrorWrapping(
nonHexPrefixedSerializedTx
Expand All @@ -153,8 +157,8 @@ export class TurnkeySigner extends ethers.Signer implements TypedDataSigner {
// - Bytes as a binary message
// - string as a UTF8-message
// i.e. "0x1234" is a SIX (6) byte string, NOT 2 bytes of data
async signMessage(message: string | Bytes): Promise<string> {
const hashedMessage = ethers.utils.hashMessage(message);
async signMessage(message: string | BytesLike): Promise<string> {
const hashedMessage = ethers.hashMessage(message);
const signedMessage = await this._signMessageWithErrorWrapping(
hashedMessage
);
Expand Down Expand Up @@ -197,11 +201,11 @@ export class TurnkeySigner extends ethers.Signer implements TypedDataSigner {
if (activity.status === "ACTIVITY_STATUS_COMPLETED") {
let result = assertNonNull(activity?.result?.signRawPayloadResult);

let assembled = ethers.utils.joinSignature({
let assembled = ethers.Signature.from({
r: `0x${result.r}`,
s: `0x${result.s}`,
v: parseInt(result.v) + 27,
});
}).serialized;

// Assemble the hex
return assertNonNull(assembled);
Expand All @@ -221,7 +225,7 @@ export class TurnkeySigner extends ethers.Signer implements TypedDataSigner {
value: Record<string, any>
): Promise<string> {
// Populate any ENS names
const populated = await ethers.utils._TypedDataEncoder.resolveNames(
const populated = await ethers.TypedDataEncoder.resolveNames(
domain,
types,
value,
Expand All @@ -236,11 +240,7 @@ export class TurnkeySigner extends ethers.Signer implements TypedDataSigner {
);

return this._signMessageWithErrorWrapping(
ethers.utils._TypedDataEncoder.hash(
populated.domain,
types,
populated.value
)
ethers.TypedDataEncoder.hash(populated.domain, types, populated.value)
);
}

Expand Down
56 changes: 49 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 5cbdce8

Please sign in to comment.