Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ethers v5 as of release v2024.4.5 #244

Open
wants to merge 5 commits into
base: ethers-v5
Choose a base branch
from
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
1 change: 0 additions & 1 deletion examples/deployer/.env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ API_PRIVATE_KEY="<Turnkey API Private Key>"
BASE_URL="https://api.turnkey.com"
ORGANIZATION_ID="<Turnkey organization ID>"
SIGN_WITH="<Turnkey Wallet Account Address, Private Key Address, or Private Key ID>" # if blank, we will create a wallet for you
PRIVATE_KEY_ID="<Turnkey (crypto) private key ID>" # if you leave it blank, we'll create one for you via calling the Turnkey API
INFURA_KEY="<Infura API Key>"
2 changes: 1 addition & 1 deletion examples/deployer/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Example: `deployer`

This example shows how to deploy a smart contract using [`Ethers`](https://docs.ethers.org/v6/api/providers/#Signer) with Turnkey.
This example shows how to deploy a smart contract using [`Ethers`](https://docs.ethers.org/v5/api/signer/) with Turnkey.

## Getting started

Expand Down
2 changes: 1 addition & 1 deletion examples/deployer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"@turnkey/ethers": "workspace:*",
"@turnkey/http": "workspace:*",
"dotenv": "^16.0.3",
"ethers": "^6.10.0",
"ethers": "^5.7.2",
"solc": "0.8.13"
}
}
21 changes: 10 additions & 11 deletions examples/deployer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,20 @@ async function main() {
signWith: process.env.SIGN_WITH!,
});

// Connect it with a Provider (https://docs.ethers.org/v6/api/providers/)
// Connect it with a Provider (https://docs.ethers.org/v5/api/providers/)
const network = "goerli";
const connectedSigner = turnkeySigner.connect(
new ethers.InfuraProvider(network)
);
const provider = new ethers.providers.InfuraProvider(network);
const connectedSigner = turnkeySigner.connect(provider);

const chainId = (await connectedSigner.provider?.getNetwork())?.chainId ?? 0;
const chainId = await connectedSigner.getChainId();
const address = await connectedSigner.getAddress();
const balance = (await connectedSigner.provider?.getBalance(address)) ?? 0;
const balance = await connectedSigner.getBalance();

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

