Skip to content

Commit f1d6391

Browse files
authored
Add endpoint to withdraw all funds (#337)
* Add endpoint to withdraw all funds * Register withdraw endpoint
1 parent b36c073 commit f1d6391

File tree

7 files changed

+225
-7
lines changed

7 files changed

+225
-7
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
},
3232
"dependencies": {
3333
"@aws-sdk/client-kms": "^3.398.0",
34+
"@eth-optimism/sdk": "^3.1.6",
3435
"@fastify/cookie": "^8.3.0",
3536
"@fastify/cors": "^8.2.1",
3637
"@fastify/express": "^2.3.0",

src/schema/extension.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export type ContractExtension =
77
| "marketplace-v3-offers"
88
| "roles"
99
| "none"
10+
| "withdraw"
1011
| "deploy-prebuilt"
1112
| "deploy-published"
1213
| "account-factory"
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { Static, Type } from "@sinclair/typebox";
2+
import { FastifyInstance } from "fastify";
3+
import { StatusCodes } from "http-status-codes";
4+
import { prisma } from "../../../db/client";
5+
import {
6+
standardResponseSchema,
7+
transactionWritesResponseSchema,
8+
} from "../../schemas/sharedApiSchemas";
9+
import { walletAuthSchema, walletParamSchema } from "../../schemas/wallet";
10+
import { getChainIdFromChain } from "../../utils/chain";
11+
12+
const ParamsSchema = Type.Omit(walletParamSchema, ["walletAddress"]);
13+
14+
const BodySchema = Type.Object({
15+
toAddress: Type.String({
16+
description: "Address to withdraw all funds to",
17+
}),
18+
});
19+
20+
export async function withdraw(fastify: FastifyInstance) {
21+
fastify.route<{
22+
Params: Static<typeof ParamsSchema>;
23+
Reply: Static<typeof transactionWritesResponseSchema>;
24+
Body: Static<typeof BodySchema>;
25+
}>({
26+
method: "POST",
27+
url: "/backend-wallet/:chain/withdraw",
28+
schema: {
29+
summary: "Withdraw all funds",
30+
description: "Withdraw all funds from this wallet to another wallet",
31+
tags: ["Backend Wallet"],
32+
operationId: "withdraw",
33+
params: ParamsSchema,
34+
body: BodySchema,
35+
headers: Type.Omit(walletAuthSchema, ["x-account-address"]),
36+
response: {
37+
...standardResponseSchema,
38+
[StatusCodes.OK]: transactionWritesResponseSchema,
39+
},
40+
},
41+
handler: async (req, res) => {
42+
const { chain } = req.params;
43+
const { toAddress } = req.body;
44+
const walletAddress = req.headers["x-backend-wallet-address"] as string;
45+
46+
const chainId = await getChainIdFromChain(chain);
47+
48+
const { id: queueId } = await prisma.transactions.create({
49+
data: {
50+
chainId: chainId.toString(),
51+
extension: "withdraw",
52+
functionName: "transfer",
53+
fromAddress: walletAddress,
54+
toAddress,
55+
data: "0x",
56+
},
57+
});
58+
59+
res.status(StatusCodes.OK).send({
60+
result: {
61+
queueId,
62+
},
63+
});
64+
},
65+
});
66+
}

src/server/routes/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ import { revokeRelayer } from "./relayer/revoke";
9595
// System
9696
import { getAllTransactions } from "./backend-wallet/getTransactions";
9797
import { sendTransactionBatch } from "./backend-wallet/sendTransactionBatch";
98+
import { withdraw } from "./backend-wallet/withdraw";
9899
import { healthCheck } from "./health";
99100
import { home } from "./home";
100101
import { updateRelayer } from "./relayer/update";
@@ -108,6 +109,7 @@ export const withRoutes = async (fastify: FastifyInstance) => {
108109
await fastify.register(getBalance);
109110
await fastify.register(getAll);
110111
await fastify.register(transfer);
112+
await fastify.register(withdraw);
111113
await fastify.register(sendTransaction);
112114
await fastify.register(sendTransactionBatch);
113115
await fastify.register(signTransaction);

src/worker/tasks/processTx.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { getSdk } from "../../utils/cache/getSdk";
2424
import { env } from "../../utils/env";
2525
import { logger } from "../../utils/logger";
2626
import { randomNonce } from "../utils/nonce";
27+
import { getWithdrawalValue } from "../utils/withdraw";
2728

2829
type SentTxStatus =
2930
| {
@@ -192,11 +193,23 @@ export const processTx = async () => {
192193
const nonce = startNonce.add(sentTxCount);
193194

194195
try {
196+
let value: ethers.BigNumberish = tx.value!;
197+
198+
if (tx.extension === "withdraw") {
199+
value = await getWithdrawalValue({
200+
provider,
201+
chainId,
202+
fromAddress: tx.fromAddress!,
203+
toAddress: tx.toAddress!,
204+
gasOverrides,
205+
});
206+
}
207+
195208
const txRequest = await signer.populateTransaction({
196209
to: tx.toAddress!,
197210
from: tx.fromAddress!,
198211
data: tx.data!,
199-
value: tx.value!,
212+
value,
200213
nonce,
201214
...gasOverrides,
202215
});

src/worker/utils/withdraw.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { asL2Provider } from "@eth-optimism/sdk";
2+
import { Base, Optimism, Zora } from "@thirdweb-dev/chains";
3+
import { getDefaultGasOverrides, toUnits } from "@thirdweb-dev/sdk";
4+
import { BigNumber, ethers } from "ethers";
5+
6+
interface GetWithdrawalValueParams {
7+
provider: ethers.providers.Provider;
8+
chainId: number;
9+
fromAddress: string;
10+
toAddress: string;
11+
gasOverrides: Awaited<ReturnType<typeof getDefaultGasOverrides>>;
12+
}
13+
14+
export const getWithdrawalValue = async ({
15+
provider,
16+
chainId,
17+
fromAddress,
18+
toAddress,
19+
gasOverrides,
20+
}: GetWithdrawalValueParams): Promise<BigNumber> => {
21+
const balance = await provider.getBalance(fromAddress);
22+
23+
let transferCost = BigNumber.from(
24+
gasOverrides.maxFeePerGas || gasOverrides.gasPrice || toUnits(1, 9),
25+
).mul(21000);
26+
27+
if (
28+
chainId === Base.chainId ||
29+
chainId === Optimism.chainId ||
30+
chainId === Zora.chainId
31+
) {
32+
const l2Provider = asL2Provider(provider);
33+
transferCost = await l2Provider.estimateTotalGasCost({
34+
to: toAddress,
35+
value: 1,
36+
});
37+
}
38+
39+
transferCost = transferCost.mul(120).div(100); // +20% in all cases for safety
40+
return BigNumber.from(balance).sub(transferCost);
41+
};

yarn.lock

Lines changed: 100 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,20 @@
600600
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.40.0.tgz#3ba73359e11f5a7bd3e407f70b3528abfae69cec"
601601
integrity sha512-ElyB54bJIhXQYVKjDSvCkPO1iU1tSAeVQJbllWJq1XQSmmA4dgFk8CbiBGpiOPxleE48vDogxCtmMYku4HSVLA==
602602

603+
"@eth-optimism/contracts-bedrock@0.16.2":
604+
version "0.16.2"
605+
resolved "https://registry.yarnpkg.com/@eth-optimism/contracts-bedrock/-/contracts-bedrock-0.16.2.tgz#065ad561c3c8b942e4e0dd3d0ea6ed7e00a0f8f0"
606+
integrity sha512-a2+f7soDbrd6jV74U02EpyMwQt2iZeDZ4c2ZwgkObcxXUZLZQ2ELt/VRFBf8TIL3wYcBOGpUa1aXAE2oHQ7oRA==
607+
608+
"@eth-optimism/contracts@0.6.0":
609+
version "0.6.0"
610+
resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.6.0.tgz#15ae76222a9b4d958a550cafb1960923af613a31"
611+
integrity sha512-vQ04wfG9kMf1Fwy3FEMqH2QZbgS0gldKhcBeBUPfO8zu68L61VI97UDXmsMQXzTsEAxK8HnokW3/gosl4/NW3w==
612+
dependencies:
613+
"@eth-optimism/core-utils" "0.12.0"
614+
"@ethersproject/abstract-provider" "^5.7.0"
615+
"@ethersproject/abstract-signer" "^5.7.0"
616+
603617
"@eth-optimism/contracts@^0.5.21":
604618
version "0.5.40"
605619
resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.5.40.tgz#d13a04a15ea947a69055e6fc74d87e215d4c936a"
@@ -631,6 +645,38 @@
631645
bufio "^1.0.7"
632646
chai "^4.3.4"
633647

648+
"@eth-optimism/core-utils@0.13.1":
649+
version "0.13.1"
650+
resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.13.1.tgz#f15ec207a629c9bbf1a10425c1b4a4c0be544755"
651+
integrity sha512-1FvzbUmCEy9zSKPG1QWg2VfA2Cy90xBA9Wkp11lXXrz91zUPCNCNSRTujXWYIC86ketNsZp7p4njSf6lTycHCw==
652+
dependencies:
653+
"@ethersproject/abi" "^5.7.0"
654+
"@ethersproject/abstract-provider" "^5.7.0"
655+
"@ethersproject/address" "^5.7.0"
656+
"@ethersproject/bignumber" "^5.7.0"
657+
"@ethersproject/bytes" "^5.7.0"
658+
"@ethersproject/constants" "^5.7.0"
659+
"@ethersproject/contracts" "^5.7.0"
660+
"@ethersproject/keccak256" "^5.7.0"
661+
"@ethersproject/properties" "^5.7.0"
662+
"@ethersproject/rlp" "^5.7.0"
663+
"@ethersproject/web" "^5.7.1"
664+
chai "^4.3.9"
665+
ethers "^5.7.2"
666+
node-fetch "^2.6.7"
667+
668+
"@eth-optimism/sdk@^3.1.6":
669+
version "3.1.6"
670+
resolved "https://registry.yarnpkg.com/@eth-optimism/sdk/-/sdk-3.1.6.tgz#00da7910f3fc5e4ba127c74315a63b0191384381"
671+
integrity sha512-YU3Sx4jPFfdXW4gs0PvnFDFPrJjbsaFxAJrsqxDpkUH3fMC3MmQgECYdkj8y1xTO6CTHm9gWLNC2WQdYTdNJsQ==
672+
dependencies:
673+
"@eth-optimism/contracts" "0.6.0"
674+
"@eth-optimism/contracts-bedrock" "0.16.2"
675+
"@eth-optimism/core-utils" "0.13.1"
676+
lodash "^4.17.21"
677+
merkletreejs "^0.3.11"
678+
rlp "^2.2.7"
679+
634680
"@ethereumjs/common@^2.4.0":
635681
version "2.6.5"
636682
resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.5.tgz#0a75a22a046272579d91919cb12d84f2756e8d30"
@@ -1288,7 +1334,7 @@
12881334
"@ethersproject/properties" "^5.5.0"
12891335
"@ethersproject/strings" "^5.5.0"
12901336

1291-
"@ethersproject/web@5.7.1", "@ethersproject/web@^5.5.0", "@ethersproject/web@^5.7.0":
1337+
"@ethersproject/web@5.7.1", "@ethersproject/web@^5.5.0", "@ethersproject/web@^5.7.0", "@ethersproject/web@^5.7.1":
12921338
version "5.7.1"
12931339
resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae"
12941340
integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==
@@ -4542,6 +4588,19 @@ chai@^4.3.4:
45424588
pathval "^1.1.1"
45434589
type-detect "^4.0.5"
45444590

4591+
chai@^4.3.9:
4592+
version "4.3.10"
4593+
resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384"
4594+
integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==
4595+
dependencies:
4596+
assertion-error "^1.1.0"
4597+
check-error "^1.0.3"
4598+
deep-eql "^4.1.3"
4599+
get-func-name "^2.0.2"
4600+
loupe "^2.3.6"
4601+
pathval "^1.1.1"
4602+
type-detect "^4.0.8"
4603+
45454604
chalk@^2.4.2:
45464605
version "2.4.2"
45474606
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
@@ -4574,6 +4633,13 @@ check-error@^1.0.2:
45744633
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
45754634
integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==
45764635

4636+
check-error@^1.0.3:
4637+
version "1.0.3"
4638+
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694"
4639+
integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==
4640+
dependencies:
4641+
get-func-name "^2.0.2"
4642+
45774643
chokidar@3.5.3, chokidar@^3.4.0, chokidar@^3.5.2:
45784644
version "3.5.3"
45794645
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
@@ -4897,6 +4963,11 @@ crypto-js@^4.1.1:
48974963
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf"
48984964
integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==
48994965

4966+
crypto-js@^4.2.0:
4967+
version "4.2.0"
4968+
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631"
4969+
integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==
4970+
49004971
crypto@1.0.1:
49014972
version "1.0.1"
49024973
resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037"
@@ -4958,7 +5029,7 @@ decompress-response@^3.3.0:
49585029
dependencies:
49595030
mimic-response "^1.0.0"
49605031

4961-
deep-eql@^4.1.2:
5032+
deep-eql@^4.1.2, deep-eql@^4.1.3:
49625033
version "4.1.3"
49635034
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d"
49645035
integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==
@@ -5522,7 +5593,7 @@ ethers-gcp-kms-signer@^1.1.6:
55225593
ethers "5.5.1"
55235594
key-encoder "2.0.3"
55245595

5525-
ethers@5, ethers@5.7.2, ethers@^5.4.1, ethers@^5.7.0, ethers@^5.7.1:
5596+
ethers@5, ethers@5.7.2, ethers@^5.4.1, ethers@^5.7.0, ethers@^5.7.1, ethers@^5.7.2:
55265597
version "5.7.2"
55275598
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e"
55285599
integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==
@@ -6078,6 +6149,11 @@ get-func-name@^2.0.0:
60786149
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
60796150
integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==
60806151

6152+
get-func-name@^2.0.1, get-func-name@^2.0.2:
6153+
version "2.0.2"
6154+
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41"
6155+
integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==
6156+
60816157
get-intrinsic@^1.0.2, get-intrinsic@^1.1.3:
60826158
version "1.2.1"
60836159
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82"
@@ -7144,6 +7220,13 @@ loupe@^2.3.1:
71447220
dependencies:
71457221
get-func-name "^2.0.0"
71467222

7223+
loupe@^2.3.6:
7224+
version "2.3.7"
7225+
resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697"
7226+
integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==
7227+
dependencies:
7228+
get-func-name "^2.0.1"
7229+
71477230
lru-cache@^5.1.1:
71487231
version "5.1.1"
71497232
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
@@ -7263,6 +7346,17 @@ merkletreejs@^0.2.24:
72637346
treeify "^1.1.0"
72647347
web3-utils "^1.3.4"
72657348

7349+
merkletreejs@^0.3.11:
7350+
version "0.3.11"
7351+
resolved "https://registry.yarnpkg.com/merkletreejs/-/merkletreejs-0.3.11.tgz#e0de05c3ca1fd368de05a12cb8efb954ef6fc04f"
7352+
integrity sha512-LJKTl4iVNTndhL+3Uz/tfkjD0klIWsHlUzgtuNnNrsf7bAlXR30m+xYB7lHr5Z/l6e/yAIsr26Dabx6Buo4VGQ==
7353+
dependencies:
7354+
bignumber.js "^9.0.1"
7355+
buffer-reverse "^1.0.1"
7356+
crypto-js "^4.2.0"
7357+
treeify "^1.1.0"
7358+
web3-utils "^1.3.4"
7359+
72667360
methods@^1.1.2, methods@~1.1.2:
72677361
version "1.1.2"
72687362
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@@ -7507,7 +7601,7 @@ node-fetch@^2.6.1, node-fetch@^2.6.11, node-fetch@^2.6.12:
75077601
dependencies:
75087602
whatwg-url "^5.0.0"
75097603

7510-
node-fetch@^2.6.9:
7604+
node-fetch@^2.6.7, node-fetch@^2.6.9:
75117605
version "2.7.0"
75127606
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
75137607
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
@@ -8461,7 +8555,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
84618555
hash-base "^3.0.0"
84628556
inherits "^2.0.1"
84638557

8464-
rlp@^2.2.3, rlp@^2.2.4:
8558+
rlp@^2.2.3, rlp@^2.2.4, rlp@^2.2.7:
84658559
version "2.2.7"
84668560
resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf"
84678561
integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==
@@ -9104,7 +9198,7 @@ type-check@~0.3.2:
91049198
dependencies:
91059199
prelude-ls "~1.1.2"
91069200

9107-
type-detect@^4.0.0, type-detect@^4.0.5:
9201+
type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8:
91089202
version "4.0.8"
91099203
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
91109204
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==

0 commit comments

Comments
 (0)