if (balance === 0) {
if (balance.isZero()) {
let warningMessage =
"The transaction won't be broadcasted because your account balance is zero.\n";
if (network === "goerli") {
Expand All @@ -74,13 +73,13 @@ async function main() {
const contract = await factory.deploy();

// The address is available immediately, but the contract is NOT deployed yet
print("Contract address", await contract.getAddress());
print("Contract address", contract.address);

const deploymentTransaction = await contract.deploymentTransaction();
await contract.deployTransaction.wait();

print(
`Contract has been deployed:`,
`https://${network}.etherscan.io/tx/${deploymentTransaction?.hash}`
`https://${network}.etherscan.io/tx/${contract.deployTransaction.hash}`
);
}

Expand Down
2 changes: 1 addition & 1 deletion examples/rebalancer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
"@turnkey/ethers": "workspace:*",
"@turnkey/http": "workspace:*",
"dotenv": "^16.0.3",
"ethers": "^6.10.0"
"ethers": "^5.7.2"
}
}
96 changes: 44 additions & 52 deletions examples/rebalancer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ dotenv.config({ path: path.resolve(process.cwd(), ".env.local") });

import { TurnkeyClient } from "@turnkey/http";
import { ApiKeyStamper } from "@turnkey/api-key-stamper";
import { FeeData } from "ethers";
import { ethers } from "ethers";
import { isKeyOfObject } from "./utils";
import {
createPrivateKey,
Expand All @@ -27,8 +27,8 @@ import keys from "./keys";
const SWEEP_THRESHOLD = 100000000000000; // 0.0001 ETH
const MIN_INTERVAL_MS = 10000; // 10 seconds
const MAX_INTERVAL_MS = 60000; // 60 seconds
const TRANSFER_GAS_LIMIT = 21000n;
const GAS_MULTIPLIER = 2n;
const TRANSFER_GAS_LIMIT = 21000;
const GAS_MULTIPLIER = 2;
const ACTIVITIES_LIMIT = "100";

// For demonstration purposes, create a globally accessible TurnkeyClient
Expand Down Expand Up @@ -232,9 +232,10 @@ async function fundImpl() {
}

await sendEth(
provider,
connectedSigner,
ethAddress.address,
120000000000000n // 0.00012 ETH
ethers.BigNumber.from(120000000000000) // 0.00012 ETH
);
}
}
Expand Down Expand Up @@ -282,45 +283,41 @@ async function sweepImpl() {
for (const pk of shortTermStoragePrivateKeys!) {
const provider = getProvider();
const connectedSigner = getTurnkeySigner(provider, pk.privateKeyId);
const balance =
(await connectedSigner.provider?.getBalance(ethAddress.address)) ?? 0n;
const balance = await connectedSigner.getBalance();
const feeData = await connectedSigner.getFeeData();
const address = await connectedSigner.getAddress();
const originalFeeData = await connectedSigner.provider?.getFeeData();

const updatedMaxFeePerGas = originalFeeData?.maxFeePerGas
? originalFeeData.maxFeePerGas * GAS_MULTIPLIER
: 0n;
const updatedMaxPriorityFeePerGas = originalFeeData?.maxPriorityFeePerGas
? originalFeeData.maxPriorityFeePerGas * GAS_MULTIPLIER
: 0n;
const feeData = new FeeData(
originalFeeData?.gasPrice,
updatedMaxFeePerGas,
updatedMaxPriorityFeePerGas
);
const gasRequired =
feeData?.maxFeePerGas && feeData?.maxPriorityFeePerGas
? (feeData?.maxFeePerGas + feeData?.maxPriorityFeePerGas) *
TRANSFER_GAS_LIMIT
: 0n;

if (balance < SWEEP_THRESHOLD) {
feeData.maxFeePerGas = feeData.maxFeePerGas!.mul(GAS_MULTIPLIER);
feeData.maxPriorityFeePerGas =
feeData.maxPriorityFeePerGas!.mul(GAS_MULTIPLIER);

const gasRequired = feeData
.maxFeePerGas!.add(feeData.maxPriorityFeePerGas!)
.mul(TRANSFER_GAS_LIMIT); // 21000 is the gas limit for a simple transfer

if (balance.lt(SWEEP_THRESHOLD)) {
console.log(
`Address ${address} has an insufficient balance for sweep. Moving on...`
);
continue;
}

const sweepAmount = balance - gasRequired * 2n; // be relatively conservative with sweep amount to prevent overdraft
const sweepAmount = balance.sub(gasRequired.mul(2)); // be relatively conservative with sweep amount to prevent overdraft

if (sweepAmount === 0n) {
if (sweepAmount.lt(0)) {
console.log(
`Address ${address} has an insufficient balance for sweep. Moving on...`
);
continue;
}

await sendEth(connectedSigner, ethAddress.address, sweepAmount, feeData);
await sendEth(
provider,
connectedSigner,
ethAddress.address,
sweepAmount,
feeData
);
}
}

Expand Down Expand Up @@ -370,36 +367,31 @@ async function recycleImpl() {
);
}

const balance =
(await connectedSigner.provider?.getBalance(ethAddress.address)) ?? 0n;

const originalFeeData = await connectedSigner.provider?.getFeeData();

const updatedMaxFeePerGas = originalFeeData?.maxFeePerGas
? originalFeeData.maxFeePerGas * GAS_MULTIPLIER
: null;
const updatedMaxPriorityFeePerGas = originalFeeData?.maxPriorityFeePerGas
? originalFeeData.maxPriorityFeePerGas * GAS_MULTIPLIER
: null;
const feeData = new FeeData(
originalFeeData?.gasPrice,
updatedMaxFeePerGas,
updatedMaxPriorityFeePerGas
);
const gasRequired =
feeData?.maxFeePerGas && feeData?.maxPriorityFeePerGas
? (feeData?.maxFeePerGas + feeData?.maxPriorityFeePerGas) *
TRANSFER_GAS_LIMIT
: 0n;
const balance = await connectedSigner.getBalance();
const feeData = await connectedSigner.getFeeData();

feeData.maxFeePerGas = feeData.maxFeePerGas!.mul(GAS_MULTIPLIER);
feeData.maxPriorityFeePerGas =
feeData.maxPriorityFeePerGas!.mul(GAS_MULTIPLIER);

const recycleAmount = balance - gasRequired * 2n; // be relatively conservative with sweep amount to prevent overdraft
const gasRequired = feeData
.maxFeePerGas!.add(feeData.maxPriorityFeePerGas!)
.mul(TRANSFER_GAS_LIMIT); // 21000 is the gas limit for a simple transfer

if (recycleAmount <= 0n) {
const recycleAmount = balance.sub(gasRequired.mul(2)); // be relatively conservative with sweep amount to prevent overdraft

if (recycleAmount.lte(0)) {
console.log("Insufficient balance for recycle...");
return;
}

await sendEth(connectedSigner, ethAddress.address, recycleAmount, feeData);
await sendEth(
provider,
connectedSigner,
ethAddress.address,
recycleAmount,
feeData
);
}

// two approaches:
Expand Down
12 changes: 7 additions & 5 deletions examples/rebalancer/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ const DEFAULT_ENV = Environment.SEPOLIA;
// Load environment variables from `.env.local`
dotenv.config({ path: path.resolve(process.cwd(), ".env.local") });

let provider = new ethers.InfuraProvider(
let provider = new ethers.providers.InfuraProvider(
DEFAULT_ENV,
process.env.INFURA_KEY || DEFAULT_INFURA_COMMUNITY_KEY
);

export function getProvider(env = Environment.SEPOLIA): ethers.Provider {
export function getProvider(
env = Environment.SEPOLIA
): ethers.providers.Provider {
if (env !== Environment.SEPOLIA) {
provider = new ethers.InfuraProvider(
provider = new ethers.providers.InfuraProvider(
env,
process.env.INFURA_KEY || DEFAULT_INFURA_COMMUNITY_KEY
);
Expand All @@ -29,9 +31,9 @@ export function getProvider(env = Environment.SEPOLIA): ethers.Provider {
}

// getTurnkeySigner returns a TurnkeySigner connected to the passed-in Provider
// (https://docs.ethers.org/v6/api/providers/)
// (https://docs.ethers.org/v5/api/providers/)
export function getTurnkeySigner(
provider: ethers.Provider,
provider: ethers.providers.Provider,
signWith: string
): TurnkeySigner {
const turnkeyClient = new TurnkeyClient(
Expand Down
47 changes: 22 additions & 25 deletions examples/rebalancer/src/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { ethers } from "ethers";
import { toReadableAmount, print } from "./utils";

export async function broadcastTx(
provider: ethers.Provider,
provider: ethers.providers.Provider,
signedTx: string,
activityId: string
) {
const network = await provider.getNetwork();
const txHash = ethers.keccak256(signedTx);
const txHash = ethers.utils.keccak256(signedTx);

console.log(
[
Expand All @@ -19,14 +19,13 @@ export async function broadcastTx(
].join("\n")
);

const confirmations =
(await (await provider.getTransaction(txHash))?.confirmations()) ?? 0;
if (confirmations > 0) {
const transaction = await provider.getTransaction(txHash);
if (transaction?.confirmations > 0) {
console.log(`Transaction ${txHash} has already been broadcasted\n`);
return;
}

const { hash } = await provider.broadcastTransaction(signedTx);
const { hash } = await provider.sendTransaction(signedTx);

console.log(`Awaiting confirmation for transaction hash ${hash}...\n`);

Expand All @@ -39,47 +38,46 @@ export async function broadcastTx(
}

export async function sendEth(
provider: ethers.providers.Provider,
connectedSigner: ethers.Signer,
destinationAddress: string,
value: bigint,
precalculatedFeeData: ethers.FeeData | undefined = undefined
value: ethers.BigNumber,
precalculatedFeeData: ethers.providers.FeeData | undefined = undefined
) {
const network = await connectedSigner.provider?.getNetwork();
const network = await provider.getNetwork();
const balance = await connectedSigner.getBalance();
const address = await connectedSigner.getAddress();
const balance = (await connectedSigner.provider?.getBalance(address)) ?? 0n;

print("Address:", address);
print("Balance:", `${ethers.formatEther(balance)} Ether`);
print("Balance:", `${ethers.utils.formatEther(balance)} Ether`);

if (balance === 0n) {
if (balance.isZero()) {
let warningMessage =
"The transaction won't be broadcast because your account balance is zero.\n";
if (network?.name === "sepolia") {
if (network.name === "sepolia") {
warningMessage +=
"Use https://sepoliafaucet.com/ to request funds on Sepolia, then run the script again.\n";
}

throw new Error(warningMessage);
}

const feeData =
precalculatedFeeData || (await connectedSigner.provider?.getFeeData());
const gasRequired =
((feeData?.maxFeePerGas ?? 0n) + (feeData?.maxPriorityFeePerGas ?? 0n)) *
21000n;

const totalCost = gasRequired + value;
const feeData = precalculatedFeeData || (await connectedSigner.getFeeData());
const gasRequired = feeData
.maxFeePerGas!.add(feeData.maxPriorityFeePerGas!)
.mul(21000);
const totalCost = gasRequired.add(value);

if (balance < totalCost) {
if (balance.lt(totalCost)) {
console.error(`Insufficient ETH balance of ${balance}. Needs ${totalCost}`);
}

const transactionRequest = {
to: destinationAddress,
value,
type: 2,
maxFeePerGas: feeData?.maxFeePerGas || 0n,
maxPriorityFeePerGas: feeData?.maxPriorityFeePerGas || 0n,
maxFeePerGas: feeData.maxFeePerGas!,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas!,
};

let sentTx;
Expand All @@ -88,15 +86,14 @@ export async function sendEth(

console.log(`Awaiting confirmation for tx hash ${sentTx.hash}...\n`);
await connectedSigner.provider?.waitForTransaction(sentTx.hash, 1);
const networkName = await connectedSigner.provider?.getNetwork();

print(
`Sent ${toReadableAmount(
value.toString(),
18,
12
)} ETH to ${destinationAddress}:`,
`https://${networkName}.etherscan.io/tx/${sentTx.hash}`
`https://${network.name}.etherscan.io/tx/${sentTx.hash}`
);
} catch (error: any) {
if (error.toString().includes("ACTIVITY_STATUS_CONSENSUS_NEEDED")) {
Expand Down
Loading
Loading