From d43eb2ff5d9aa3a5b39bb172ae179b0b1c1af805 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Thu, 18 Apr 2024 16:46:37 +0100 Subject: [PATCH 01/53] feat(cli): deploy via kms --- packages/cli/package.json | 1 + packages/cli/scripts/aws.ts | 51 ++ pnpm-lock.yaml | 931 +++++++++++++++++++++++++++++++++--- 3 files changed, 927 insertions(+), 56 deletions(-) create mode 100644 packages/cli/scripts/aws.ts diff --git a/packages/cli/package.json b/packages/cli/package.json index 80cedb8da2..42a65d2132 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -30,6 +30,7 @@ "test:ci": "pnpm run test" }, "dependencies": { + "@aws-sdk/client-kms": "*", "@ethersproject/abi": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@improbable-eng/grpc-web": "^0.15.0", diff --git a/packages/cli/scripts/aws.ts b/packages/cli/scripts/aws.ts new file mode 100644 index 0000000000..cd59f32b4d --- /dev/null +++ b/packages/cli/scripts/aws.ts @@ -0,0 +1,51 @@ +import { utils } from "ethers"; +import { KMSClient, SignCommand, SignCommandInput, SignCommandOutput } from "@aws-sdk/client-kms"; +import { toAccount } from "viem/accounts"; +import { keccak256, toHex } from "viem"; + +type SignParams = { + keyId: SignCommandInput["KeyId"]; + message: string; + kmsInstance: KMSClient; +}; + +const sign = async (signParams: SignParams): Promise => { + const { keyId, message, kmsInstance } = signParams; + const formatted = Buffer.from(utils.arrayify(message)); + + const command = new SignCommand({ + KeyId: keyId, + Message: formatted, + SigningAlgorithm: "ECDSA_SHA_256", + MessageType: "DIGEST", + }); + + return kmsInstance.send(command); +}; + +const keyId = "PLACEHOLDER"; + +const kmsInstance = new KMSClient({ + region: "eu-west-2", + credentials: { + accessKeyId: "PLACEHOLDER", + secretAccessKey: "PLACEHOLDER", + }, +}); + +const account = toAccount({ + address: "0x0000000000000000000000000000000000000000", + async signMessage({ message }) { + if (typeof message === "string") { + const signature = await sign({ keyId, message: keccak256(toHex(message)), kmsInstance }); + + if (signature.Signature == undefined) { + throw new Error("Signature is undefined."); + } + + return signature.Signature; + } + }, +}); + +console.log(await account.signMessage({ message: "hello" })); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2bba08a5cc..a0004cde67 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,6 +130,9 @@ importers: packages/cli: dependencies: + '@aws-sdk/client-kms': + specifier: '*' + version: 3.556.0 '@ethersproject/abi': specifier: ^5.7.0 version: 5.7.0 @@ -656,10 +659,10 @@ importers: version: 29.5.0(@types/node@18.15.11) ts-jest: specifier: ^29.0.5 - version: 29.0.5(@babel/core@7.24.4)(esbuild@0.17.17)(jest@29.5.0)(typescript@5.4.5) + version: 29.0.5(@babel/core@7.24.4)(esbuild@0.17.17)(jest@29.5.0)(typescript@5.4.2) tsup: specifier: ^6.7.0 - version: 6.7.0(typescript@5.4.5) + version: 6.7.0(postcss@8.4.23)(typescript@5.4.2) type-fest: specifier: ^2.14.0 version: 2.14.0 @@ -1020,10 +1023,10 @@ importers: version: 29.5.0(@types/node@18.15.11) ts-jest: specifier: ^29.0.5 - version: 29.0.5(@babel/core@7.24.4)(esbuild@0.17.17)(jest@29.5.0)(typescript@5.4.5) + version: 29.0.5(@babel/core@7.24.4)(esbuild@0.17.17)(jest@29.5.0)(typescript@5.4.2) tsup: specifier: ^6.7.0 - version: 6.7.0(typescript@5.4.5) + version: 6.7.0(postcss@8.4.23)(typescript@5.4.2) packages/world: dependencies: @@ -1258,6 +1261,481 @@ packages: resolution: {integrity: sha512-fDTBSVzxLj9k1ZjinkawmaQdcXFKMBVK8c+vqMPxwoa94mPMZxBo84yQcqyFVcIcWIkg6qQQmH1ozyT4nqFT/g==} dev: false + /@aws-crypto/ie11-detection@3.0.0: + resolution: {integrity: sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==} + dependencies: + tslib: 1.14.1 + dev: false + + /@aws-crypto/sha256-browser@3.0.0: + resolution: {integrity: sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==} + dependencies: + '@aws-crypto/ie11-detection': 3.0.0 + '@aws-crypto/sha256-js': 3.0.0 + '@aws-crypto/supports-web-crypto': 3.0.0 + '@aws-crypto/util': 3.0.0 + '@aws-sdk/types': 3.535.0 + '@aws-sdk/util-locate-window': 3.535.0 + '@aws-sdk/util-utf8-browser': 3.259.0 + tslib: 1.14.1 + dev: false + + /@aws-crypto/sha256-js@3.0.0: + resolution: {integrity: sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==} + dependencies: + '@aws-crypto/util': 3.0.0 + '@aws-sdk/types': 3.535.0 + tslib: 1.14.1 + dev: false + + /@aws-crypto/supports-web-crypto@3.0.0: + resolution: {integrity: sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==} + dependencies: + tslib: 1.14.1 + dev: false + + /@aws-crypto/util@3.0.0: + resolution: {integrity: sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==} + dependencies: + '@aws-sdk/types': 3.535.0 + '@aws-sdk/util-utf8-browser': 3.259.0 + tslib: 1.14.1 + dev: false + + /@aws-sdk/client-kms@3.556.0: + resolution: {integrity: sha512-+UXRCLtpCI41TPcsfKUxaKkNrpOLFAMlfdo3SF6QzCPXsfYMvRwWwj6EGyuBfjebJUGXBMKRBXgczLA1rq59JA==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-crypto/sha256-browser': 3.0.0 + '@aws-crypto/sha256-js': 3.0.0 + '@aws-sdk/client-sts': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) + '@aws-sdk/core': 3.556.0 + '@aws-sdk/credential-provider-node': 3.556.0 + '@aws-sdk/middleware-host-header': 3.535.0 + '@aws-sdk/middleware-logger': 3.535.0 + '@aws-sdk/middleware-recursion-detection': 3.535.0 + '@aws-sdk/middleware-user-agent': 3.540.0 + '@aws-sdk/region-config-resolver': 3.535.0 + '@aws-sdk/types': 3.535.0 + '@aws-sdk/util-endpoints': 3.540.0 + '@aws-sdk/util-user-agent-browser': 3.535.0 + '@aws-sdk/util-user-agent-node': 3.535.0 + '@smithy/config-resolver': 2.2.0 + '@smithy/core': 1.4.2 + '@smithy/fetch-http-handler': 2.5.0 + '@smithy/hash-node': 2.2.0 + '@smithy/invalid-dependency': 2.2.0 + '@smithy/middleware-content-length': 2.2.0 + '@smithy/middleware-endpoint': 2.5.1 + '@smithy/middleware-retry': 2.3.1 + '@smithy/middleware-serde': 2.3.0 + '@smithy/middleware-stack': 2.2.0 + '@smithy/node-config-provider': 2.3.0 + '@smithy/node-http-handler': 2.5.0 + '@smithy/protocol-http': 3.3.0 + '@smithy/smithy-client': 2.5.1 + '@smithy/types': 2.12.0 + '@smithy/url-parser': 2.2.0 + '@smithy/util-base64': 2.3.0 + '@smithy/util-body-length-browser': 2.2.0 + '@smithy/util-body-length-node': 2.3.0 + '@smithy/util-defaults-mode-browser': 2.2.1 + '@smithy/util-defaults-mode-node': 2.3.1 + '@smithy/util-endpoints': 1.2.0 + '@smithy/util-middleware': 2.2.0 + '@smithy/util-retry': 2.2.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.2 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/client-sso-oidc@3.556.0(@aws-sdk/credential-provider-node@3.556.0): + resolution: {integrity: sha512-AXKd2TB6nNrksu+OfmHl8uI07PdgzOo4o8AxoRO8SHlwoMAGvcT9optDGVSYoVfgOKTymCoE7h8/UoUfPc11wQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@aws-sdk/credential-provider-node': ^3.556.0 + dependencies: + '@aws-crypto/sha256-browser': 3.0.0 + '@aws-crypto/sha256-js': 3.0.0 + '@aws-sdk/client-sts': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) + '@aws-sdk/core': 3.556.0 + '@aws-sdk/credential-provider-node': 3.556.0 + '@aws-sdk/middleware-host-header': 3.535.0 + '@aws-sdk/middleware-logger': 3.535.0 + '@aws-sdk/middleware-recursion-detection': 3.535.0 + '@aws-sdk/middleware-user-agent': 3.540.0 + '@aws-sdk/region-config-resolver': 3.535.0 + '@aws-sdk/types': 3.535.0 + '@aws-sdk/util-endpoints': 3.540.0 + '@aws-sdk/util-user-agent-browser': 3.535.0 + '@aws-sdk/util-user-agent-node': 3.535.0 + '@smithy/config-resolver': 2.2.0 + '@smithy/core': 1.4.2 + '@smithy/fetch-http-handler': 2.5.0 + '@smithy/hash-node': 2.2.0 + '@smithy/invalid-dependency': 2.2.0 + '@smithy/middleware-content-length': 2.2.0 + '@smithy/middleware-endpoint': 2.5.1 + '@smithy/middleware-retry': 2.3.1 + '@smithy/middleware-serde': 2.3.0 + '@smithy/middleware-stack': 2.2.0 + '@smithy/node-config-provider': 2.3.0 + '@smithy/node-http-handler': 2.5.0 + '@smithy/protocol-http': 3.3.0 + '@smithy/smithy-client': 2.5.1 + '@smithy/types': 2.12.0 + '@smithy/url-parser': 2.2.0 + '@smithy/util-base64': 2.3.0 + '@smithy/util-body-length-browser': 2.2.0 + '@smithy/util-body-length-node': 2.3.0 + '@smithy/util-defaults-mode-browser': 2.2.1 + '@smithy/util-defaults-mode-node': 2.3.1 + '@smithy/util-endpoints': 1.2.0 + '@smithy/util-middleware': 2.2.0 + '@smithy/util-retry': 2.2.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.2 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/client-sso@3.556.0: + resolution: {integrity: sha512-unXdWS7uvHqCcOyC1de+Fr8m3F2vMg2m24GPea0bg7rVGTYmiyn9mhUX11VCt+ozydrw+F50FQwL6OqoqPocmw==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-crypto/sha256-browser': 3.0.0 + '@aws-crypto/sha256-js': 3.0.0 + '@aws-sdk/core': 3.556.0 + '@aws-sdk/middleware-host-header': 3.535.0 + '@aws-sdk/middleware-logger': 3.535.0 + '@aws-sdk/middleware-recursion-detection': 3.535.0 + '@aws-sdk/middleware-user-agent': 3.540.0 + '@aws-sdk/region-config-resolver': 3.535.0 + '@aws-sdk/types': 3.535.0 + '@aws-sdk/util-endpoints': 3.540.0 + '@aws-sdk/util-user-agent-browser': 3.535.0 + '@aws-sdk/util-user-agent-node': 3.535.0 + '@smithy/config-resolver': 2.2.0 + '@smithy/core': 1.4.2 + '@smithy/fetch-http-handler': 2.5.0 + '@smithy/hash-node': 2.2.0 + '@smithy/invalid-dependency': 2.2.0 + '@smithy/middleware-content-length': 2.2.0 + '@smithy/middleware-endpoint': 2.5.1 + '@smithy/middleware-retry': 2.3.1 + '@smithy/middleware-serde': 2.3.0 + '@smithy/middleware-stack': 2.2.0 + '@smithy/node-config-provider': 2.3.0 + '@smithy/node-http-handler': 2.5.0 + '@smithy/protocol-http': 3.3.0 + '@smithy/smithy-client': 2.5.1 + '@smithy/types': 2.12.0 + '@smithy/url-parser': 2.2.0 + '@smithy/util-base64': 2.3.0 + '@smithy/util-body-length-browser': 2.2.0 + '@smithy/util-body-length-node': 2.3.0 + '@smithy/util-defaults-mode-browser': 2.2.1 + '@smithy/util-defaults-mode-node': 2.3.1 + '@smithy/util-endpoints': 1.2.0 + '@smithy/util-middleware': 2.2.0 + '@smithy/util-retry': 2.2.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.2 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/client-sts@3.556.0(@aws-sdk/credential-provider-node@3.556.0): + resolution: {integrity: sha512-TsK3js7Suh9xEmC886aY+bv0KdLLYtzrcmVt6sJ/W6EnDXYQhBuKYFhp03NrN2+vSvMGpqJwR62DyfKe1G0QzQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@aws-sdk/credential-provider-node': ^3.556.0 + dependencies: + '@aws-crypto/sha256-browser': 3.0.0 + '@aws-crypto/sha256-js': 3.0.0 + '@aws-sdk/core': 3.556.0 + '@aws-sdk/credential-provider-node': 3.556.0 + '@aws-sdk/middleware-host-header': 3.535.0 + '@aws-sdk/middleware-logger': 3.535.0 + '@aws-sdk/middleware-recursion-detection': 3.535.0 + '@aws-sdk/middleware-user-agent': 3.540.0 + '@aws-sdk/region-config-resolver': 3.535.0 + '@aws-sdk/types': 3.535.0 + '@aws-sdk/util-endpoints': 3.540.0 + '@aws-sdk/util-user-agent-browser': 3.535.0 + '@aws-sdk/util-user-agent-node': 3.535.0 + '@smithy/config-resolver': 2.2.0 + '@smithy/core': 1.4.2 + '@smithy/fetch-http-handler': 2.5.0 + '@smithy/hash-node': 2.2.0 + '@smithy/invalid-dependency': 2.2.0 + '@smithy/middleware-content-length': 2.2.0 + '@smithy/middleware-endpoint': 2.5.1 + '@smithy/middleware-retry': 2.3.1 + '@smithy/middleware-serde': 2.3.0 + '@smithy/middleware-stack': 2.2.0 + '@smithy/node-config-provider': 2.3.0 + '@smithy/node-http-handler': 2.5.0 + '@smithy/protocol-http': 3.3.0 + '@smithy/smithy-client': 2.5.1 + '@smithy/types': 2.12.0 + '@smithy/url-parser': 2.2.0 + '@smithy/util-base64': 2.3.0 + '@smithy/util-body-length-browser': 2.2.0 + '@smithy/util-body-length-node': 2.3.0 + '@smithy/util-defaults-mode-browser': 2.2.1 + '@smithy/util-defaults-mode-node': 2.3.1 + '@smithy/util-endpoints': 1.2.0 + '@smithy/util-middleware': 2.2.0 + '@smithy/util-retry': 2.2.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.2 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/core@3.556.0: + resolution: {integrity: sha512-vJaSaHw2kPQlo11j/Rzuz0gk1tEaKdz+2ser0f0qZ5vwFlANjt08m/frU17ctnVKC1s58bxpctO/1P894fHLrA==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/core': 1.4.2 + '@smithy/protocol-http': 3.3.0 + '@smithy/signature-v4': 2.3.0 + '@smithy/smithy-client': 2.5.1 + '@smithy/types': 2.12.0 + fast-xml-parser: 4.2.5 + tslib: 2.6.2 + dev: false + + /@aws-sdk/credential-provider-env@3.535.0: + resolution: {integrity: sha512-XppwO8c0GCGSAvdzyJOhbtktSEaShg14VJKg8mpMa1XcgqzmcqqHQjtDWbx5rZheY1VdpXZhpEzJkB6LpQejpA==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/types': 3.535.0 + '@smithy/property-provider': 2.2.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@aws-sdk/credential-provider-http@3.552.0: + resolution: {integrity: sha512-vsmu7Cz1i45pFEqzVb4JcFmAmVnWFNLsGheZc8SCptlqCO5voETrZZILHYIl4cjKkSDk3pblBOf0PhyjqWW6WQ==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/types': 3.535.0 + '@smithy/fetch-http-handler': 2.5.0 + '@smithy/node-http-handler': 2.5.0 + '@smithy/property-provider': 2.2.0 + '@smithy/protocol-http': 3.3.0 + '@smithy/smithy-client': 2.5.1 + '@smithy/types': 2.12.0 + '@smithy/util-stream': 2.2.0 + tslib: 2.6.2 + dev: false + + /@aws-sdk/credential-provider-ini@3.556.0(@aws-sdk/credential-provider-node@3.556.0): + resolution: {integrity: sha512-0Nz4ErOlXhe3muxWYMbPwRMgfKmVbBp36BAE2uv/z5wTbfdBkcgUwaflEvlKCLUTdHzuZsQk+BFS/gVyaUeOuA==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/client-sts': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) + '@aws-sdk/credential-provider-env': 3.535.0 + '@aws-sdk/credential-provider-process': 3.535.0 + '@aws-sdk/credential-provider-sso': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) + '@aws-sdk/credential-provider-web-identity': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) + '@aws-sdk/types': 3.535.0 + '@smithy/credential-provider-imds': 2.3.0 + '@smithy/property-provider': 2.2.0 + '@smithy/shared-ini-file-loader': 2.4.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@aws-sdk/credential-provider-node' + - aws-crt + dev: false + + /@aws-sdk/credential-provider-node@3.556.0: + resolution: {integrity: sha512-s1xVtKjyGc60O8qcNIzS1X3H+pWEwEfZ7TgNznVDNyuXvLrlNWiAcigPWGl2aAkc8tGcsSG0Qpyw2KYC939LFg==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/credential-provider-env': 3.535.0 + '@aws-sdk/credential-provider-http': 3.552.0 + '@aws-sdk/credential-provider-ini': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) + '@aws-sdk/credential-provider-process': 3.535.0 + '@aws-sdk/credential-provider-sso': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) + '@aws-sdk/credential-provider-web-identity': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) + '@aws-sdk/types': 3.535.0 + '@smithy/credential-provider-imds': 2.3.0 + '@smithy/property-provider': 2.2.0 + '@smithy/shared-ini-file-loader': 2.4.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/credential-provider-process@3.535.0: + resolution: {integrity: sha512-9O1OaprGCnlb/kYl8RwmH7Mlg8JREZctB8r9sa1KhSsWFq/SWO0AuJTyowxD7zL5PkeS4eTvzFFHWCa3OO5epA==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/types': 3.535.0 + '@smithy/property-provider': 2.2.0 + '@smithy/shared-ini-file-loader': 2.4.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@aws-sdk/credential-provider-sso@3.556.0(@aws-sdk/credential-provider-node@3.556.0): + resolution: {integrity: sha512-ETuBgcnpfxqadEAqhQFWpKoV1C/NAgvs5CbBc5EJbelJ8f4prTdErIHjrRtVT8c02MXj92QwczsiNYd5IoOqyw==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/client-sso': 3.556.0 + '@aws-sdk/token-providers': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) + '@aws-sdk/types': 3.535.0 + '@smithy/property-provider': 2.2.0 + '@smithy/shared-ini-file-loader': 2.4.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@aws-sdk/credential-provider-node' + - aws-crt + dev: false + + /@aws-sdk/credential-provider-web-identity@3.556.0(@aws-sdk/credential-provider-node@3.556.0): + resolution: {integrity: sha512-R/YAL8Uh8i+dzVjzMnbcWLIGeeRi2mioHVGnVF+minmaIkCiQMZg2HPrdlKm49El+RljT28Nl5YHRuiqzEIwMA==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/client-sts': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) + '@aws-sdk/types': 3.535.0 + '@smithy/property-provider': 2.2.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@aws-sdk/credential-provider-node' + - aws-crt + dev: false + + /@aws-sdk/middleware-host-header@3.535.0: + resolution: {integrity: sha512-0h6TWjBWtDaYwHMQJI9ulafeS4lLaw1vIxRjbpH0svFRt6Eve+Sy8NlVhECfTU2hNz/fLubvrUxsXoThaLBIew==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/types': 3.535.0 + '@smithy/protocol-http': 3.3.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@aws-sdk/middleware-logger@3.535.0: + resolution: {integrity: sha512-huNHpONOrEDrdRTvSQr1cJiRMNf0S52NDXtaPzdxiubTkP+vni2MohmZANMOai/qT0olmEVX01LhZ0ZAOgmg6A==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/types': 3.535.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@aws-sdk/middleware-recursion-detection@3.535.0: + resolution: {integrity: sha512-am2qgGs+gwqmR4wHLWpzlZ8PWhm4ktj5bYSgDrsOfjhdBlWNxvPoID9/pDAz5RWL48+oH7I6SQzMqxXsFDikrw==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/types': 3.535.0 + '@smithy/protocol-http': 3.3.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@aws-sdk/middleware-user-agent@3.540.0: + resolution: {integrity: sha512-8Rd6wPeXDnOYzWj1XCmOKcx/Q87L0K1/EHqOBocGjLVbN3gmRxBvpmR1pRTjf7IsWfnnzN5btqtcAkfDPYQUMQ==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/types': 3.535.0 + '@aws-sdk/util-endpoints': 3.540.0 + '@smithy/protocol-http': 3.3.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@aws-sdk/region-config-resolver@3.535.0: + resolution: {integrity: sha512-IXOznDiaItBjsQy4Fil0kzX/J3HxIOknEphqHbOfUf+LpA5ugcsxuQQONrbEQusCBnfJyymrldBvBhFmtlU9Wg==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/types': 3.535.0 + '@smithy/node-config-provider': 2.3.0 + '@smithy/types': 2.12.0 + '@smithy/util-config-provider': 2.3.0 + '@smithy/util-middleware': 2.2.0 + tslib: 2.6.2 + dev: false + + /@aws-sdk/token-providers@3.556.0(@aws-sdk/credential-provider-node@3.556.0): + resolution: {integrity: sha512-tvIiugNF0/+2wfuImMrpKjXMx4nCnFWQjQvouObny+wrif/PGqqQYrybwxPJDvzbd965bu1I+QuSv85/ug7xsg==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/client-sso-oidc': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) + '@aws-sdk/types': 3.535.0 + '@smithy/property-provider': 2.2.0 + '@smithy/shared-ini-file-loader': 2.4.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@aws-sdk/credential-provider-node' + - aws-crt + dev: false + + /@aws-sdk/types@3.535.0: + resolution: {integrity: sha512-aY4MYfduNj+sRR37U7XxYR8wemfbKP6lx00ze2M2uubn7mZotuVrWYAafbMSXrdEMSToE5JDhr28vArSOoLcSg==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@aws-sdk/util-endpoints@3.540.0: + resolution: {integrity: sha512-1kMyQFAWx6f8alaI6UT65/5YW/7pDWAKAdNwL6vuJLea03KrZRX3PMoONOSJpAS5m3Ot7HlWZvf3wZDNTLELZw==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/types': 3.535.0 + '@smithy/types': 2.12.0 + '@smithy/util-endpoints': 1.2.0 + tslib: 2.6.2 + dev: false + + /@aws-sdk/util-locate-window@3.535.0: + resolution: {integrity: sha512-PHJ3SL6d2jpcgbqdgiPxkXpu7Drc2PYViwxSIqvvMKhDwzSB1W3mMvtpzwKM4IE7zLFodZo0GKjJ9AsoXndXhA==} + engines: {node: '>=14.0.0'} + dependencies: + tslib: 2.6.2 + dev: false + + /@aws-sdk/util-user-agent-browser@3.535.0: + resolution: {integrity: sha512-RWMcF/xV5n+nhaA/Ff5P3yNP3Kur/I+VNZngog4TEs92oB/nwOdAg/2JL8bVAhUbMrjTjpwm7PItziYFQoqyig==} + dependencies: + '@aws-sdk/types': 3.535.0 + '@smithy/types': 2.12.0 + bowser: 2.11.0 + tslib: 2.6.2 + dev: false + + /@aws-sdk/util-user-agent-node@3.535.0: + resolution: {integrity: sha512-dRek0zUuIT25wOWJlsRm97nTkUlh1NDcLsQZIN2Y8KxhwoXXWtJs5vaDPT+qAg+OpcNj80i1zLR/CirqlFg/TQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + dependencies: + '@aws-sdk/types': 3.535.0 + '@smithy/node-config-provider': 2.3.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@aws-sdk/util-utf8-browser@3.259.0: + resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} + dependencies: + tslib: 2.6.2 + dev: false + /@babel/code-frame@7.21.4: resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==} engines: {node: '>=6.9.0'} @@ -3434,6 +3912,369 @@ packages: '@sinonjs/commons': 2.0.0 dev: true + /@smithy/abort-controller@2.2.0: + resolution: {integrity: sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/config-resolver@2.2.0: + resolution: {integrity: sha512-fsiMgd8toyUba6n1WRmr+qACzXltpdDkPTAaDqc8QqPBUzO+/JKwL6bUBseHVi8tu9l+3JOK+tSf7cay+4B3LA==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/node-config-provider': 2.3.0 + '@smithy/types': 2.12.0 + '@smithy/util-config-provider': 2.3.0 + '@smithy/util-middleware': 2.2.0 + tslib: 2.6.2 + dev: false + + /@smithy/core@1.4.2: + resolution: {integrity: sha512-2fek3I0KZHWJlRLvRTqxTEri+qV0GRHrJIoLFuBMZB4EMg4WgeBGfF0X6abnrNYpq55KJ6R4D6x4f0vLnhzinA==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/middleware-endpoint': 2.5.1 + '@smithy/middleware-retry': 2.3.1 + '@smithy/middleware-serde': 2.3.0 + '@smithy/protocol-http': 3.3.0 + '@smithy/smithy-client': 2.5.1 + '@smithy/types': 2.12.0 + '@smithy/util-middleware': 2.2.0 + tslib: 2.6.2 + dev: false + + /@smithy/credential-provider-imds@2.3.0: + resolution: {integrity: sha512-BWB9mIukO1wjEOo1Ojgl6LrG4avcaC7T/ZP6ptmAaW4xluhSIPZhY+/PI5YKzlk+jsm+4sQZB45Bt1OfMeQa3w==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/node-config-provider': 2.3.0 + '@smithy/property-provider': 2.2.0 + '@smithy/types': 2.12.0 + '@smithy/url-parser': 2.2.0 + tslib: 2.6.2 + dev: false + + /@smithy/fetch-http-handler@2.5.0: + resolution: {integrity: sha512-BOWEBeppWhLn/no/JxUL/ghTfANTjT7kg3Ww2rPqTUY9R4yHPXxJ9JhMe3Z03LN3aPwiwlpDIUcVw1xDyHqEhw==} + dependencies: + '@smithy/protocol-http': 3.3.0 + '@smithy/querystring-builder': 2.2.0 + '@smithy/types': 2.12.0 + '@smithy/util-base64': 2.3.0 + tslib: 2.6.2 + dev: false + + /@smithy/hash-node@2.2.0: + resolution: {integrity: sha512-zLWaC/5aWpMrHKpoDF6nqpNtBhlAYKF/7+9yMN7GpdR8CzohnWfGtMznPybnwSS8saaXBMxIGwJqR4HmRp6b3g==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/types': 2.12.0 + '@smithy/util-buffer-from': 2.2.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.2 + dev: false + + /@smithy/invalid-dependency@2.2.0: + resolution: {integrity: sha512-nEDASdbKFKPXN2O6lOlTgrEEOO9NHIeO+HVvZnkqc8h5U9g3BIhWsvzFo+UcUbliMHvKNPD/zVxDrkP1Sbgp8Q==} + dependencies: + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/is-array-buffer@2.2.0: + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + dependencies: + tslib: 2.6.2 + dev: false + + /@smithy/middleware-content-length@2.2.0: + resolution: {integrity: sha512-5bl2LG1Ah/7E5cMSC+q+h3IpVHMeOkG0yLRyQT1p2aMJkSrZG7RlXHPuAgb7EyaFeidKEnnd/fNaLLaKlHGzDQ==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/protocol-http': 3.3.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/middleware-endpoint@2.5.1: + resolution: {integrity: sha512-1/8kFp6Fl4OsSIVTWHnNjLnTL8IqpIb/D3sTSczrKFnrE9VMNWxnrRKNvpUHOJ6zpGD5f62TPm7+17ilTJpiCQ==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/middleware-serde': 2.3.0 + '@smithy/node-config-provider': 2.3.0 + '@smithy/shared-ini-file-loader': 2.4.0 + '@smithy/types': 2.12.0 + '@smithy/url-parser': 2.2.0 + '@smithy/util-middleware': 2.2.0 + tslib: 2.6.2 + dev: false + + /@smithy/middleware-retry@2.3.1: + resolution: {integrity: sha512-P2bGufFpFdYcWvqpyqqmalRtwFUNUA8vHjJR5iGqbfR6mp65qKOLcUd6lTr4S9Gn/enynSrSf3p3FVgVAf6bXA==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/node-config-provider': 2.3.0 + '@smithy/protocol-http': 3.3.0 + '@smithy/service-error-classification': 2.1.5 + '@smithy/smithy-client': 2.5.1 + '@smithy/types': 2.12.0 + '@smithy/util-middleware': 2.2.0 + '@smithy/util-retry': 2.2.0 + tslib: 2.6.2 + uuid: 9.0.1 + dev: false + + /@smithy/middleware-serde@2.3.0: + resolution: {integrity: sha512-sIADe7ojwqTyvEQBe1nc/GXB9wdHhi9UwyX0lTyttmUWDJLP655ZYE1WngnNyXREme8I27KCaUhyhZWRXL0q7Q==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/middleware-stack@2.2.0: + resolution: {integrity: sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/node-config-provider@2.3.0: + resolution: {integrity: sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/property-provider': 2.2.0 + '@smithy/shared-ini-file-loader': 2.4.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/node-http-handler@2.5.0: + resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/abort-controller': 2.2.0 + '@smithy/protocol-http': 3.3.0 + '@smithy/querystring-builder': 2.2.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/property-provider@2.2.0: + resolution: {integrity: sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/protocol-http@3.3.0: + resolution: {integrity: sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/querystring-builder@2.2.0: + resolution: {integrity: sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/types': 2.12.0 + '@smithy/util-uri-escape': 2.2.0 + tslib: 2.6.2 + dev: false + + /@smithy/querystring-parser@2.2.0: + resolution: {integrity: sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/service-error-classification@2.1.5: + resolution: {integrity: sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/types': 2.12.0 + dev: false + + /@smithy/shared-ini-file-loader@2.4.0: + resolution: {integrity: sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/signature-v4@2.3.0: + resolution: {integrity: sha512-ui/NlpILU+6HAQBfJX8BBsDXuKSNrjTSuOYArRblcrErwKFutjrCNb/OExfVRyj9+26F9J+ZmfWT+fKWuDrH3Q==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/is-array-buffer': 2.2.0 + '@smithy/types': 2.12.0 + '@smithy/util-hex-encoding': 2.2.0 + '@smithy/util-middleware': 2.2.0 + '@smithy/util-uri-escape': 2.2.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.2 + dev: false + + /@smithy/smithy-client@2.5.1: + resolution: {integrity: sha512-jrbSQrYCho0yDaaf92qWgd+7nAeap5LtHTI51KXqmpIFCceKU3K9+vIVTUH72bOJngBMqa4kyu1VJhRcSrk/CQ==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/middleware-endpoint': 2.5.1 + '@smithy/middleware-stack': 2.2.0 + '@smithy/protocol-http': 3.3.0 + '@smithy/types': 2.12.0 + '@smithy/util-stream': 2.2.0 + tslib: 2.6.2 + dev: false + + /@smithy/types@2.12.0: + resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==} + engines: {node: '>=14.0.0'} + dependencies: + tslib: 2.6.2 + dev: false + + /@smithy/url-parser@2.2.0: + resolution: {integrity: sha512-hoA4zm61q1mNTpksiSWp2nEl1dt3j726HdRhiNgVJQMj7mLp7dprtF57mOB6JvEk/x9d2bsuL5hlqZbBuHQylQ==} + dependencies: + '@smithy/querystring-parser': 2.2.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/util-base64@2.3.0: + resolution: {integrity: sha512-s3+eVwNeJuXUwuMbusncZNViuhv2LjVJ1nMwTqSA0XAC7gjKhqqxRdJPhR8+YrkoZ9IiIbFk/yK6ACe/xlF+hw==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/util-buffer-from': 2.2.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.2 + dev: false + + /@smithy/util-body-length-browser@2.2.0: + resolution: {integrity: sha512-dtpw9uQP7W+n3vOtx0CfBD5EWd7EPdIdsQnWTDoFf77e3VUf05uA7R7TGipIo8e4WL2kuPdnsr3hMQn9ziYj5w==} + dependencies: + tslib: 2.6.2 + dev: false + + /@smithy/util-body-length-node@2.3.0: + resolution: {integrity: sha512-ITWT1Wqjubf2CJthb0BuT9+bpzBfXeMokH/AAa5EJQgbv9aPMVfnM76iFIZVFf50hYXGbtiV71BHAthNWd6+dw==} + engines: {node: '>=14.0.0'} + dependencies: + tslib: 2.6.2 + dev: false + + /@smithy/util-buffer-from@2.2.0: + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.6.2 + dev: false + + /@smithy/util-config-provider@2.3.0: + resolution: {integrity: sha512-HZkzrRcuFN1k70RLqlNK4FnPXKOpkik1+4JaBoHNJn+RnJGYqaa3c5/+XtLOXhlKzlRgNvyaLieHTW2VwGN0VQ==} + engines: {node: '>=14.0.0'} + dependencies: + tslib: 2.6.2 + dev: false + + /@smithy/util-defaults-mode-browser@2.2.1: + resolution: {integrity: sha512-RtKW+8j8skk17SYowucwRUjeh4mCtnm5odCL0Lm2NtHQBsYKrNW0od9Rhopu9wF1gHMfHeWF7i90NwBz/U22Kw==} + engines: {node: '>= 10.0.0'} + dependencies: + '@smithy/property-provider': 2.2.0 + '@smithy/smithy-client': 2.5.1 + '@smithy/types': 2.12.0 + bowser: 2.11.0 + tslib: 2.6.2 + dev: false + + /@smithy/util-defaults-mode-node@2.3.1: + resolution: {integrity: sha512-vkMXHQ0BcLFysBMWgSBLSk3+leMpFSyyFj8zQtv5ZyUBx8/owVh1/pPEkzmW/DR/Gy/5c8vjLDD9gZjXNKbrpA==} + engines: {node: '>= 10.0.0'} + dependencies: + '@smithy/config-resolver': 2.2.0 + '@smithy/credential-provider-imds': 2.3.0 + '@smithy/node-config-provider': 2.3.0 + '@smithy/property-provider': 2.2.0 + '@smithy/smithy-client': 2.5.1 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/util-endpoints@1.2.0: + resolution: {integrity: sha512-BuDHv8zRjsE5zXd3PxFXFknzBG3owCpjq8G3FcsXW3CykYXuEqM3nTSsmLzw5q+T12ZYuDlVUZKBdpNbhVtlrQ==} + engines: {node: '>= 14.0.0'} + dependencies: + '@smithy/node-config-provider': 2.3.0 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/util-hex-encoding@2.2.0: + resolution: {integrity: sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ==} + engines: {node: '>=14.0.0'} + dependencies: + tslib: 2.6.2 + dev: false + + /@smithy/util-middleware@2.2.0: + resolution: {integrity: sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/util-retry@2.2.0: + resolution: {integrity: sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g==} + engines: {node: '>= 14.0.0'} + dependencies: + '@smithy/service-error-classification': 2.1.5 + '@smithy/types': 2.12.0 + tslib: 2.6.2 + dev: false + + /@smithy/util-stream@2.2.0: + resolution: {integrity: sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/fetch-http-handler': 2.5.0 + '@smithy/node-http-handler': 2.5.0 + '@smithy/types': 2.12.0 + '@smithy/util-base64': 2.3.0 + '@smithy/util-buffer-from': 2.2.0 + '@smithy/util-hex-encoding': 2.2.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.2 + dev: false + + /@smithy/util-uri-escape@2.2.0: + resolution: {integrity: sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==} + engines: {node: '>=14.0.0'} + dependencies: + tslib: 2.6.2 + dev: false + + /@smithy/util-utf8@2.3.0: + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.6.2 + dev: false + /@solidity-parser/parser@0.14.5: resolution: {integrity: sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==} dependencies: @@ -4610,6 +5451,10 @@ packages: resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} dev: false + /bowser@2.11.0: + resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + dev: false + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -6294,6 +7139,13 @@ packages: resolution: {integrity: sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==} dev: false + /fast-xml-parser@4.2.5: + resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==} + hasBin: true + dependencies: + strnum: 1.0.5 + dev: false + /fastify-plugin@4.5.1: resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} dev: false @@ -9514,11 +10366,6 @@ packages: pump: 3.0.0 dev: false - /punycode@2.1.1: - resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} - engines: {node: '>=6'} - dev: true - /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} @@ -9895,7 +10742,7 @@ packages: /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: - tslib: 2.5.0 + tslib: 2.6.2 dev: true /safe-buffer@5.1.2: @@ -10443,6 +11290,10 @@ packages: acorn: 8.11.3 dev: true + /strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + dev: false + /sucrase@3.32.0: resolution: {integrity: sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==} engines: {node: '>=8'} @@ -10711,7 +11562,7 @@ packages: engines: {node: '>=6'} dependencies: psl: 1.9.0 - punycode: 2.1.1 + punycode: 2.3.0 universalify: 0.2.0 url-parse: 1.5.10 dev: true @@ -10775,7 +11626,7 @@ packages: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true - /ts-jest@29.0.5(@babel/core@7.24.4)(esbuild@0.17.17)(jest@29.5.0)(typescript@5.4.5): + /ts-jest@29.0.5(@babel/core@7.24.4)(esbuild@0.17.17)(jest@29.5.0)(typescript@5.4.2): resolution: {integrity: sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -10806,7 +11657,7 @@ packages: lodash.memoize: 4.1.2 make-error: 1.3.6 semver: 7.5.0 - typescript: 5.4.5 + typescript: 5.4.2 yargs-parser: 21.1.1 dev: true @@ -10841,6 +11692,10 @@ packages: /tslib@2.5.0: resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} + dev: false + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} /tsscmp@1.0.6: resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} @@ -10884,42 +11739,6 @@ packages: - ts-node dev: true - /tsup@6.7.0(typescript@5.4.5): - resolution: {integrity: sha512-L3o8hGkaHnu5TdJns+mCqFsDBo83bJ44rlK7e6VdanIvpea4ArPcU3swWGsLVbXak1PqQx/V+SSmFPujBK+zEQ==} - engines: {node: '>=14.18'} - hasBin: true - peerDependencies: - '@swc/core': ^1 - postcss: ^8.4.12 - typescript: '>=4.1.0' - peerDependenciesMeta: - '@swc/core': - optional: true - postcss: - optional: true - typescript: - optional: true - dependencies: - bundle-require: 4.0.1(esbuild@0.17.17) - cac: 6.7.14 - chokidar: 3.5.3 - debug: 4.3.4 - esbuild: 0.17.17 - execa: 5.1.1 - globby: 11.1.0 - joycon: 3.1.1 - postcss-load-config: 3.1.4(postcss@8.4.23) - resolve-from: 5.0.0 - rollup: 3.21.8 - source-map: 0.8.0-beta.0 - sucrase: 3.32.0 - tree-kill: 1.2.2 - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - - ts-node - dev: true - /tsx@3.12.6: resolution: {integrity: sha512-q93WgS3lBdHlPgS0h1i+87Pt6n9K/qULIMNYZo07nSeu2z5QE2CellcAZfofVXBo2tQg9av2ZcRMQ2S2i5oadQ==} hasBin: true @@ -10942,7 +11761,7 @@ packages: smartwrap: 2.0.2 strip-ansi: 6.0.1 wcwidth: 1.0.1 - yargs: 17.7.1 + yargs: 17.7.2 dev: true /tunnel-agent@0.6.0: @@ -11081,12 +11900,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - /typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} - engines: {node: '>=14.17'} - hasBin: true - dev: true - /ufo@1.3.2: resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==} dev: true @@ -11188,6 +12001,11 @@ packages: hasBin: true dev: false + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + dev: false + /v8-to-istanbul@9.1.0: resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} engines: {node: '>=10.12.0'} @@ -11685,6 +12503,7 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 + dev: false /yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} From bbf9819ba8d44e4a18ac0788ee30965dcd487f1a Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Thu, 18 Apr 2024 16:47:52 +0100 Subject: [PATCH 02/53] rename file --- packages/cli/scripts/{aws.ts => kms.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/cli/scripts/{aws.ts => kms.ts} (100%) diff --git a/packages/cli/scripts/aws.ts b/packages/cli/scripts/kms.ts similarity index 100% rename from packages/cli/scripts/aws.ts rename to packages/cli/scripts/kms.ts From 5a1f69745fd9dde76920c179db462c5da619edaa Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 11:28:03 +0100 Subject: [PATCH 03/53] feat: it works --- packages/cli/package.json | 7 + packages/cli/scripts/eth.ts | 83 ++++++++++ packages/cli/scripts/kms-signer.ts | 53 +++++++ packages/cli/scripts/kms.ts | 51 ++---- packages/cli/scripts/types.ts | 21 +++ pnpm-lock.yaml | 242 +++++++++++++++++++++++++++++ 6 files changed, 422 insertions(+), 35 deletions(-) create mode 100644 packages/cli/scripts/eth.ts create mode 100644 packages/cli/scripts/kms-signer.ts create mode 100644 packages/cli/scripts/types.ts diff --git a/packages/cli/package.json b/packages/cli/package.json index 42a65d2132..904ec2b435 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -32,7 +32,11 @@ "dependencies": { "@aws-sdk/client-kms": "*", "@ethersproject/abi": "^5.7.0", + "@ethersproject/bytes": "^5.5.4", "@ethersproject/providers": "^5.7.2", + "@ethersproject/abstract-signer": "^5.5.4", + "@ethersproject/hash": "^5.5.4", + "@ethersproject/keccak256": "^5.5.4", "@improbable-eng/grpc-web": "^0.15.0", "@improbable-eng/grpc-web-node-http-transport": "^0.15.0", "@latticexyz/abi-ts": "workspace:*", @@ -46,11 +50,14 @@ "@latticexyz/utils": "workspace:*", "@latticexyz/world": "workspace:*", "@latticexyz/world-modules": "workspace:*", + "asn1.js": "^5.4.1", + "bn.js": "^4.12.0", "chalk": "^5.0.1", "chokidar": "^3.5.3", "debug": "^4.3.4", "dotenv": "^16.0.3", "ejs": "^3.1.8", + "ethereumjs-util": "^7.1.5", "ethers": "^5.7.2", "execa": "^7.0.0", "glob": "^8.0.3", diff --git a/packages/cli/scripts/eth.ts b/packages/cli/scripts/eth.ts new file mode 100644 index 0000000000..abbec48ce2 --- /dev/null +++ b/packages/cli/scripts/eth.ts @@ -0,0 +1,83 @@ +import asn1 from "asn1.js"; +import { BigNumber, utils } from "ethers"; + +import { sign } from "./kms"; +import { CreateSignatureParams, SignParams } from "./types"; +import { BytesLike, SignatureLike } from "@ethersproject/bytes"; + +const EcdsaSigAsnParse = asn1.define("EcdsaSig", function (this: any) { + this.seq().obj(this.key("r").int(), this.key("s").int()); +}); + +const EcdsaPubKey = asn1.define("EcdsaPubKey", function (this: any) { + this.seq().obj(this.key("algo").seq().obj(this.key("a").objid(), this.key("b").objid()), this.key("pubKey").bitstr()); +}); + +const getRS = async (signParams: SignParams) => { + const signature = await sign(signParams); + + if (signature.Signature == undefined) { + throw new Error("Signature is undefined."); + } + + const decoded = EcdsaSigAsnParse.decode(Buffer.from(signature.Signature), "der"); + + const r = BigNumber.from(`0x${decoded.r.toString("hex")}`); + let s = BigNumber.from(`0x${decoded.s.toString("hex")}`); + + const secp256k1N = BigNumber.from("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); + const secp256k1halfN = secp256k1N.div(BigNumber.from(2)); + + if (s.gt(secp256k1halfN)) { + s = secp256k1N.sub(s); + } + + return { + r: r.toHexString(), + s: s.toHexString(), + }; +}; + +const getRecoveryParam = (msg: string, r: string, s: string, expectedEthAddr: string) => { + const formatted = msg; + let recoveryParam: number; + for (recoveryParam = 0; recoveryParam <= 1; recoveryParam++) { + const address = utils + .recoverAddress(formatted, { + r, + s, + recoveryParam, + }) + .toLowerCase(); + if (address !== expectedEthAddr.toLowerCase()) { + continue; + } + return recoveryParam; + } + throw new Error("Failed to calculate recovery param"); +}; + +export const getEthAddressFromPublicKey = (publicKey: Uint8Array): string => { + const res = EcdsaPubKey.decode(Buffer.from(publicKey)); + + const pubKeyBuffer: Buffer = res.pubKey.data; + + const address = utils.computeAddress(pubKeyBuffer); + return address; +}; + +export const createSignature = async ({ + keyId, + message, + address, + kmsInstance, +}: CreateSignatureParams): Promise> => { + const { r, s } = await getRS({ keyId, message, kmsInstance }); + const recoveryParam = getRecoveryParam(message, r, s, address); + + return { + r: r, + s: s, + v: recoveryParam, + }; +}; diff --git a/packages/cli/scripts/kms-signer.ts b/packages/cli/scripts/kms-signer.ts new file mode 100644 index 0000000000..2136000248 --- /dev/null +++ b/packages/cli/scripts/kms-signer.ts @@ -0,0 +1,53 @@ +import { utils } from "ethers"; +import { KMSClient } from "@aws-sdk/client-kms"; +import { getEthAddressFromKMS } from "./kms"; +import { Hex, hashMessage, hexToSignature, recoverMessageAddress, signatureToHex, verifyMessage } from "viem"; +import { createSignature } from "./eth"; + +const keyId = "PLACEHOLDER"; + +const kmsInstance = new KMSClient({ + region: "eu-west-2", + credentials: { + accessKeyId: "PLACEHOLDER", + secretAccessKey: "PLACEHOLDER", + }, +}); + +async function signMessage(message: string): Promise { + const hash = hashMessage(message); + + const address = await getEthAddressFromKMS({ kmsInstance, keyId }); + + const ethersStructuredSignature = await createSignature({ + kmsInstance: kmsInstance, + keyId: keyId, + message: hash, + address, + }); + + const ethersHexSignature = utils.joinSignature(ethersStructuredSignature) as Hex; + + const structuredSignature = hexToSignature(ethersHexSignature); + + return signatureToHex(structuredSignature); +} + +const message = "hello"; + +const signature = await signMessage(message); + +const recoveredAddress = await recoverMessageAddress({ + message, + signature, +}); + +const valid = await verifyMessage({ + address: recoveredAddress, + message, + signature, +}); + +console.log(signature); +console.log(recoveredAddress); +console.log(valid); diff --git a/packages/cli/scripts/kms.ts b/packages/cli/scripts/kms.ts index cd59f32b4d..f24a03c938 100644 --- a/packages/cli/scripts/kms.ts +++ b/packages/cli/scripts/kms.ts @@ -1,15 +1,23 @@ +import { SignParams, GetEthAddressFromKMSparams, GetPublicKeyParams } from "./types"; +import { getEthAddressFromPublicKey } from "./eth"; import { utils } from "ethers"; -import { KMSClient, SignCommand, SignCommandInput, SignCommandOutput } from "@aws-sdk/client-kms"; -import { toAccount } from "viem/accounts"; -import { keccak256, toHex } from "viem"; +import { GetPublicKeyCommand, GetPublicKeyCommandOutput, SignCommand, SignCommandOutput } from "@aws-sdk/client-kms"; -type SignParams = { - keyId: SignCommandInput["KeyId"]; - message: string; - kmsInstance: KMSClient; +export const getPublicKey = (getPublicKeyParams: GetPublicKeyParams): Promise => { + const { keyId, kmsInstance } = getPublicKeyParams; + const command = new GetPublicKeyCommand({ KeyId: keyId }); + + return kmsInstance.send(command); +}; + +export const getEthAddressFromKMS = async (getEthAddressFromKMSparams: GetEthAddressFromKMSparams): Promise => { + const { keyId, kmsInstance } = getEthAddressFromKMSparams; + const KMSKey = await getPublicKey({ keyId, kmsInstance }); + + return getEthAddressFromPublicKey(KMSKey.PublicKey); }; -const sign = async (signParams: SignParams): Promise => { +export const sign = async (signParams: SignParams): Promise => { const { keyId, message, kmsInstance } = signParams; const formatted = Buffer.from(utils.arrayify(message)); @@ -22,30 +30,3 @@ const sign = async (signParams: SignParams): Promise => { return kmsInstance.send(command); }; - -const keyId = "PLACEHOLDER"; - -const kmsInstance = new KMSClient({ - region: "eu-west-2", - credentials: { - accessKeyId: "PLACEHOLDER", - secretAccessKey: "PLACEHOLDER", - }, -}); - -const account = toAccount({ - address: "0x0000000000000000000000000000000000000000", - async signMessage({ message }) { - if (typeof message === "string") { - const signature = await sign({ keyId, message: keccak256(toHex(message)), kmsInstance }); - - if (signature.Signature == undefined) { - throw new Error("Signature is undefined."); - } - - return signature.Signature; - } - }, -}); - -console.log(await account.signMessage({ message: "hello" })); diff --git a/packages/cli/scripts/types.ts b/packages/cli/scripts/types.ts new file mode 100644 index 0000000000..e8ba8b383e --- /dev/null +++ b/packages/cli/scripts/types.ts @@ -0,0 +1,21 @@ +import { KMSClient, SignCommandInput } from "@aws-sdk/client-kms"; + +export type SignParams = { + keyId: SignCommandInput["KeyId"]; + message: string; + kmsInstance: KMSClient; +}; + +export type GetEthAddressFromKMSparams = { + keyId: SignCommandInput["KeyId"]; + kmsInstance: KMSClient; +}; + +export type GetPublicKeyParams = { + keyId: SignCommandInput["KeyId"]; + kmsInstance: KMSClient; +}; + +export type CreateSignatureParams = SignParams & { + address: string; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0004cde67..9712a19a1f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -136,6 +136,18 @@ importers: '@ethersproject/abi': specifier: ^5.7.0 version: 5.7.0 + '@ethersproject/abstract-signer': + specifier: ^5.5.4 + version: 5.7.0 + '@ethersproject/bytes': + specifier: ^5.5.4 + version: 5.7.0 + '@ethersproject/hash': + specifier: ^5.5.4 + version: 5.7.0 + '@ethersproject/keccak256': + specifier: ^5.5.4 + version: 5.7.0 '@ethersproject/providers': specifier: ^5.7.2 version: 5.7.2 @@ -178,6 +190,12 @@ importers: '@latticexyz/world-modules': specifier: workspace:* version: link:../world-modules + asn1.js: + specifier: ^5.4.1 + version: 5.4.1 + bn.js: + specifier: ^4.12.0 + version: 4.12.0 chalk: specifier: ^5.0.1 version: 5.2.0 @@ -193,6 +211,9 @@ importers: ejs: specifier: ^3.1.8 version: 3.1.8 + ethereumjs-util: + specifier: ^7.1.5 + version: 7.1.5 ethers: specifier: ^5.7.2 version: 5.7.2 @@ -4369,6 +4390,12 @@ packages: dependencies: '@types/node': 18.15.11 + /@types/bn.js@5.1.5: + resolution: {integrity: sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==} + dependencies: + '@types/node': 18.15.11 + dev: false + /@types/body-parser@1.19.5: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: @@ -4578,6 +4605,12 @@ packages: resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} dev: true + /@types/pbkdf2@3.1.2: + resolution: {integrity: sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==} + dependencies: + '@types/node': 18.15.11 + dev: false + /@types/prettier@2.7.2: resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==} dev: true @@ -4616,6 +4649,12 @@ packages: resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} dev: true + /@types/secp256k1@4.0.6: + resolution: {integrity: sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==} + dependencies: + '@types/node': 18.15.11 + dev: false + /@types/semver@6.2.3: resolution: {integrity: sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==} dev: true @@ -5260,6 +5299,15 @@ packages: objnest: 5.1.1 dev: false + /asn1.js@5.4.1: + resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} + dependencies: + bn.js: 4.12.0 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + safer-buffer: 2.1.2 + dev: false + /assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: true @@ -5402,6 +5450,12 @@ packages: /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + /base-x@3.0.9: + resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: false @@ -5443,6 +5497,10 @@ packages: readable-stream: 3.6.0 dev: false + /blakejs@1.2.1: + resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} + dev: false + /bn.js@4.12.0: resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} dev: false @@ -5486,6 +5544,17 @@ packages: resolution: {integrity: sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg==} dev: false + /browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + dependencies: + buffer-xor: 1.0.3 + cipher-base: 1.0.4 + create-hash: 1.2.0 + evp_bytestokey: 1.0.3 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: false + /browserslist@4.23.0: resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -5504,6 +5573,20 @@ packages: fast-json-stable-stringify: 2.1.0 dev: true + /bs58@4.0.1: + resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} + dependencies: + base-x: 3.0.9 + dev: false + + /bs58check@2.1.2: + resolution: {integrity: sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==} + dependencies: + bs58: 4.0.1 + create-hash: 1.2.0 + safe-buffer: 5.2.1 + dev: false + /bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} dependencies: @@ -5513,6 +5596,10 @@ packages: /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + /buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + dev: false + /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} dependencies: @@ -5757,6 +5844,13 @@ packages: engines: {node: '>=8'} dev: true + /cipher-base@1.0.4: + resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: false + /cjs-module-lexer@1.2.2: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} dev: true @@ -5990,6 +6084,27 @@ packages: yaml: 1.10.2 dev: true + /create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + dependencies: + cipher-base: 1.0.4 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.2 + sha.js: 2.4.11 + dev: false + + /create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + dependencies: + cipher-base: 1.0.4 + create-hash: 1.2.0 + inherits: 2.0.4 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + dev: false + /cross-spawn@5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} dependencies: @@ -6900,6 +7015,37 @@ packages: engines: {node: '>=0.10.0'} dev: true + /ethereum-cryptography@0.1.3: + resolution: {integrity: sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==} + dependencies: + '@types/pbkdf2': 3.1.2 + '@types/secp256k1': 4.0.6 + blakejs: 1.2.1 + browserify-aes: 1.2.0 + bs58check: 2.1.2 + create-hash: 1.2.0 + create-hmac: 1.1.7 + hash.js: 1.1.7 + keccak: 3.0.4 + pbkdf2: 3.1.2 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + scrypt-js: 3.0.1 + secp256k1: 4.0.3 + setimmediate: 1.0.5 + dev: false + + /ethereumjs-util@7.1.5: + resolution: {integrity: sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==} + engines: {node: '>=10.0.0'} + dependencies: + '@types/bn.js': 5.1.5 + bn.js: 5.2.1 + create-hash: 1.2.0 + ethereum-cryptography: 0.1.3 + rlp: 2.2.7 + dev: false + /ethers@5.7.2: resolution: {integrity: sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==} dependencies: @@ -6963,6 +7109,13 @@ packages: engines: {node: '>=0.8.x'} dev: false + /evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + dependencies: + md5.js: 1.3.5 + safe-buffer: 5.2.1 + dev: false + /execa@3.4.0: resolution: {integrity: sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==} engines: {node: ^8.12.0 || >=9.7.0} @@ -7669,6 +7822,15 @@ packages: async: 1.5.2 dev: false + /hash-base@3.1.0: + resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} + engines: {node: '>=4'} + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.0 + safe-buffer: 5.2.1 + dev: false + /hash.js@1.1.7: resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} dependencies: @@ -8807,6 +8969,16 @@ packages: object.assign: 4.1.4 dev: true + /keccak@3.0.4: + resolution: {integrity: sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==} + engines: {node: '>=10.0.0'} + requiresBuild: true + dependencies: + node-addon-api: 2.0.2 + node-gyp-build: 4.8.0 + readable-stream: 3.6.0 + dev: false + /keygrip@1.1.0: resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} engines: {node: '>= 0.6'} @@ -9203,6 +9375,14 @@ packages: engines: {node: '>=8'} dev: true + /md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: false + /media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -9518,6 +9698,10 @@ packages: semver: 7.5.0 dev: false + /node-addon-api@2.0.2: + resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} + dev: false + /node-fetch@2.6.9: resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==} engines: {node: 4.x || >=6.0.0} @@ -9530,6 +9714,11 @@ packages: whatwg-url: 5.0.0 dev: false + /node-gyp-build@4.8.0: + resolution: {integrity: sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==} + hasBin: true + dev: false + /node-gyp@9.4.1: resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} engines: {node: ^12.13 || ^14.13 || >=16} @@ -9972,6 +10161,17 @@ packages: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: true + /pbkdf2@3.1.2: + resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} + engines: {node: '>=0.12'} + dependencies: + create-hash: 1.2.0 + create-hmac: 1.1.7 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + dev: false + /peek-stream@1.1.3: resolution: {integrity: sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==} dependencies: @@ -10390,6 +10590,12 @@ packages: engines: {node: '>=8'} dev: true + /randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -10706,6 +10912,20 @@ packages: dependencies: glob: 7.2.3 + /ripemd160@2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + dev: false + + /rlp@2.2.7: + resolution: {integrity: sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==} + hasBin: true + dependencies: + bn.js: 5.2.1 + dev: false + /rollup@3.21.8: resolution: {integrity: sha512-SSFV2T2fWtQ/vvBip85u2Nr0GNKireabH9d7nXswBg+XSH+jbVDSYptRAEbCEsquhs503rpPA9POYAp0/Jhasw==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} @@ -10790,6 +11010,16 @@ packages: resolution: {integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==} dev: false + /secp256k1@4.0.3: + resolution: {integrity: sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==} + engines: {node: '>=10.0.0'} + requiresBuild: true + dependencies: + elliptic: 6.5.4 + node-addon-api: 2.0.2 + node-gyp-build: 4.8.0 + dev: false + /secure-json-parse@2.7.0: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} dev: false @@ -10834,10 +11064,22 @@ packages: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} dev: false + /setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + dev: false + /setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} dev: false + /sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: false + /shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} From 6233816685300f186b17926e80c1d9ef8c9f824b Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 12:04:26 +0100 Subject: [PATCH 04/53] chore: unused package --- packages/cli/package.json | 6 ------ pnpm-lock.yaml | 18 ------------------ 2 files changed, 24 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 904ec2b435..0cefe3f826 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -31,12 +31,7 @@ }, "dependencies": { "@aws-sdk/client-kms": "*", - "@ethersproject/abi": "^5.7.0", "@ethersproject/bytes": "^5.5.4", - "@ethersproject/providers": "^5.7.2", - "@ethersproject/abstract-signer": "^5.5.4", - "@ethersproject/hash": "^5.5.4", - "@ethersproject/keccak256": "^5.5.4", "@improbable-eng/grpc-web": "^0.15.0", "@improbable-eng/grpc-web-node-http-transport": "^0.15.0", "@latticexyz/abi-ts": "workspace:*", @@ -51,7 +46,6 @@ "@latticexyz/world": "workspace:*", "@latticexyz/world-modules": "workspace:*", "asn1.js": "^5.4.1", - "bn.js": "^4.12.0", "chalk": "^5.0.1", "chokidar": "^3.5.3", "debug": "^4.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9712a19a1f..8731ac5b58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -133,24 +133,9 @@ importers: '@aws-sdk/client-kms': specifier: '*' version: 3.556.0 - '@ethersproject/abi': - specifier: ^5.7.0 - version: 5.7.0 - '@ethersproject/abstract-signer': - specifier: ^5.5.4 - version: 5.7.0 '@ethersproject/bytes': specifier: ^5.5.4 version: 5.7.0 - '@ethersproject/hash': - specifier: ^5.5.4 - version: 5.7.0 - '@ethersproject/keccak256': - specifier: ^5.5.4 - version: 5.7.0 - '@ethersproject/providers': - specifier: ^5.7.2 - version: 5.7.2 '@improbable-eng/grpc-web': specifier: ^0.15.0 version: 0.15.0(google-protobuf@3.21.2) @@ -193,9 +178,6 @@ importers: asn1.js: specifier: ^5.4.1 version: 5.4.1 - bn.js: - specifier: ^4.12.0 - version: 4.12.0 chalk: specifier: ^5.0.1 version: 5.2.0 From 76e2dca45b005b1a4a036e94670b9f1bfd38d9be Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 12:30:20 +0100 Subject: [PATCH 05/53] feat: viem account --- packages/cli/scripts/eth.ts | 15 ++-- packages/cli/scripts/kms-account.ts | 115 ++++++++++++++++++++++++++++ packages/cli/scripts/kms-signer.ts | 53 ------------- packages/cli/scripts/kms.ts | 5 +- 4 files changed, 124 insertions(+), 64 deletions(-) create mode 100644 packages/cli/scripts/kms-account.ts delete mode 100644 packages/cli/scripts/kms-signer.ts diff --git a/packages/cli/scripts/eth.ts b/packages/cli/scripts/eth.ts index abbec48ce2..c9f4336a76 100644 --- a/packages/cli/scripts/eth.ts +++ b/packages/cli/scripts/eth.ts @@ -3,7 +3,7 @@ import { BigNumber, utils } from "ethers"; import { sign } from "./kms"; import { CreateSignatureParams, SignParams } from "./types"; -import { BytesLike, SignatureLike } from "@ethersproject/bytes"; +import { Hex, hexToSignature } from "viem"; const EcdsaSigAsnParse = asn1.define("EcdsaSig", function (this: any) { this.seq().obj(this.key("r").int(), this.key("s").int()); @@ -66,18 +66,15 @@ export const getEthAddressFromPublicKey = (publicKey: Uint8Array): string => { return address; }; -export const createSignature = async ({ - keyId, - message, - address, - kmsInstance, -}: CreateSignatureParams): Promise> => { +export const createSignature = async ({ keyId, message, address, kmsInstance }: CreateSignatureParams) => { const { r, s } = await getRS({ keyId, message, kmsInstance }); const recoveryParam = getRecoveryParam(message, r, s, address); - return { + const ethersSignature = utils.joinSignature({ r: r, s: s, v: recoveryParam, - }; + }) as Hex; + + return hexToSignature(ethersSignature); }; diff --git a/packages/cli/scripts/kms-account.ts b/packages/cli/scripts/kms-account.ts new file mode 100644 index 0000000000..299cb1c279 --- /dev/null +++ b/packages/cli/scripts/kms-account.ts @@ -0,0 +1,115 @@ +import { hashMessage, hashTypedData, signatureToHex, verifyMessage, verifyTypedData } from "viem"; +import { toAccount } from "viem/accounts"; +import { KMSClient } from "@aws-sdk/client-kms"; +import { getEthAddressFromKMS } from "./kms"; +import { createSignature } from "./eth"; + +const keyId = "PLACE"; + +const kmsInstance = new KMSClient({ + region: "eu-west-2", + credentials: { + accessKeyId: "PLACE", + secretAccessKey: "PLACE", + }, +}); + +async function createAccount() { + const address = await getEthAddressFromKMS({ kmsInstance, keyId }); + const account = toAccount({ + address, + async signMessage({ message }) { + const hash = hashMessage(message); + const signature = await createSignature({ + kmsInstance: kmsInstance, + keyId: keyId, + message: hash, + address, + }); + + return signatureToHex(signature); + }, + async signTransaction() { + return "0x"; // TODO + }, + async signTypedData(typedData) { + const hash = hashTypedData(typedData); + + const address = await getEthAddressFromKMS({ kmsInstance, keyId }); + + const signature = await createSignature({ + kmsInstance: kmsInstance, + keyId: keyId, + message: hash, + address, + }); + + return signatureToHex(signature); + }, + }); + + return account; +} + +const account = await createAccount(); + +// Test signMessage +{ + const message = "hello world"; + const signature = await account.signMessage({ message }); + + const valid = await verifyMessage({ + address: account.address, + message, + signature, + }); + + console.log(valid); +} + +// Test signTypedData +{ + const chainId = 1; + const verifyingContract = "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5"; + const domain = { chainId, verifyingContract } as const; + const types = { + Person: [ + { name: "name", type: "string" }, + { name: "wallet", type: "address" }, + ], + Mail: [ + { name: "from", type: "Person" }, + { name: "to", type: "Person" }, + { name: "contents", type: "string" }, + ], + }; + const message = { + from: { + name: "Cow", + wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + to: { + name: "Bob", + wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + contents: "Hello, Bob!", + }; + + const signature = await account.signTypedData({ + domain, + types, + primaryType: "Mail", + message, + }); + + const valid = await verifyTypedData({ + address: account.address, + signature, + domain, + types, + primaryType: "Mail", + message, + }); + + console.log(valid); +} diff --git a/packages/cli/scripts/kms-signer.ts b/packages/cli/scripts/kms-signer.ts deleted file mode 100644 index 2136000248..0000000000 --- a/packages/cli/scripts/kms-signer.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { utils } from "ethers"; -import { KMSClient } from "@aws-sdk/client-kms"; -import { getEthAddressFromKMS } from "./kms"; -import { Hex, hashMessage, hexToSignature, recoverMessageAddress, signatureToHex, verifyMessage } from "viem"; -import { createSignature } from "./eth"; - -const keyId = "PLACEHOLDER"; - -const kmsInstance = new KMSClient({ - region: "eu-west-2", - credentials: { - accessKeyId: "PLACEHOLDER", - secretAccessKey: "PLACEHOLDER", - }, -}); - -async function signMessage(message: string): Promise { - const hash = hashMessage(message); - - const address = await getEthAddressFromKMS({ kmsInstance, keyId }); - - const ethersStructuredSignature = await createSignature({ - kmsInstance: kmsInstance, - keyId: keyId, - message: hash, - address, - }); - - const ethersHexSignature = utils.joinSignature(ethersStructuredSignature) as Hex; - - const structuredSignature = hexToSignature(ethersHexSignature); - - return signatureToHex(structuredSignature); -} - -const message = "hello"; - -const signature = await signMessage(message); - -const recoveredAddress = await recoverMessageAddress({ - message, - signature, -}); - -const valid = await verifyMessage({ - address: recoveredAddress, - message, - signature, -}); - -console.log(signature); -console.log(recoveredAddress); -console.log(valid); diff --git a/packages/cli/scripts/kms.ts b/packages/cli/scripts/kms.ts index f24a03c938..bb9b34591b 100644 --- a/packages/cli/scripts/kms.ts +++ b/packages/cli/scripts/kms.ts @@ -2,6 +2,7 @@ import { SignParams, GetEthAddressFromKMSparams, GetPublicKeyParams } from "./ty import { getEthAddressFromPublicKey } from "./eth"; import { utils } from "ethers"; import { GetPublicKeyCommand, GetPublicKeyCommandOutput, SignCommand, SignCommandOutput } from "@aws-sdk/client-kms"; +import { Address } from "viem"; export const getPublicKey = (getPublicKeyParams: GetPublicKeyParams): Promise => { const { keyId, kmsInstance } = getPublicKeyParams; @@ -10,11 +11,11 @@ export const getPublicKey = (getPublicKeyParams: GetPublicKeyParams): Promise => { +export const getEthAddressFromKMS = async (getEthAddressFromKMSparams: GetEthAddressFromKMSparams) => { const { keyId, kmsInstance } = getEthAddressFromKMSparams; const KMSKey = await getPublicKey({ keyId, kmsInstance }); - return getEthAddressFromPublicKey(KMSKey.PublicKey); + return getEthAddressFromPublicKey(KMSKey.PublicKey) as Address; }; export const sign = async (signParams: SignParams): Promise => { From 16068a08d5c175fec6c49b3b2fd542519c5166e4 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 12:52:40 +0100 Subject: [PATCH 06/53] refactor: file --- packages/cli/scripts/kms-account.ts | 78 +----------------------- packages/cli/scripts/test-kms-account.ts | 75 +++++++++++++++++++++++ 2 files changed, 78 insertions(+), 75 deletions(-) create mode 100644 packages/cli/scripts/test-kms-account.ts diff --git a/packages/cli/scripts/kms-account.ts b/packages/cli/scripts/kms-account.ts index 299cb1c279..51dc4cbf20 100644 --- a/packages/cli/scripts/kms-account.ts +++ b/packages/cli/scripts/kms-account.ts @@ -1,21 +1,12 @@ -import { hashMessage, hashTypedData, signatureToHex, verifyMessage, verifyTypedData } from "viem"; +import { hashMessage, hashTypedData, signatureToHex } from "viem"; import { toAccount } from "viem/accounts"; import { KMSClient } from "@aws-sdk/client-kms"; import { getEthAddressFromKMS } from "./kms"; import { createSignature } from "./eth"; -const keyId = "PLACE"; - -const kmsInstance = new KMSClient({ - region: "eu-west-2", - credentials: { - accessKeyId: "PLACE", - secretAccessKey: "PLACE", - }, -}); - -async function createAccount() { +export async function createKmsAccount(keyId: string, kmsInstance: KMSClient) { const address = await getEthAddressFromKMS({ kmsInstance, keyId }); + const account = toAccount({ address, async signMessage({ message }) { @@ -50,66 +41,3 @@ async function createAccount() { return account; } - -const account = await createAccount(); - -// Test signMessage -{ - const message = "hello world"; - const signature = await account.signMessage({ message }); - - const valid = await verifyMessage({ - address: account.address, - message, - signature, - }); - - console.log(valid); -} - -// Test signTypedData -{ - const chainId = 1; - const verifyingContract = "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5"; - const domain = { chainId, verifyingContract } as const; - const types = { - Person: [ - { name: "name", type: "string" }, - { name: "wallet", type: "address" }, - ], - Mail: [ - { name: "from", type: "Person" }, - { name: "to", type: "Person" }, - { name: "contents", type: "string" }, - ], - }; - const message = { - from: { - name: "Cow", - wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", - }, - to: { - name: "Bob", - wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", - }, - contents: "Hello, Bob!", - }; - - const signature = await account.signTypedData({ - domain, - types, - primaryType: "Mail", - message, - }); - - const valid = await verifyTypedData({ - address: account.address, - signature, - domain, - types, - primaryType: "Mail", - message, - }); - - console.log(valid); -} diff --git a/packages/cli/scripts/test-kms-account.ts b/packages/cli/scripts/test-kms-account.ts new file mode 100644 index 0000000000..5660c3d4bf --- /dev/null +++ b/packages/cli/scripts/test-kms-account.ts @@ -0,0 +1,75 @@ +import { verifyMessage, verifyTypedData } from "viem"; +import { KMSClient } from "@aws-sdk/client-kms"; +import { createKmsAccount } from "./kms-account"; + +const keyId = "PLACEHOLDER"; +const kmsInstance = new KMSClient({ + region: "eu-west-2", + credentials: { + accessKeyId: "PLACEHOLDER", + secretAccessKey: "PLACEHOLDER", + }, +}); + +const account = await createKmsAccount(keyId, kmsInstance); + +// Test signMessage +{ + const message = "hello world"; + const signature = await account.signMessage({ message }); + + const valid = await verifyMessage({ + address: account.address, + message, + signature, + }); + + console.log(valid); +} + +// Test signTypedData +{ + const chainId = 1; + const verifyingContract = "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5"; + const domain = { chainId, verifyingContract } as const; + const types = { + Person: [ + { name: "name", type: "string" }, + { name: "wallet", type: "address" }, + ], + Mail: [ + { name: "from", type: "Person" }, + { name: "to", type: "Person" }, + { name: "contents", type: "string" }, + ], + }; + const message = { + from: { + name: "Cow", + wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + to: { + name: "Bob", + wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + contents: "Hello, Bob!", + }; + + const signature = await account.signTypedData({ + domain, + types, + primaryType: "Mail", + message, + }); + + const valid = await verifyTypedData({ + address: account.address, + signature, + domain, + types, + primaryType: "Mail", + message, + }); + + console.log(valid); +} From 621b5c2c8f668d79ab8a632767a873a1265e6cae Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 13:04:43 +0100 Subject: [PATCH 07/53] refactor: move to common --- packages/cli/package.json | 6 +- packages/cli/scripts/test-kms-account.ts | 75 ----------------- packages/common/package.json | 3 + .../scripts => common/src/account}/eth.ts | 1 + .../scripts => common/src/account}/kms.ts | 8 +- .../common/src/account/kmsAccount.test.ts | 82 +++++++++++++++++++ .../src/account/kmsAccount.ts} | 6 +- .../scripts => common/src/account}/types.ts | 0 pnpm-lock.yaml | 9 ++ 9 files changed, 105 insertions(+), 85 deletions(-) delete mode 100644 packages/cli/scripts/test-kms-account.ts rename packages/{cli/scripts => common/src/account}/eth.ts (99%) rename packages/{cli/scripts => common/src/account}/kms.ts (84%) create mode 100644 packages/common/src/account/kmsAccount.test.ts rename packages/{cli/scripts/kms-account.ts => common/src/account/kmsAccount.ts} (90%) rename packages/{cli/scripts => common/src/account}/types.ts (100%) diff --git a/packages/cli/package.json b/packages/cli/package.json index 0cefe3f826..80cedb8da2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -30,8 +30,8 @@ "test:ci": "pnpm run test" }, "dependencies": { - "@aws-sdk/client-kms": "*", - "@ethersproject/bytes": "^5.5.4", + "@ethersproject/abi": "^5.7.0", + "@ethersproject/providers": "^5.7.2", "@improbable-eng/grpc-web": "^0.15.0", "@improbable-eng/grpc-web-node-http-transport": "^0.15.0", "@latticexyz/abi-ts": "workspace:*", @@ -45,13 +45,11 @@ "@latticexyz/utils": "workspace:*", "@latticexyz/world": "workspace:*", "@latticexyz/world-modules": "workspace:*", - "asn1.js": "^5.4.1", "chalk": "^5.0.1", "chokidar": "^3.5.3", "debug": "^4.3.4", "dotenv": "^16.0.3", "ejs": "^3.1.8", - "ethereumjs-util": "^7.1.5", "ethers": "^5.7.2", "execa": "^7.0.0", "glob": "^8.0.3", diff --git a/packages/cli/scripts/test-kms-account.ts b/packages/cli/scripts/test-kms-account.ts deleted file mode 100644 index 5660c3d4bf..0000000000 --- a/packages/cli/scripts/test-kms-account.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { verifyMessage, verifyTypedData } from "viem"; -import { KMSClient } from "@aws-sdk/client-kms"; -import { createKmsAccount } from "./kms-account"; - -const keyId = "PLACEHOLDER"; -const kmsInstance = new KMSClient({ - region: "eu-west-2", - credentials: { - accessKeyId: "PLACEHOLDER", - secretAccessKey: "PLACEHOLDER", - }, -}); - -const account = await createKmsAccount(keyId, kmsInstance); - -// Test signMessage -{ - const message = "hello world"; - const signature = await account.signMessage({ message }); - - const valid = await verifyMessage({ - address: account.address, - message, - signature, - }); - - console.log(valid); -} - -// Test signTypedData -{ - const chainId = 1; - const verifyingContract = "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5"; - const domain = { chainId, verifyingContract } as const; - const types = { - Person: [ - { name: "name", type: "string" }, - { name: "wallet", type: "address" }, - ], - Mail: [ - { name: "from", type: "Person" }, - { name: "to", type: "Person" }, - { name: "contents", type: "string" }, - ], - }; - const message = { - from: { - name: "Cow", - wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", - }, - to: { - name: "Bob", - wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", - }, - contents: "Hello, Bob!", - }; - - const signature = await account.signTypedData({ - domain, - types, - primaryType: "Mail", - message, - }); - - const valid = await verifyTypedData({ - address: account.address, - signature, - domain, - types, - primaryType: "Mail", - message, - }); - - console.log(valid); -} diff --git a/packages/common/package.json b/packages/common/package.json index f4ffab22f0..c4f13142ef 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -57,9 +57,12 @@ "test:ci": "pnpm run test" }, "dependencies": { + "@aws-sdk/client-kms": "*", "@latticexyz/schema-type": "workspace:*", "@solidity-parser/parser": "^0.16.0", + "asn1.js": "^5.4.1", "debug": "^4.3.4", + "ethers": "^5.7.2", "execa": "^7.0.0", "p-queue": "^7.4.1", "p-retry": "^5.1.2", diff --git a/packages/cli/scripts/eth.ts b/packages/common/src/account/eth.ts similarity index 99% rename from packages/cli/scripts/eth.ts rename to packages/common/src/account/eth.ts index c9f4336a76..a890d5f48c 100644 --- a/packages/cli/scripts/eth.ts +++ b/packages/common/src/account/eth.ts @@ -1,3 +1,4 @@ +// @ts-ignore import asn1 from "asn1.js"; import { BigNumber, utils } from "ethers"; diff --git a/packages/cli/scripts/kms.ts b/packages/common/src/account/kms.ts similarity index 84% rename from packages/cli/scripts/kms.ts rename to packages/common/src/account/kms.ts index bb9b34591b..0447c14bd1 100644 --- a/packages/cli/scripts/kms.ts +++ b/packages/common/src/account/kms.ts @@ -1,7 +1,7 @@ +import { GetPublicKeyCommand, GetPublicKeyCommandOutput, SignCommand, SignCommandOutput } from "@aws-sdk/client-kms"; import { SignParams, GetEthAddressFromKMSparams, GetPublicKeyParams } from "./types"; import { getEthAddressFromPublicKey } from "./eth"; import { utils } from "ethers"; -import { GetPublicKeyCommand, GetPublicKeyCommandOutput, SignCommand, SignCommandOutput } from "@aws-sdk/client-kms"; import { Address } from "viem"; export const getPublicKey = (getPublicKeyParams: GetPublicKeyParams): Promise => { @@ -11,11 +11,13 @@ export const getPublicKey = (getPublicKeyParams: GetPublicKeyParams): Promise { +export const getEthAddressFromKMS = async ( + getEthAddressFromKMSparams: GetEthAddressFromKMSparams, +): Promise
=> { const { keyId, kmsInstance } = getEthAddressFromKMSparams; const KMSKey = await getPublicKey({ keyId, kmsInstance }); - return getEthAddressFromPublicKey(KMSKey.PublicKey) as Address; + return getEthAddressFromPublicKey(KMSKey.PublicKey as Uint8Array) as Address; }; export const sign = async (signParams: SignParams): Promise => { diff --git a/packages/common/src/account/kmsAccount.test.ts b/packages/common/src/account/kmsAccount.test.ts new file mode 100644 index 0000000000..e1b082de4c --- /dev/null +++ b/packages/common/src/account/kmsAccount.test.ts @@ -0,0 +1,82 @@ +import { describe, it, expect, beforeEach } from "vitest"; +import { createKmsAccount } from "./kmsAccount"; +import { KMSClient } from "@aws-sdk/client-kms"; +import { LocalAccount, verifyMessage, verifyTypedData } from "viem"; + +describe("kmsAccount", () => { + let account: LocalAccount; + + beforeEach(async () => { + const keyId = "PLACEHOLDER"; + const kmsInstance = new KMSClient({ + region: "eu-west-2", + credentials: { + accessKeyId: "PLACEHOLDER", + secretAccessKey: "PLACEHOLDER", + }, + }); + + account = await createKmsAccount(keyId, kmsInstance); + }); + + it("signMessage", async () => { + const message = "hello world"; + const signature = await account.signMessage({ message }); + + const valid = await verifyMessage({ + address: account.address, + message, + signature, + }); + + expect(valid).toBeTruthy(); + }); + + it("signTypedData", async () => { + // Test signTypedData + + const chainId = 1; + const verifyingContract = "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5"; + const domain = { chainId, verifyingContract } as const; + const types = { + Person: [ + { name: "name", type: "string" }, + { name: "wallet", type: "address" }, + ], + Mail: [ + { name: "from", type: "Person" }, + { name: "to", type: "Person" }, + { name: "contents", type: "string" }, + ], + }; + const message = { + from: { + name: "Cow", + wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + to: { + name: "Bob", + wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + contents: "Hello, Bob!", + }; + + const signature = await account.signTypedData({ + domain, + types, + primaryType: "Mail", + message, + }); + + const valid = await verifyTypedData({ + address: account.address, + signature, + domain, + types, + primaryType: "Mail", + message, + }); + + expect(valid).toBeTruthy(); + }); +}); diff --git a/packages/cli/scripts/kms-account.ts b/packages/common/src/account/kmsAccount.ts similarity index 90% rename from packages/cli/scripts/kms-account.ts rename to packages/common/src/account/kmsAccount.ts index 51dc4cbf20..218048a204 100644 --- a/packages/cli/scripts/kms-account.ts +++ b/packages/common/src/account/kmsAccount.ts @@ -1,10 +1,10 @@ -import { hashMessage, hashTypedData, signatureToHex } from "viem"; -import { toAccount } from "viem/accounts"; import { KMSClient } from "@aws-sdk/client-kms"; +import { LocalAccount, hashMessage, hashTypedData, signatureToHex } from "viem"; +import { toAccount } from "viem/accounts"; import { getEthAddressFromKMS } from "./kms"; import { createSignature } from "./eth"; -export async function createKmsAccount(keyId: string, kmsInstance: KMSClient) { +export async function createKmsAccount(keyId: string, kmsInstance: KMSClient): Promise { const address = await getEthAddressFromKMS({ kmsInstance, keyId }); const account = toAccount({ diff --git a/packages/cli/scripts/types.ts b/packages/common/src/account/types.ts similarity index 100% rename from packages/cli/scripts/types.ts rename to packages/common/src/account/types.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8731ac5b58..bebb205940 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -284,15 +284,24 @@ importers: packages/common: dependencies: + '@aws-sdk/client-kms': + specifier: '*' + version: 3.556.0 '@latticexyz/schema-type': specifier: workspace:* version: link:../schema-type '@solidity-parser/parser': specifier: ^0.16.0 version: 0.16.0 + asn1.js: + specifier: ^5.4.1 + version: 5.4.1 debug: specifier: ^4.3.4 version: 4.3.4 + ethers: + specifier: ^5.7.2 + version: 5.7.2 execa: specifier: ^7.0.0 version: 7.0.0 From c7aacb368c71fddfc99777c23ee7daa047d151f0 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 14:05:22 +0100 Subject: [PATCH 08/53] refactor: address types --- packages/common/src/account/eth.ts | 48 ++++++++++--------- .../common/src/account/kmsAccount.test.ts | 2 - packages/common/src/account/kmsAccount.ts | 11 ++--- packages/common/src/account/types.ts | 3 +- 4 files changed, 31 insertions(+), 33 deletions(-) diff --git a/packages/common/src/account/eth.ts b/packages/common/src/account/eth.ts index a890d5f48c..c0b7a4719d 100644 --- a/packages/common/src/account/eth.ts +++ b/packages/common/src/account/eth.ts @@ -1,10 +1,9 @@ // @ts-ignore import asn1 from "asn1.js"; -import { BigNumber, utils } from "ethers"; - +import { utils } from "ethers"; import { sign } from "./kms"; import { CreateSignatureParams, SignParams } from "./types"; -import { Hex, hexToSignature } from "viem"; +import { Hex, Signature, hexToSignature, isAddressEqual, toHex } from "viem"; const EcdsaSigAsnParse = asn1.define("EcdsaSig", function (this: any) { this.seq().obj(this.key("r").int(), this.key("s").int()); @@ -14,43 +13,41 @@ const EcdsaPubKey = asn1.define("EcdsaPubKey", function (this: any) { this.seq().obj(this.key("algo").seq().obj(this.key("a").objid(), this.key("b").objid()), this.key("pubKey").bitstr()); }); -const getRS = async (signParams: SignParams) => { +const getRS = async (signParams: SignParams): Promise<{ r: Hex; s: Hex }> => { const signature = await sign(signParams); - if (signature.Signature == undefined) { + if (signature.Signature === undefined) { throw new Error("Signature is undefined."); } const decoded = EcdsaSigAsnParse.decode(Buffer.from(signature.Signature), "der"); - const r = BigNumber.from(`0x${decoded.r.toString("hex")}`); - let s = BigNumber.from(`0x${decoded.s.toString("hex")}`); + const r = BigInt(decoded.r); + let s = BigInt(decoded.s); - const secp256k1N = BigNumber.from("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); - const secp256k1halfN = secp256k1N.div(BigNumber.from(2)); + const secp256k1N = BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); + const secp256k1halfN = secp256k1N / 2n; - if (s.gt(secp256k1halfN)) { - s = secp256k1N.sub(s); + if (s > secp256k1halfN) { + s = secp256k1N - s; } return { - r: r.toHexString(), - s: s.toHexString(), + r: toHex(r), + s: toHex(s), }; }; -const getRecoveryParam = (msg: string, r: string, s: string, expectedEthAddr: string) => { +const getRecoveryParam = (msg: string, r: string, s: string, expectedEthAddr: Hex): number => { const formatted = msg; let recoveryParam: number; for (recoveryParam = 0; recoveryParam <= 1; recoveryParam++) { - const address = utils - .recoverAddress(formatted, { - r, - s, - recoveryParam, - }) - .toLowerCase(); - if (address !== expectedEthAddr.toLowerCase()) { + const address = utils.recoverAddress(formatted, { + r, + s, + recoveryParam, + }) as Hex; + if (!isAddressEqual(address, expectedEthAddr)) { continue; } return recoveryParam; @@ -67,7 +64,12 @@ export const getEthAddressFromPublicKey = (publicKey: Uint8Array): string => { return address; }; -export const createSignature = async ({ keyId, message, address, kmsInstance }: CreateSignatureParams) => { +export const createSignature = async ({ + keyId, + message, + address, + kmsInstance, +}: CreateSignatureParams): Promise => { const { r, s } = await getRS({ keyId, message, kmsInstance }); const recoveryParam = getRecoveryParam(message, r, s, address); diff --git a/packages/common/src/account/kmsAccount.test.ts b/packages/common/src/account/kmsAccount.test.ts index e1b082de4c..61b00a771f 100644 --- a/packages/common/src/account/kmsAccount.test.ts +++ b/packages/common/src/account/kmsAccount.test.ts @@ -33,8 +33,6 @@ describe("kmsAccount", () => { }); it("signTypedData", async () => { - // Test signTypedData - const chainId = 1; const verifyingContract = "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5"; const domain = { chainId, verifyingContract } as const; diff --git a/packages/common/src/account/kmsAccount.ts b/packages/common/src/account/kmsAccount.ts index 218048a204..a26a17bb0b 100644 --- a/packages/common/src/account/kmsAccount.ts +++ b/packages/common/src/account/kmsAccount.ts @@ -12,8 +12,8 @@ export async function createKmsAccount(keyId: string, kmsInstance: KMSClient): P async signMessage({ message }) { const hash = hashMessage(message); const signature = await createSignature({ - kmsInstance: kmsInstance, - keyId: keyId, + kmsInstance, + keyId, message: hash, address, }); @@ -25,12 +25,9 @@ export async function createKmsAccount(keyId: string, kmsInstance: KMSClient): P }, async signTypedData(typedData) { const hash = hashTypedData(typedData); - - const address = await getEthAddressFromKMS({ kmsInstance, keyId }); - const signature = await createSignature({ - kmsInstance: kmsInstance, - keyId: keyId, + kmsInstance, + keyId, message: hash, address, }); diff --git a/packages/common/src/account/types.ts b/packages/common/src/account/types.ts index e8ba8b383e..3ef7917021 100644 --- a/packages/common/src/account/types.ts +++ b/packages/common/src/account/types.ts @@ -1,4 +1,5 @@ import { KMSClient, SignCommandInput } from "@aws-sdk/client-kms"; +import { Hex } from "viem"; export type SignParams = { keyId: SignCommandInput["KeyId"]; @@ -17,5 +18,5 @@ export type GetPublicKeyParams = { }; export type CreateSignatureParams = SignParams & { - address: string; + address: Hex; }; From e368be07e762716a72421f97cc3d2fe46788c148 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 15:22:45 +0100 Subject: [PATCH 09/53] feat: signTransaction --- .../common/src/account/kmsAccount.test.ts | 19 +++++++++++++++---- packages/common/src/account/kmsAccount.ts | 15 ++++++++++++--- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/common/src/account/kmsAccount.test.ts b/packages/common/src/account/kmsAccount.test.ts index 61b00a771f..ba30ee3fde 100644 --- a/packages/common/src/account/kmsAccount.test.ts +++ b/packages/common/src/account/kmsAccount.test.ts @@ -1,18 +1,18 @@ import { describe, it, expect, beforeEach } from "vitest"; import { createKmsAccount } from "./kmsAccount"; import { KMSClient } from "@aws-sdk/client-kms"; -import { LocalAccount, verifyMessage, verifyTypedData } from "viem"; +import { LocalAccount, parseGwei, verifyMessage, verifyTypedData } from "viem"; describe("kmsAccount", () => { let account: LocalAccount; beforeEach(async () => { - const keyId = "PLACEHOLDER"; + const keyId = "PLACE"; const kmsInstance = new KMSClient({ region: "eu-west-2", credentials: { - accessKeyId: "PLACEHOLDER", - secretAccessKey: "PLACEHOLDER", + accessKeyId: "PLACE", + secretAccessKey: "PLACE", }, }); @@ -32,6 +32,17 @@ describe("kmsAccount", () => { expect(valid).toBeTruthy(); }); + it("signTransaction", async () => { + await account.signTransaction({ + chainId: 1, + maxFeePerGas: parseGwei("20"), + maxPriorityFeePerGas: parseGwei("3"), + gas: 21000n, + nonce: 69, + to: "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + }); + }); + it("signTypedData", async () => { const chainId = 1; const verifyingContract = "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5"; diff --git a/packages/common/src/account/kmsAccount.ts b/packages/common/src/account/kmsAccount.ts index a26a17bb0b..e77fb457bf 100644 --- a/packages/common/src/account/kmsAccount.ts +++ b/packages/common/src/account/kmsAccount.ts @@ -1,5 +1,5 @@ import { KMSClient } from "@aws-sdk/client-kms"; -import { LocalAccount, hashMessage, hashTypedData, signatureToHex } from "viem"; +import { LocalAccount, hashMessage, hashTypedData, keccak256, serializeTransaction, signatureToHex } from "viem"; import { toAccount } from "viem/accounts"; import { getEthAddressFromKMS } from "./kms"; import { createSignature } from "./eth"; @@ -20,8 +20,17 @@ export async function createKmsAccount(keyId: string, kmsInstance: KMSClient): P return signatureToHex(signature); }, - async signTransaction() { - return "0x"; // TODO + async signTransaction(transaction) { + const unsignedTx = serializeTransaction(transaction); + const hash = keccak256(unsignedTx); + const signature = await createSignature({ + kmsInstance, + keyId, + message: hash, + address, + }); + + return signatureToHex(signature); }, async signTypedData(typedData) { const hash = hashTypedData(typedData); From 4dd5e8581e09c00ec4220083dae01a9bbcb331b0 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 15:26:48 +0100 Subject: [PATCH 10/53] chore: lockfile --- pnpm-lock.yaml | 228 ++----------------------------------------------- 1 file changed, 5 insertions(+), 223 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bebb205940..a2f30c1ec9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,12 +130,12 @@ importers: packages/cli: dependencies: - '@aws-sdk/client-kms': - specifier: '*' - version: 3.556.0 - '@ethersproject/bytes': - specifier: ^5.5.4 + '@ethersproject/abi': + specifier: ^5.7.0 version: 5.7.0 + '@ethersproject/providers': + specifier: ^5.7.2 + version: 5.7.2 '@improbable-eng/grpc-web': specifier: ^0.15.0 version: 0.15.0(google-protobuf@3.21.2) @@ -175,9 +175,6 @@ importers: '@latticexyz/world-modules': specifier: workspace:* version: link:../world-modules - asn1.js: - specifier: ^5.4.1 - version: 5.4.1 chalk: specifier: ^5.0.1 version: 5.2.0 @@ -193,9 +190,6 @@ importers: ejs: specifier: ^3.1.8 version: 3.1.8 - ethereumjs-util: - specifier: ^7.1.5 - version: 7.1.5 ethers: specifier: ^5.7.2 version: 5.7.2 @@ -4381,12 +4375,6 @@ packages: dependencies: '@types/node': 18.15.11 - /@types/bn.js@5.1.5: - resolution: {integrity: sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==} - dependencies: - '@types/node': 18.15.11 - dev: false - /@types/body-parser@1.19.5: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: @@ -4596,12 +4584,6 @@ packages: resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} dev: true - /@types/pbkdf2@3.1.2: - resolution: {integrity: sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==} - dependencies: - '@types/node': 18.15.11 - dev: false - /@types/prettier@2.7.2: resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==} dev: true @@ -4640,12 +4622,6 @@ packages: resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} dev: true - /@types/secp256k1@4.0.6: - resolution: {integrity: sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==} - dependencies: - '@types/node': 18.15.11 - dev: false - /@types/semver@6.2.3: resolution: {integrity: sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==} dev: true @@ -5441,12 +5417,6 @@ packages: /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - /base-x@3.0.9: - resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} - dependencies: - safe-buffer: 5.2.1 - dev: false - /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: false @@ -5488,10 +5458,6 @@ packages: readable-stream: 3.6.0 dev: false - /blakejs@1.2.1: - resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} - dev: false - /bn.js@4.12.0: resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} dev: false @@ -5535,17 +5501,6 @@ packages: resolution: {integrity: sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg==} dev: false - /browserify-aes@1.2.0: - resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} - dependencies: - buffer-xor: 1.0.3 - cipher-base: 1.0.4 - create-hash: 1.2.0 - evp_bytestokey: 1.0.3 - inherits: 2.0.4 - safe-buffer: 5.2.1 - dev: false - /browserslist@4.23.0: resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -5564,20 +5519,6 @@ packages: fast-json-stable-stringify: 2.1.0 dev: true - /bs58@4.0.1: - resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} - dependencies: - base-x: 3.0.9 - dev: false - - /bs58check@2.1.2: - resolution: {integrity: sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==} - dependencies: - bs58: 4.0.1 - create-hash: 1.2.0 - safe-buffer: 5.2.1 - dev: false - /bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} dependencies: @@ -5587,10 +5528,6 @@ packages: /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - /buffer-xor@1.0.3: - resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} - dev: false - /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} dependencies: @@ -5835,13 +5772,6 @@ packages: engines: {node: '>=8'} dev: true - /cipher-base@1.0.4: - resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - dev: false - /cjs-module-lexer@1.2.2: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} dev: true @@ -6075,27 +6005,6 @@ packages: yaml: 1.10.2 dev: true - /create-hash@1.2.0: - resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} - dependencies: - cipher-base: 1.0.4 - inherits: 2.0.4 - md5.js: 1.3.5 - ripemd160: 2.0.2 - sha.js: 2.4.11 - dev: false - - /create-hmac@1.1.7: - resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} - dependencies: - cipher-base: 1.0.4 - create-hash: 1.2.0 - inherits: 2.0.4 - ripemd160: 2.0.2 - safe-buffer: 5.2.1 - sha.js: 2.4.11 - dev: false - /cross-spawn@5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} dependencies: @@ -7006,37 +6915,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /ethereum-cryptography@0.1.3: - resolution: {integrity: sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==} - dependencies: - '@types/pbkdf2': 3.1.2 - '@types/secp256k1': 4.0.6 - blakejs: 1.2.1 - browserify-aes: 1.2.0 - bs58check: 2.1.2 - create-hash: 1.2.0 - create-hmac: 1.1.7 - hash.js: 1.1.7 - keccak: 3.0.4 - pbkdf2: 3.1.2 - randombytes: 2.1.0 - safe-buffer: 5.2.1 - scrypt-js: 3.0.1 - secp256k1: 4.0.3 - setimmediate: 1.0.5 - dev: false - - /ethereumjs-util@7.1.5: - resolution: {integrity: sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==} - engines: {node: '>=10.0.0'} - dependencies: - '@types/bn.js': 5.1.5 - bn.js: 5.2.1 - create-hash: 1.2.0 - ethereum-cryptography: 0.1.3 - rlp: 2.2.7 - dev: false - /ethers@5.7.2: resolution: {integrity: sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==} dependencies: @@ -7100,13 +6978,6 @@ packages: engines: {node: '>=0.8.x'} dev: false - /evp_bytestokey@1.0.3: - resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} - dependencies: - md5.js: 1.3.5 - safe-buffer: 5.2.1 - dev: false - /execa@3.4.0: resolution: {integrity: sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==} engines: {node: ^8.12.0 || >=9.7.0} @@ -7813,15 +7684,6 @@ packages: async: 1.5.2 dev: false - /hash-base@3.1.0: - resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} - engines: {node: '>=4'} - dependencies: - inherits: 2.0.4 - readable-stream: 3.6.0 - safe-buffer: 5.2.1 - dev: false - /hash.js@1.1.7: resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} dependencies: @@ -8960,16 +8822,6 @@ packages: object.assign: 4.1.4 dev: true - /keccak@3.0.4: - resolution: {integrity: sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==} - engines: {node: '>=10.0.0'} - requiresBuild: true - dependencies: - node-addon-api: 2.0.2 - node-gyp-build: 4.8.0 - readable-stream: 3.6.0 - dev: false - /keygrip@1.1.0: resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} engines: {node: '>= 0.6'} @@ -9366,14 +9218,6 @@ packages: engines: {node: '>=8'} dev: true - /md5.js@1.3.5: - resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} - dependencies: - hash-base: 3.1.0 - inherits: 2.0.4 - safe-buffer: 5.2.1 - dev: false - /media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -9689,10 +9533,6 @@ packages: semver: 7.5.0 dev: false - /node-addon-api@2.0.2: - resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} - dev: false - /node-fetch@2.6.9: resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==} engines: {node: 4.x || >=6.0.0} @@ -9705,11 +9545,6 @@ packages: whatwg-url: 5.0.0 dev: false - /node-gyp-build@4.8.0: - resolution: {integrity: sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==} - hasBin: true - dev: false - /node-gyp@9.4.1: resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} engines: {node: ^12.13 || ^14.13 || >=16} @@ -10152,17 +9987,6 @@ packages: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: true - /pbkdf2@3.1.2: - resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} - engines: {node: '>=0.12'} - dependencies: - create-hash: 1.2.0 - create-hmac: 1.1.7 - ripemd160: 2.0.2 - safe-buffer: 5.2.1 - sha.js: 2.4.11 - dev: false - /peek-stream@1.1.3: resolution: {integrity: sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==} dependencies: @@ -10581,12 +10405,6 @@ packages: engines: {node: '>=8'} dev: true - /randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - dependencies: - safe-buffer: 5.2.1 - dev: false - /rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -10903,20 +10721,6 @@ packages: dependencies: glob: 7.2.3 - /ripemd160@2.0.2: - resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} - dependencies: - hash-base: 3.1.0 - inherits: 2.0.4 - dev: false - - /rlp@2.2.7: - resolution: {integrity: sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==} - hasBin: true - dependencies: - bn.js: 5.2.1 - dev: false - /rollup@3.21.8: resolution: {integrity: sha512-SSFV2T2fWtQ/vvBip85u2Nr0GNKireabH9d7nXswBg+XSH+jbVDSYptRAEbCEsquhs503rpPA9POYAp0/Jhasw==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} @@ -11001,16 +10805,6 @@ packages: resolution: {integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==} dev: false - /secp256k1@4.0.3: - resolution: {integrity: sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==} - engines: {node: '>=10.0.0'} - requiresBuild: true - dependencies: - elliptic: 6.5.4 - node-addon-api: 2.0.2 - node-gyp-build: 4.8.0 - dev: false - /secure-json-parse@2.7.0: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} dev: false @@ -11055,22 +10849,10 @@ packages: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} dev: false - /setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - dev: false - /setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} dev: false - /sha.js@2.4.11: - resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} - hasBin: true - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - dev: false - /shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} From 0bcc517102cb6ca5cd90fe2566dcc2e477e9531a Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 16:42:49 +0100 Subject: [PATCH 11/53] refactor: types --- packages/common/src/account/eth.ts | 15 ++++++++++----- packages/common/src/account/kms.ts | 27 ++++++++++++++++++++++----- packages/common/src/account/types.ts | 15 --------------- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/packages/common/src/account/eth.ts b/packages/common/src/account/eth.ts index c0b7a4719d..348b8c829d 100644 --- a/packages/common/src/account/eth.ts +++ b/packages/common/src/account/eth.ts @@ -1,9 +1,13 @@ -// @ts-ignore +// @ts-expect-error import asn1 from "asn1.js"; import { utils } from "ethers"; import { sign } from "./kms"; -import { CreateSignatureParams, SignParams } from "./types"; -import { Hex, Signature, hexToSignature, isAddressEqual, toHex } from "viem"; +import { SignParams } from "./types"; +import { Address, Hex, Signature, hexToSignature, isAddressEqual, toHex } from "viem"; + +type CreateSignatureParams = SignParams & { + address: Hex; +}; const EcdsaSigAsnParse = asn1.define("EcdsaSig", function (this: any) { this.seq().obj(this.key("r").int(), this.key("s").int()); @@ -55,12 +59,12 @@ const getRecoveryParam = (msg: string, r: string, s: string, expectedEthAddr: He throw new Error("Failed to calculate recovery param"); }; -export const getEthAddressFromPublicKey = (publicKey: Uint8Array): string => { +export const getEthAddressFromPublicKey = (publicKey: Uint8Array): Address => { const res = EcdsaPubKey.decode(Buffer.from(publicKey)); const pubKeyBuffer: Buffer = res.pubKey.data; - const address = utils.computeAddress(pubKeyBuffer); + const address = utils.computeAddress(pubKeyBuffer) as Address; return address; }; @@ -73,6 +77,7 @@ export const createSignature = async ({ const { r, s } = await getRS({ keyId, message, kmsInstance }); const recoveryParam = getRecoveryParam(message, r, s, address); + // REMOVE THIS const ethersSignature = utils.joinSignature({ r: r, s: s, diff --git a/packages/common/src/account/kms.ts b/packages/common/src/account/kms.ts index 0447c14bd1..5744ff1f44 100644 --- a/packages/common/src/account/kms.ts +++ b/packages/common/src/account/kms.ts @@ -1,10 +1,27 @@ -import { GetPublicKeyCommand, GetPublicKeyCommandOutput, SignCommand, SignCommandOutput } from "@aws-sdk/client-kms"; -import { SignParams, GetEthAddressFromKMSparams, GetPublicKeyParams } from "./types"; -import { getEthAddressFromPublicKey } from "./eth"; +import { + GetPublicKeyCommand, + GetPublicKeyCommandOutput, + KMSClient, + SignCommand, + SignCommandInput, + SignCommandOutput, +} from "@aws-sdk/client-kms"; import { utils } from "ethers"; import { Address } from "viem"; +import { SignParams } from "./types"; +import { getEthAddressFromPublicKey } from "./eth"; + +type GetPublicKeyParams = { + keyId: SignCommandInput["KeyId"]; + kmsInstance: KMSClient; +}; + +type GetEthAddressFromKMSparams = { + keyId: SignCommandInput["KeyId"]; + kmsInstance: KMSClient; +}; -export const getPublicKey = (getPublicKeyParams: GetPublicKeyParams): Promise => { +const getPublicKey = (getPublicKeyParams: GetPublicKeyParams): Promise => { const { keyId, kmsInstance } = getPublicKeyParams; const command = new GetPublicKeyCommand({ KeyId: keyId }); @@ -17,7 +34,7 @@ export const getEthAddressFromKMS = async ( const { keyId, kmsInstance } = getEthAddressFromKMSparams; const KMSKey = await getPublicKey({ keyId, kmsInstance }); - return getEthAddressFromPublicKey(KMSKey.PublicKey as Uint8Array) as Address; + return getEthAddressFromPublicKey(KMSKey.PublicKey as Uint8Array); }; export const sign = async (signParams: SignParams): Promise => { diff --git a/packages/common/src/account/types.ts b/packages/common/src/account/types.ts index 3ef7917021..fa24cb6349 100644 --- a/packages/common/src/account/types.ts +++ b/packages/common/src/account/types.ts @@ -1,22 +1,7 @@ import { KMSClient, SignCommandInput } from "@aws-sdk/client-kms"; -import { Hex } from "viem"; export type SignParams = { keyId: SignCommandInput["KeyId"]; message: string; kmsInstance: KMSClient; }; - -export type GetEthAddressFromKMSparams = { - keyId: SignCommandInput["KeyId"]; - kmsInstance: KMSClient; -}; - -export type GetPublicKeyParams = { - keyId: SignCommandInput["KeyId"]; - kmsInstance: KMSClient; -}; - -export type CreateSignatureParams = SignParams & { - address: Hex; -}; From 808fd259854bb8233c9b9ae6f15621090436ff1e Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 17:55:35 +0100 Subject: [PATCH 12/53] test: local testing --- .../common/src/account/kmsAccount.test.ts | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/common/src/account/kmsAccount.test.ts b/packages/common/src/account/kmsAccount.test.ts index ba30ee3fde..5230ee419e 100644 --- a/packages/common/src/account/kmsAccount.test.ts +++ b/packages/common/src/account/kmsAccount.test.ts @@ -1,21 +1,35 @@ import { describe, it, expect, beforeEach } from "vitest"; import { createKmsAccount } from "./kmsAccount"; -import { KMSClient } from "@aws-sdk/client-kms"; +import { CreateKeyCommand, KMSClient } from "@aws-sdk/client-kms"; import { LocalAccount, parseGwei, verifyMessage, verifyTypedData } from "viem"; describe("kmsAccount", () => { let account: LocalAccount; + let keyId: string; beforeEach(async () => { - const keyId = "PLACE"; const kmsInstance = new KMSClient({ - region: "eu-west-2", + endpoint: process.env.KMS_ENDPOINT, + region: "local", credentials: { - accessKeyId: "PLACE", - secretAccessKey: "PLACE", + accessKeyId: "AKIAXTTRUF7NU7KDMIED", + secretAccessKey: "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", }, }); + const command = new CreateKeyCommand({ + KeyUsage: "SIGN_VERIFY", + CustomerMasterKeySpec: "ECC_SECG_P256K1", + }); + + const createResponse = await kmsInstance.send(command); + + if (!createResponse.KeyMetadata || !createResponse.KeyMetadata.KeyId) { + throw new Error("key creation failed"); + } + + keyId = createResponse.KeyMetadata.KeyId; + account = await createKmsAccount(keyId, kmsInstance); }); From ef738adda362d834800b2884079440d3a4706931 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 18:25:30 +0100 Subject: [PATCH 13/53] refactor: signatureToHex --- packages/common/src/account/eth.ts | 19 ++++++++----------- packages/common/src/account/kmsAccount.ts | 8 ++++---- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/packages/common/src/account/eth.ts b/packages/common/src/account/eth.ts index 348b8c829d..015c9c85e7 100644 --- a/packages/common/src/account/eth.ts +++ b/packages/common/src/account/eth.ts @@ -3,7 +3,7 @@ import asn1 from "asn1.js"; import { utils } from "ethers"; import { sign } from "./kms"; import { SignParams } from "./types"; -import { Address, Hex, Signature, hexToSignature, isAddressEqual, toHex } from "viem"; +import { Address, Hex, isAddressEqual, signatureToHex, toHex } from "viem"; type CreateSignatureParams = SignParams & { address: Hex; @@ -73,16 +73,13 @@ export const createSignature = async ({ message, address, kmsInstance, -}: CreateSignatureParams): Promise => { +}: CreateSignatureParams): Promise => { const { r, s } = await getRS({ keyId, message, kmsInstance }); - const recoveryParam = getRecoveryParam(message, r, s, address); + const yParity = getRecoveryParam(message, r, s, address); - // REMOVE THIS - const ethersSignature = utils.joinSignature({ - r: r, - s: s, - v: recoveryParam, - }) as Hex; - - return hexToSignature(ethersSignature); + return signatureToHex({ + r, + s, + yParity, + }); }; diff --git a/packages/common/src/account/kmsAccount.ts b/packages/common/src/account/kmsAccount.ts index e77fb457bf..b657ee7914 100644 --- a/packages/common/src/account/kmsAccount.ts +++ b/packages/common/src/account/kmsAccount.ts @@ -1,5 +1,5 @@ import { KMSClient } from "@aws-sdk/client-kms"; -import { LocalAccount, hashMessage, hashTypedData, keccak256, serializeTransaction, signatureToHex } from "viem"; +import { LocalAccount, hashMessage, hashTypedData, keccak256, serializeTransaction } from "viem"; import { toAccount } from "viem/accounts"; import { getEthAddressFromKMS } from "./kms"; import { createSignature } from "./eth"; @@ -18,7 +18,7 @@ export async function createKmsAccount(keyId: string, kmsInstance: KMSClient): P address, }); - return signatureToHex(signature); + return signature; }, async signTransaction(transaction) { const unsignedTx = serializeTransaction(transaction); @@ -30,7 +30,7 @@ export async function createKmsAccount(keyId: string, kmsInstance: KMSClient): P address, }); - return signatureToHex(signature); + return signature; }, async signTypedData(typedData) { const hash = hashTypedData(typedData); @@ -41,7 +41,7 @@ export async function createKmsAccount(keyId: string, kmsInstance: KMSClient): P address, }); - return signatureToHex(signature); + return signature; }, }); From 3b1161c5fc83c68d042f73c3d1056a138070b591 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 18:41:56 +0100 Subject: [PATCH 14/53] refactor: types --- packages/common/src/account/kms.ts | 14 +++-- packages/common/src/account/kmsAccount.ts | 14 ++--- .../common/src/account/{eth.ts => sign.ts} | 52 ++++++++++++------- packages/common/src/account/types.ts | 7 --- 4 files changed, 48 insertions(+), 39 deletions(-) rename packages/common/src/account/{eth.ts => sign.ts} (61%) delete mode 100644 packages/common/src/account/types.ts diff --git a/packages/common/src/account/kms.ts b/packages/common/src/account/kms.ts index 5744ff1f44..f128652e5b 100644 --- a/packages/common/src/account/kms.ts +++ b/packages/common/src/account/kms.ts @@ -8,8 +8,7 @@ import { } from "@aws-sdk/client-kms"; import { utils } from "ethers"; import { Address } from "viem"; -import { SignParams } from "./types"; -import { getEthAddressFromPublicKey } from "./eth"; +import { getEthAddressFromPublicKey } from "./sign"; type GetPublicKeyParams = { keyId: SignCommandInput["KeyId"]; @@ -21,6 +20,12 @@ type GetEthAddressFromKMSparams = { kmsInstance: KMSClient; }; +type SignParams = { + hash: string; + keyId: SignCommandInput["KeyId"]; + kmsInstance: KMSClient; +}; + const getPublicKey = (getPublicKeyParams: GetPublicKeyParams): Promise => { const { keyId, kmsInstance } = getPublicKeyParams; const command = new GetPublicKeyCommand({ KeyId: keyId }); @@ -37,9 +42,8 @@ export const getEthAddressFromKMS = async ( return getEthAddressFromPublicKey(KMSKey.PublicKey as Uint8Array); }; -export const sign = async (signParams: SignParams): Promise => { - const { keyId, message, kmsInstance } = signParams; - const formatted = Buffer.from(utils.arrayify(message)); +export const signWithKMS = async ({ keyId, hash, kmsInstance }: SignParams): Promise => { + const formatted = Buffer.from(utils.arrayify(hash)); const command = new SignCommand({ KeyId: keyId, diff --git a/packages/common/src/account/kmsAccount.ts b/packages/common/src/account/kmsAccount.ts index b657ee7914..5e0a162e25 100644 --- a/packages/common/src/account/kmsAccount.ts +++ b/packages/common/src/account/kmsAccount.ts @@ -2,7 +2,7 @@ import { KMSClient } from "@aws-sdk/client-kms"; import { LocalAccount, hashMessage, hashTypedData, keccak256, serializeTransaction } from "viem"; import { toAccount } from "viem/accounts"; import { getEthAddressFromKMS } from "./kms"; -import { createSignature } from "./eth"; +import { sign } from "./sign"; export async function createKmsAccount(keyId: string, kmsInstance: KMSClient): Promise { const address = await getEthAddressFromKMS({ kmsInstance, keyId }); @@ -11,10 +11,10 @@ export async function createKmsAccount(keyId: string, kmsInstance: KMSClient): P address, async signMessage({ message }) { const hash = hashMessage(message); - const signature = await createSignature({ + const signature = await sign({ kmsInstance, keyId, - message: hash, + hash, address, }); @@ -23,10 +23,10 @@ export async function createKmsAccount(keyId: string, kmsInstance: KMSClient): P async signTransaction(transaction) { const unsignedTx = serializeTransaction(transaction); const hash = keccak256(unsignedTx); - const signature = await createSignature({ + const signature = await sign({ kmsInstance, keyId, - message: hash, + hash, address, }); @@ -34,10 +34,10 @@ export async function createKmsAccount(keyId: string, kmsInstance: KMSClient): P }, async signTypedData(typedData) { const hash = hashTypedData(typedData); - const signature = await createSignature({ + const signature = await sign({ kmsInstance, keyId, - message: hash, + hash, address, }); diff --git a/packages/common/src/account/eth.ts b/packages/common/src/account/sign.ts similarity index 61% rename from packages/common/src/account/eth.ts rename to packages/common/src/account/sign.ts index 015c9c85e7..1da4e4d699 100644 --- a/packages/common/src/account/eth.ts +++ b/packages/common/src/account/sign.ts @@ -1,13 +1,10 @@ // @ts-expect-error import asn1 from "asn1.js"; import { utils } from "ethers"; -import { sign } from "./kms"; -import { SignParams } from "./types"; +import { signWithKMS } from "./kms"; import { Address, Hex, isAddressEqual, signatureToHex, toHex } from "viem"; - -type CreateSignatureParams = SignParams & { - address: Hex; -}; +import { KMSClient, SignCommandInput } from "@aws-sdk/client-kms"; +import { publicKeyToAddress } from "viem/utils"; const EcdsaSigAsnParse = asn1.define("EcdsaSig", function (this: any) { this.seq().obj(this.key("r").int(), this.key("s").int()); @@ -17,8 +14,12 @@ const EcdsaPubKey = asn1.define("EcdsaPubKey", function (this: any) { this.seq().obj(this.key("algo").seq().obj(this.key("a").objid(), this.key("b").objid()), this.key("pubKey").bitstr()); }); -const getRS = async (signParams: SignParams): Promise<{ r: Hex; s: Hex }> => { - const signature = await sign(signParams); +const getRS = async (signParams: { + hash: string; + keyId: SignCommandInput["KeyId"]; + kmsInstance: KMSClient; +}): Promise<{ r: Hex; s: Hex }> => { + const signature = await signWithKMS(signParams); if (signature.Signature === undefined) { throw new Error("Signature is undefined."); @@ -42,11 +43,10 @@ const getRS = async (signParams: SignParams): Promise<{ r: Hex; s: Hex }> => { }; }; -const getRecoveryParam = (msg: string, r: string, s: string, expectedEthAddr: Hex): number => { - const formatted = msg; +const getRecoveryParam = (message: string, r: Hex, s: Hex, expectedEthAddr: Hex): number => { let recoveryParam: number; for (recoveryParam = 0; recoveryParam <= 1; recoveryParam++) { - const address = utils.recoverAddress(formatted, { + const address = utils.recoverAddress(message, { r, s, recoveryParam, @@ -68,18 +68,30 @@ export const getEthAddressFromPublicKey = (publicKey: Uint8Array): Address => { return address; }; -export const createSignature = async ({ - keyId, - message, - address, - kmsInstance, -}: CreateSignatureParams): Promise => { - const { r, s } = await getRS({ keyId, message, kmsInstance }); - const yParity = getRecoveryParam(message, r, s, address); +type SignParameters = { + hash: string; + keyId: SignCommandInput["KeyId"]; + kmsInstance: KMSClient; + address: Hex; +}; + +type SignReturnType = Hex; + +/** + * @description Signs a hash with a given KMS key. + * + * @param hash The hash to sign. + * + * @returns The signature. + */ +export const sign = async ({ hash, address, keyId, kmsInstance }: SignParameters): Promise => { + const { r, s } = await getRS({ keyId, hash, kmsInstance }); + const recovery = getRecoveryParam(hash, r, s, address); return signatureToHex({ r, s, - yParity, + v: recovery ? 28n : 27n, + yParity: recovery, }); }; diff --git a/packages/common/src/account/types.ts b/packages/common/src/account/types.ts deleted file mode 100644 index fa24cb6349..0000000000 --- a/packages/common/src/account/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { KMSClient, SignCommandInput } from "@aws-sdk/client-kms"; - -export type SignParams = { - keyId: SignCommandInput["KeyId"]; - message: string; - kmsInstance: KMSClient; -}; From aeacfd91411387e23253e28bc5c18f84ed72248c Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 18:50:55 +0100 Subject: [PATCH 15/53] refactor: compute public key --- packages/common/src/account/sign.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/common/src/account/sign.ts b/packages/common/src/account/sign.ts index 1da4e4d699..8953cf359c 100644 --- a/packages/common/src/account/sign.ts +++ b/packages/common/src/account/sign.ts @@ -1,15 +1,18 @@ -// @ts-expect-error +// @ts-expect-error types import asn1 from "asn1.js"; import { utils } from "ethers"; import { signWithKMS } from "./kms"; import { Address, Hex, isAddressEqual, signatureToHex, toHex } from "viem"; import { KMSClient, SignCommandInput } from "@aws-sdk/client-kms"; import { publicKeyToAddress } from "viem/utils"; +import { computePublicKey } from "ethers/lib/utils"; +// eslint-disable-next-line @typescript-eslint/no-explicit-any const EcdsaSigAsnParse = asn1.define("EcdsaSig", function (this: any) { this.seq().obj(this.key("r").int(), this.key("s").int()); }); +// eslint-disable-next-line @typescript-eslint/no-explicit-any const EcdsaPubKey = asn1.define("EcdsaPubKey", function (this: any) { this.seq().obj(this.key("algo").seq().obj(this.key("a").objid(), this.key("b").objid()), this.key("pubKey").bitstr()); }); @@ -51,10 +54,10 @@ const getRecoveryParam = (message: string, r: Hex, s: Hex, expectedEthAddr: Hex) s, recoveryParam, }) as Hex; - if (!isAddressEqual(address, expectedEthAddr)) { - continue; + + if (isAddressEqual(address, expectedEthAddr)) { + return recoveryParam; } - return recoveryParam; } throw new Error("Failed to calculate recovery param"); }; @@ -62,9 +65,11 @@ const getRecoveryParam = (message: string, r: Hex, s: Hex, expectedEthAddr: Hex) export const getEthAddressFromPublicKey = (publicKey: Uint8Array): Address => { const res = EcdsaPubKey.decode(Buffer.from(publicKey)); - const pubKeyBuffer: Buffer = res.pubKey.data; + const publicKeyBuffer: Buffer = res.pubKey.data; + + const publicKeyString = computePublicKey(publicKeyBuffer); + const address = publicKeyToAddress(publicKeyString as Hex); - const address = utils.computeAddress(pubKeyBuffer) as Address; return address; }; From 874a9f433c3eb0339f054c6841011b916f056bcb Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 19:07:07 +0100 Subject: [PATCH 16/53] refactor: custom arrayify util --- packages/common/src/account/kms.ts | 18 ++++++++++++++---- packages/common/src/account/sign.ts | 4 ++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/common/src/account/kms.ts b/packages/common/src/account/kms.ts index f128652e5b..2a8e01662a 100644 --- a/packages/common/src/account/kms.ts +++ b/packages/common/src/account/kms.ts @@ -6,8 +6,7 @@ import { SignCommandInput, SignCommandOutput, } from "@aws-sdk/client-kms"; -import { utils } from "ethers"; -import { Address } from "viem"; +import { Address, Hex } from "viem"; import { getEthAddressFromPublicKey } from "./sign"; type GetPublicKeyParams = { @@ -21,7 +20,7 @@ type GetEthAddressFromKMSparams = { }; type SignParams = { - hash: string; + hash: Hex; keyId: SignCommandInput["KeyId"]; kmsInstance: KMSClient; }; @@ -42,8 +41,19 @@ export const getEthAddressFromKMS = async ( return getEthAddressFromPublicKey(KMSKey.PublicKey as Uint8Array); }; +function arrayify(value: Hex): Uint8Array { + const hex = value.substring(2); + + const result = []; + for (let i = 0; i < hex.length; i += 2) { + result.push(parseInt(hex.substring(i, i + 2), 16)); + } + + return new Uint8Array(result); +} + export const signWithKMS = async ({ keyId, hash, kmsInstance }: SignParams): Promise => { - const formatted = Buffer.from(utils.arrayify(hash)); + const formatted = Buffer.from(arrayify(hash)); const command = new SignCommand({ KeyId: keyId, diff --git a/packages/common/src/account/sign.ts b/packages/common/src/account/sign.ts index 8953cf359c..80283fbc77 100644 --- a/packages/common/src/account/sign.ts +++ b/packages/common/src/account/sign.ts @@ -18,7 +18,7 @@ const EcdsaPubKey = asn1.define("EcdsaPubKey", function (this: any) { }); const getRS = async (signParams: { - hash: string; + hash: Hex; keyId: SignCommandInput["KeyId"]; kmsInstance: KMSClient; }): Promise<{ r: Hex; s: Hex }> => { @@ -74,7 +74,7 @@ export const getEthAddressFromPublicKey = (publicKey: Uint8Array): Address => { }; type SignParameters = { - hash: string; + hash: Hex; keyId: SignCommandInput["KeyId"]; kmsInstance: KMSClient; address: Hex; From b784718dab47c99f5dd27236165d14a6ded09923 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 19:16:40 +0100 Subject: [PATCH 17/53] refactor: recovery param --- packages/common/src/account/sign.ts | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/common/src/account/sign.ts b/packages/common/src/account/sign.ts index 80283fbc77..af1533e30c 100644 --- a/packages/common/src/account/sign.ts +++ b/packages/common/src/account/sign.ts @@ -1,11 +1,10 @@ // @ts-expect-error types import asn1 from "asn1.js"; -import { utils } from "ethers"; -import { signWithKMS } from "./kms"; import { Address, Hex, isAddressEqual, signatureToHex, toHex } from "viem"; import { KMSClient, SignCommandInput } from "@aws-sdk/client-kms"; -import { publicKeyToAddress } from "viem/utils"; +import { publicKeyToAddress, recoverAddress } from "viem/utils"; import { computePublicKey } from "ethers/lib/utils"; +import { signWithKMS } from "./kms"; // eslint-disable-next-line @typescript-eslint/no-explicit-any const EcdsaSigAsnParse = asn1.define("EcdsaSig", function (this: any) { @@ -46,17 +45,20 @@ const getRS = async (signParams: { }; }; -const getRecoveryParam = (message: string, r: Hex, s: Hex, expectedEthAddr: Hex): number => { - let recoveryParam: number; - for (recoveryParam = 0; recoveryParam <= 1; recoveryParam++) { - const address = utils.recoverAddress(message, { +const getRecovery = async (hash: Hex, r: Hex, s: Hex, expectedAddress: Hex): Promise => { + let recovery: number; + for (recovery = 0; recovery <= 1; recovery++) { + const signature = signatureToHex({ r, s, - recoveryParam, - }) as Hex; + v: recovery ? 28n : 27n, + yParity: recovery, + }); + + const address = await recoverAddress({ hash, signature }); - if (isAddressEqual(address, expectedEthAddr)) { - return recoveryParam; + if (isAddressEqual(address, expectedAddress)) { + return recovery; } } throw new Error("Failed to calculate recovery param"); @@ -91,7 +93,7 @@ type SignReturnType = Hex; */ export const sign = async ({ hash, address, keyId, kmsInstance }: SignParameters): Promise => { const { r, s } = await getRS({ keyId, hash, kmsInstance }); - const recovery = getRecoveryParam(hash, r, s, address); + const recovery = await getRecovery(hash, r, s, address); return signatureToHex({ r, From 6b70a3d207eff07ec3bdb0f85483cda9a72a21bb Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 19:17:51 +0100 Subject: [PATCH 18/53] refactor: rename variable --- packages/common/src/account/sign.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/common/src/account/sign.ts b/packages/common/src/account/sign.ts index af1533e30c..5094c56346 100644 --- a/packages/common/src/account/sign.ts +++ b/packages/common/src/account/sign.ts @@ -69,8 +69,8 @@ export const getEthAddressFromPublicKey = (publicKey: Uint8Array): Address => { const publicKeyBuffer: Buffer = res.pubKey.data; - const publicKeyString = computePublicKey(publicKeyBuffer); - const address = publicKeyToAddress(publicKeyString as Hex); + const publicKeyHex = computePublicKey(publicKeyBuffer) as Hex; + const address = publicKeyToAddress(publicKeyHex); return address; }; From 16e4996bbaf02c79aeaebcad313827769d90a603 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 19:26:34 +0100 Subject: [PATCH 19/53] refactor: ethers dependency --- packages/common/package.json | 2 +- packages/common/src/account/sign.ts | 2 +- pnpm-lock.yaml | 26 ++++++++++++++++++++++---- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/common/package.json b/packages/common/package.json index c4f13142ef..995aa3c678 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -58,11 +58,11 @@ }, "dependencies": { "@aws-sdk/client-kms": "*", + "@ethersproject/signing-key": "^5.7.0", "@latticexyz/schema-type": "workspace:*", "@solidity-parser/parser": "^0.16.0", "asn1.js": "^5.4.1", "debug": "^4.3.4", - "ethers": "^5.7.2", "execa": "^7.0.0", "p-queue": "^7.4.1", "p-retry": "^5.1.2", diff --git a/packages/common/src/account/sign.ts b/packages/common/src/account/sign.ts index 5094c56346..cb78b22c66 100644 --- a/packages/common/src/account/sign.ts +++ b/packages/common/src/account/sign.ts @@ -3,7 +3,7 @@ import asn1 from "asn1.js"; import { Address, Hex, isAddressEqual, signatureToHex, toHex } from "viem"; import { KMSClient, SignCommandInput } from "@aws-sdk/client-kms"; import { publicKeyToAddress, recoverAddress } from "viem/utils"; -import { computePublicKey } from "ethers/lib/utils"; +import { computePublicKey } from "@ethersproject/signing-key"; import { signWithKMS } from "./kms"; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a2f30c1ec9..4b6c8071f1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -281,6 +281,9 @@ importers: '@aws-sdk/client-kms': specifier: '*' version: 3.556.0 + '@ethersproject/signing-key': + specifier: ^5.7.0 + version: 5.7.0 '@latticexyz/schema-type': specifier: workspace:* version: link:../schema-type @@ -293,9 +296,6 @@ importers: debug: specifier: ^4.3.4 version: 4.3.4 - ethers: - specifier: ^5.7.2 - version: 5.7.2 execa: specifier: ^7.0.0 version: 7.0.0 @@ -1249,7 +1249,7 @@ packages: '@arktype/util': 0.0.23 '@typescript/analyze-trace': 0.10.1 '@typescript/vfs': 1.5.0 - arktype: 1.0.29-alpha + arktype: 2.0.0-dev.7 typescript: 5.4.2 transitivePeerDependencies: - supports-color @@ -1259,6 +1259,12 @@ packages: resolution: {integrity: sha512-f3p8apUMOTl6KlFNTrOX6twQJDeSdSmtYPVca7SaQjUUTAxfUlaROY1/i1C3y2llYDrrQBEbsggk8k2Oxxgrvw==} dev: true + /@arktype/schema@0.0.7: + resolution: {integrity: sha512-awo14Oi98bn6pEgN7soiXvD+mQzidLgzABVO7Tpbw6xtU7+XRWp6JucSEmWLASC+fcoK0QSJxdoC+rm3iIKarQ==} + dependencies: + '@arktype/util': 0.0.33 + dev: true + /@arktype/util@0.0.23: resolution: {integrity: sha512-MwtDGjbgfXWxlExjIL78HvPlWOma1XFEcDd3VWuCPMt/f+3TR7fXyiGFNlIYZGOYdOIU7qgjaE8dmbd6jdJa3Q==} dev: true @@ -1267,6 +1273,10 @@ packages: resolution: {integrity: sha512-fDTBSVzxLj9k1ZjinkawmaQdcXFKMBVK8c+vqMPxwoa94mPMZxBo84yQcqyFVcIcWIkg6qQQmH1ozyT4nqFT/g==} dev: false + /@arktype/util@0.0.33: + resolution: {integrity: sha512-MYbrLHf0tVYjxI84m0mMRISmKKVoPzv25B1/X05nePUcyPqROoDBn+hYhHpB0GqnJZQOr8UG1CyMuxjFeVbTNg==} + dev: true + /@aws-crypto/ie11-detection@3.0.0: resolution: {integrity: sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==} dependencies: @@ -5201,6 +5211,14 @@ packages: /arktype@1.0.29-alpha: resolution: {integrity: sha512-glMLgVhIQRSkR3tymiS+POAcWVJH09sfrgic0jHnyFL8BlhHAJZX2BzdImU9zYr1y9NBqy+U93ZNrRTHXsKRDw==} + dev: false + + /arktype@2.0.0-dev.7: + resolution: {integrity: sha512-TeehK+ExNsvqNoEccNOMs73LcNwR9+gX9pQsoCIvZfuxrQ24nB5MUQGweAAuNSwVX7GUUU9Ad0BWGnsvD8ST+g==} + dependencies: + '@arktype/schema': 0.0.7 + '@arktype/util': 0.0.33 + dev: true /array-includes@3.1.6: resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} From 3f069296a7e1ef56521ba06c26b7281ff3825577 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 19:29:10 +0100 Subject: [PATCH 20/53] refactor: package version --- packages/common/package.json | 2 +- pnpm-lock.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/common/package.json b/packages/common/package.json index 995aa3c678..de12a9b3fc 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -57,7 +57,7 @@ "test:ci": "pnpm run test" }, "dependencies": { - "@aws-sdk/client-kms": "*", + "@aws-sdk/client-kms": "^3.556.0", "@ethersproject/signing-key": "^5.7.0", "@latticexyz/schema-type": "workspace:*", "@solidity-parser/parser": "^0.16.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4b6c8071f1..ee03300f1a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -279,7 +279,7 @@ importers: packages/common: dependencies: '@aws-sdk/client-kms': - specifier: '*' + specifier: ^3.556.0 version: 3.556.0 '@ethersproject/signing-key': specifier: ^5.7.0 From c58feea00ed2ca17c1aac40c373f756a1ee1b94e Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Fri, 19 Apr 2024 19:59:01 +0100 Subject: [PATCH 21/53] refactor: rename account --- packages/common/src/account/kmsAccount.test.ts | 4 ++-- .../common/src/account/{kmsAccount.ts => kmsToAccount.ts} | 7 ++++++- packages/common/src/account/sign.ts | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) rename packages/common/src/account/{kmsAccount.ts => kmsToAccount.ts} (84%) diff --git a/packages/common/src/account/kmsAccount.test.ts b/packages/common/src/account/kmsAccount.test.ts index 5230ee419e..9d40e15751 100644 --- a/packages/common/src/account/kmsAccount.test.ts +++ b/packages/common/src/account/kmsAccount.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from "vitest"; -import { createKmsAccount } from "./kmsAccount"; +import { kmsToAccount } from "./kmsToAccount"; import { CreateKeyCommand, KMSClient } from "@aws-sdk/client-kms"; import { LocalAccount, parseGwei, verifyMessage, verifyTypedData } from "viem"; @@ -30,7 +30,7 @@ describe("kmsAccount", () => { keyId = createResponse.KeyMetadata.KeyId; - account = await createKmsAccount(keyId, kmsInstance); + account = await kmsToAccount(keyId, kmsInstance); }); it("signMessage", async () => { diff --git a/packages/common/src/account/kmsAccount.ts b/packages/common/src/account/kmsToAccount.ts similarity index 84% rename from packages/common/src/account/kmsAccount.ts rename to packages/common/src/account/kmsToAccount.ts index 5e0a162e25..90db6670eb 100644 --- a/packages/common/src/account/kmsAccount.ts +++ b/packages/common/src/account/kmsToAccount.ts @@ -4,7 +4,12 @@ import { toAccount } from "viem/accounts"; import { getEthAddressFromKMS } from "./kms"; import { sign } from "./sign"; -export async function createKmsAccount(keyId: string, kmsInstance: KMSClient): Promise { +/** + * @description Creates an Account from a KMS key and instance. + * + * @returns A Local Account. + */ +export async function kmsToAccount(keyId: string, kmsInstance: KMSClient): Promise { const address = await getEthAddressFromKMS({ kmsInstance, keyId }); const account = toAccount({ diff --git a/packages/common/src/account/sign.ts b/packages/common/src/account/sign.ts index cb78b22c66..1df9939730 100644 --- a/packages/common/src/account/sign.ts +++ b/packages/common/src/account/sign.ts @@ -1,8 +1,8 @@ // @ts-expect-error types import asn1 from "asn1.js"; import { Address, Hex, isAddressEqual, signatureToHex, toHex } from "viem"; -import { KMSClient, SignCommandInput } from "@aws-sdk/client-kms"; import { publicKeyToAddress, recoverAddress } from "viem/utils"; +import { KMSClient, SignCommandInput } from "@aws-sdk/client-kms"; import { computePublicKey } from "@ethersproject/signing-key"; import { signWithKMS } from "./kms"; From a9405dd79e78c868fedd7d580c1c0c67aff32544 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 22 Apr 2024 13:44:26 +0100 Subject: [PATCH 22/53] refactor: filename --- .../src/account/{kmsAccount.test.ts => kmsToAccount.test.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/common/src/account/{kmsAccount.test.ts => kmsToAccount.test.ts} (100%) diff --git a/packages/common/src/account/kmsAccount.test.ts b/packages/common/src/account/kmsToAccount.test.ts similarity index 100% rename from packages/common/src/account/kmsAccount.test.ts rename to packages/common/src/account/kmsToAccount.test.ts From bafcdb88751cfa0535b914dbfaa8cfd121e7b398 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 22 Apr 2024 13:47:23 +0100 Subject: [PATCH 23/53] refactor: fromHex --- packages/common/src/account/kms.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/packages/common/src/account/kms.ts b/packages/common/src/account/kms.ts index 2a8e01662a..25c277dd67 100644 --- a/packages/common/src/account/kms.ts +++ b/packages/common/src/account/kms.ts @@ -6,7 +6,7 @@ import { SignCommandInput, SignCommandOutput, } from "@aws-sdk/client-kms"; -import { Address, Hex } from "viem"; +import { Address, Hex, fromHex } from "viem"; import { getEthAddressFromPublicKey } from "./sign"; type GetPublicKeyParams = { @@ -41,19 +41,8 @@ export const getEthAddressFromKMS = async ( return getEthAddressFromPublicKey(KMSKey.PublicKey as Uint8Array); }; -function arrayify(value: Hex): Uint8Array { - const hex = value.substring(2); - - const result = []; - for (let i = 0; i < hex.length; i += 2) { - result.push(parseInt(hex.substring(i, i + 2), 16)); - } - - return new Uint8Array(result); -} - export const signWithKMS = async ({ keyId, hash, kmsInstance }: SignParams): Promise => { - const formatted = Buffer.from(arrayify(hash)); + const formatted = Buffer.from(fromHex(hash, "bytes")); const command = new SignCommand({ KeyId: keyId, From 14b886afd8ce37e6477c854431ecff54f63dfeb3 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 22 Apr 2024 13:53:48 +0100 Subject: [PATCH 24/53] test: attempt to run local kms --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b2eb2e308a..11cc02efe8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,6 +17,10 @@ jobs: - 5432:5432 # needed because the postgres container does not provide a healthcheck options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + local-kms: + image: nsmithuk/local-kms + ports: + - 8080:8080 steps: - name: Checkout uses: actions/checkout@v3 From ec0e3a3ea93d383bd854dbbc94e1ee1af9a1e579 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 22 Apr 2024 14:04:26 +0100 Subject: [PATCH 25/53] hardcode endpoint --- packages/common/src/account/kmsToAccount.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/common/src/account/kmsToAccount.test.ts b/packages/common/src/account/kmsToAccount.test.ts index 9d40e15751..ef29d3eaea 100644 --- a/packages/common/src/account/kmsToAccount.test.ts +++ b/packages/common/src/account/kmsToAccount.test.ts @@ -9,7 +9,7 @@ describe("kmsAccount", () => { beforeEach(async () => { const kmsInstance = new KMSClient({ - endpoint: process.env.KMS_ENDPOINT, + endpoint: "http://localhost:8080", region: "local", credentials: { accessKeyId: "AKIAXTTRUF7NU7KDMIED", From 4adf782fcb15ecf1be4eb32f60fc3d8f3963c390 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 22 Apr 2024 14:06:18 +0100 Subject: [PATCH 26/53] Revert "hardcode endpoint" This reverts commit ec0e3a3ea93d383bd854dbbc94e1ee1af9a1e579. --- packages/common/src/account/kmsToAccount.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/common/src/account/kmsToAccount.test.ts b/packages/common/src/account/kmsToAccount.test.ts index ef29d3eaea..9d40e15751 100644 --- a/packages/common/src/account/kmsToAccount.test.ts +++ b/packages/common/src/account/kmsToAccount.test.ts @@ -9,7 +9,7 @@ describe("kmsAccount", () => { beforeEach(async () => { const kmsInstance = new KMSClient({ - endpoint: "http://localhost:8080", + endpoint: process.env.KMS_ENDPOINT, region: "local", credentials: { accessKeyId: "AKIAXTTRUF7NU7KDMIED", From a92a8a79cb31496b997a691139f266c8603cc997 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 22 Apr 2024 14:06:40 +0100 Subject: [PATCH 27/53] fix: env variable --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 11cc02efe8..2692432e37 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,6 +44,7 @@ jobs: if: steps.check_changes.outputs.changes_outside_docs env: DATABASE_URL: "postgres://postgres@localhost:5432/postgres" + KMS_ENDPOINT: "http://localhost:8080" run: pnpm test:ci - name: Generate gas reports From c73c834143a3b6dde9b46412fef6093f991f1876 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 22 Apr 2024 14:16:13 +0100 Subject: [PATCH 28/53] test: beforeAll --- packages/common/src/account/kmsToAccount.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/common/src/account/kmsToAccount.test.ts b/packages/common/src/account/kmsToAccount.test.ts index 9d40e15751..07bc648e27 100644 --- a/packages/common/src/account/kmsToAccount.test.ts +++ b/packages/common/src/account/kmsToAccount.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, beforeEach } from "vitest"; +import { describe, it, expect, beforeAll } from "vitest"; import { kmsToAccount } from "./kmsToAccount"; import { CreateKeyCommand, KMSClient } from "@aws-sdk/client-kms"; import { LocalAccount, parseGwei, verifyMessage, verifyTypedData } from "viem"; @@ -7,7 +7,7 @@ describe("kmsAccount", () => { let account: LocalAccount; let keyId: string; - beforeEach(async () => { + beforeAll(async () => { const kmsInstance = new KMSClient({ endpoint: process.env.KMS_ENDPOINT, region: "local", From 417b1bbe28a1273e547b9b8b385926bb7003cde3 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 22 Apr 2024 14:35:10 +0100 Subject: [PATCH 29/53] feat: optional dependencies --- packages/common/package.json | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/common/package.json b/packages/common/package.json index de12a9b3fc..198e7907ff 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -57,11 +57,9 @@ "test:ci": "pnpm run test" }, "dependencies": { - "@aws-sdk/client-kms": "^3.556.0", "@ethersproject/signing-key": "^5.7.0", "@latticexyz/schema-type": "workspace:*", "@solidity-parser/parser": "^0.16.0", - "asn1.js": "^5.4.1", "debug": "^4.3.4", "execa": "^7.0.0", "p-queue": "^7.4.1", @@ -75,5 +73,17 @@ "@types/node": "^18.15.11", "tsup": "^6.7.0", "vitest": "0.34.6" + }, + "peerDependencies": { + "@aws-sdk/client-kms": "^3.556.0", + "asn1.js": "^5.4.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-kms": { + "optional": true + }, + "asn1.js": { + "optional": true + } } } From 8b7dbdcb03f9749a508dbf7bbcab9d992f020819 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 22 Apr 2024 14:54:21 +0100 Subject: [PATCH 30/53] refactor: remove ethers dependency --- packages/common/package.json | 1 - packages/common/src/account/sign.ts | 3 +-- pnpm-lock.yaml | 3 --- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/common/package.json b/packages/common/package.json index 198e7907ff..9a1660850b 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -57,7 +57,6 @@ "test:ci": "pnpm run test" }, "dependencies": { - "@ethersproject/signing-key": "^5.7.0", "@latticexyz/schema-type": "workspace:*", "@solidity-parser/parser": "^0.16.0", "debug": "^4.3.4", diff --git a/packages/common/src/account/sign.ts b/packages/common/src/account/sign.ts index 1df9939730..cdaca736e0 100644 --- a/packages/common/src/account/sign.ts +++ b/packages/common/src/account/sign.ts @@ -3,7 +3,6 @@ import asn1 from "asn1.js"; import { Address, Hex, isAddressEqual, signatureToHex, toHex } from "viem"; import { publicKeyToAddress, recoverAddress } from "viem/utils"; import { KMSClient, SignCommandInput } from "@aws-sdk/client-kms"; -import { computePublicKey } from "@ethersproject/signing-key"; import { signWithKMS } from "./kms"; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -69,7 +68,7 @@ export const getEthAddressFromPublicKey = (publicKey: Uint8Array): Address => { const publicKeyBuffer: Buffer = res.pubKey.data; - const publicKeyHex = computePublicKey(publicKeyBuffer) as Hex; + const publicKeyHex = toHex(publicKeyBuffer); const address = publicKeyToAddress(publicKeyHex); return address; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee03300f1a..4e7a2bba44 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -281,9 +281,6 @@ importers: '@aws-sdk/client-kms': specifier: ^3.556.0 version: 3.556.0 - '@ethersproject/signing-key': - specifier: ^5.7.0 - version: 5.7.0 '@latticexyz/schema-type': specifier: workspace:* version: link:../schema-type From 6a87377330ad4d9c46edbd0da0d66ba41a61a00f Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 22 Apr 2024 15:05:36 +0100 Subject: [PATCH 31/53] refactor: export, rename, move files --- .../kmsToAccount.test.ts => createKmsAccount.test.ts} | 6 +++--- .../src/{account/kmsToAccount.ts => createKmsAccount.ts} | 6 +++--- packages/common/src/index.ts | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) rename packages/common/src/{account/kmsToAccount.test.ts => createKmsAccount.test.ts} (94%) rename packages/common/src/{account/kmsToAccount.ts => createKmsAccount.ts} (85%) diff --git a/packages/common/src/account/kmsToAccount.test.ts b/packages/common/src/createKmsAccount.test.ts similarity index 94% rename from packages/common/src/account/kmsToAccount.test.ts rename to packages/common/src/createKmsAccount.test.ts index 07bc648e27..3d12b6506e 100644 --- a/packages/common/src/account/kmsToAccount.test.ts +++ b/packages/common/src/createKmsAccount.test.ts @@ -1,9 +1,9 @@ import { describe, it, expect, beforeAll } from "vitest"; -import { kmsToAccount } from "./kmsToAccount"; +import { createKmsAccount } from "./createKmsAccount"; import { CreateKeyCommand, KMSClient } from "@aws-sdk/client-kms"; import { LocalAccount, parseGwei, verifyMessage, verifyTypedData } from "viem"; -describe("kmsAccount", () => { +describe("createKmsAccount", () => { let account: LocalAccount; let keyId: string; @@ -30,7 +30,7 @@ describe("kmsAccount", () => { keyId = createResponse.KeyMetadata.KeyId; - account = await kmsToAccount(keyId, kmsInstance); + account = await createKmsAccount(keyId, kmsInstance); }); it("signMessage", async () => { diff --git a/packages/common/src/account/kmsToAccount.ts b/packages/common/src/createKmsAccount.ts similarity index 85% rename from packages/common/src/account/kmsToAccount.ts rename to packages/common/src/createKmsAccount.ts index 90db6670eb..a2c55dcd9e 100644 --- a/packages/common/src/account/kmsToAccount.ts +++ b/packages/common/src/createKmsAccount.ts @@ -1,15 +1,15 @@ import { KMSClient } from "@aws-sdk/client-kms"; import { LocalAccount, hashMessage, hashTypedData, keccak256, serializeTransaction } from "viem"; import { toAccount } from "viem/accounts"; -import { getEthAddressFromKMS } from "./kms"; -import { sign } from "./sign"; +import { getEthAddressFromKMS } from "./account/kms"; +import { sign } from "./account/sign"; /** * @description Creates an Account from a KMS key and instance. * * @returns A Local Account. */ -export async function kmsToAccount(keyId: string, kmsInstance: KMSClient): Promise { +export async function createKmsAccount(keyId: string, kmsInstance: KMSClient): Promise { const address = await getEthAddressFromKMS({ kmsInstance, keyId }); const account = toAccount({ diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 0da5bb7794..384e63b476 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -1,6 +1,7 @@ export * from "./common"; export * from "./createBenchmark"; export * from "./createBurnerAccount"; +export * from "./createKmsAccount"; export * from "./createNonceManager"; export * from "./getContract"; export * from "./getBurnerPrivateKey"; From 9b278f2928ef8432b3ad06471d2857c0b0ec37ef Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 22 Apr 2024 15:10:29 +0100 Subject: [PATCH 32/53] feat(cli): KMS deployer --- packages/cli/package.json | 4 ++++ packages/cli/src/runDeploy.ts | 18 ++++++++++++++++-- packages/world/ts/config/v2/defaults.ts | 1 + packages/world/ts/config/v2/input.ts | 2 ++ packages/world/ts/config/v2/output.ts | 2 ++ pnpm-lock.yaml | 6 ++++++ 6 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 80cedb8da2..f39fee568c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -80,5 +80,9 @@ "tsup": "^6.7.0", "tsx": "^3.12.6", "vitest": "0.34.6" + }, + "peerDependencies": { + "@aws-sdk/client-kms": "^3.556.0", + "asn1.js": "^5.4.1" } } diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 07a6868307..e9d2478452 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -3,7 +3,6 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { InferredOptionTypes, Options } from "yargs"; import { deploy } from "./deploy/deploy"; import { createWalletClient, http, Hex, isHex } from "viem"; -import { privateKeyToAccount } from "viem/accounts"; import { loadConfig } from "@latticexyz/config/node"; import { World as WorldConfig } from "@latticexyz/world"; import { worldToV1 } from "@latticexyz/world/config/v2"; @@ -15,6 +14,8 @@ import { getChainId } from "viem/actions"; import { postDeploy } from "./utils/postDeploy"; import { WorldDeploy } from "./deploy/common"; import { build } from "./build"; +import { createKmsAccount } from "@latticexyz/common"; +import { KMSClient } from "@aws-sdk/client-kms"; export const deployOptions = { configPath: { type: "string", desc: "Path to the MUD config file" }, @@ -89,6 +90,19 @@ in your contracts directory to use the default anvil private key.`, const resolvedConfig = resolveConfig({ config, forgeSourceDir: srcDir, forgeOutDir: outDir }); + const kmsInstance = new KMSClient({ + endpoint: process.env.KMS_ENDPOINT, + region: "local", + credentials: { + accessKeyId: "", + secretAccessKey: "", + }, + }); + + const keyId = ""; + + const account = await createKmsAccount(keyId, kmsInstance); + const client = createWalletClient({ transport: http(rpc, { batch: opts.rpcBatch @@ -98,7 +112,7 @@ in your contracts directory to use the default anvil private key.`, } : undefined, }), - account: privateKeyToAccount(privateKey), + account, }); console.log("Deploying from", client.account.address); diff --git a/packages/world/ts/config/v2/defaults.ts b/packages/world/ts/config/v2/defaults.ts index 034a031e25..f9ab5f7a3c 100644 --- a/packages/world/ts/config/v2/defaults.ts +++ b/packages/world/ts/config/v2/defaults.ts @@ -20,6 +20,7 @@ export const DEPLOY_DEFAULTS = { deploysDirectory: "./deploys", worldsFile: "./worlds.json", useProxy: false, + useKms: false, } as const; export type DEPLOY_DEFAULTS = typeof DEPLOY_DEFAULTS; diff --git a/packages/world/ts/config/v2/input.ts b/packages/world/ts/config/v2/input.ts index 3f6bf09baa..9974e4a3ea 100644 --- a/packages/world/ts/config/v2/input.ts +++ b/packages/world/ts/config/v2/input.ts @@ -42,6 +42,8 @@ export type CodegenInput = { worldImportPath?: string; /** Deploy the World as an upgradable proxy */ useProxy?: boolean; + /** Deploy the World using KMS */ + useKms?: boolean; }; export type WorldInput = evaluate< diff --git a/packages/world/ts/config/v2/output.ts b/packages/world/ts/config/v2/output.ts index 980fb76b66..f3ef179118 100644 --- a/packages/world/ts/config/v2/output.ts +++ b/packages/world/ts/config/v2/output.ts @@ -43,6 +43,8 @@ export type Deploy = { readonly worldsFile: string; /** Deploy the World as an upgradable proxy */ readonly useProxy: boolean; + /** Deploy the World using KMS */ + readonly useKms: boolean; }; export type Codegen = { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4e7a2bba44..df17adb13e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,6 +130,9 @@ importers: packages/cli: dependencies: + '@aws-sdk/client-kms': + specifier: ^3.556.0 + version: 3.556.0 '@ethersproject/abi': specifier: ^5.7.0 version: 5.7.0 @@ -175,6 +178,9 @@ importers: '@latticexyz/world-modules': specifier: workspace:* version: link:../world-modules + asn1.js: + specifier: ^5.4.1 + version: 5.4.1 chalk: specifier: ^5.0.1 version: 5.2.0 From c86d6ae21dc88fbca08cd9eca0219abe02ae2a93 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 22 Apr 2024 15:20:57 +0100 Subject: [PATCH 33/53] feat: use flag --- packages/cli/src/runDeploy.ts | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index e9d2478452..435f36cc75 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -3,6 +3,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { InferredOptionTypes, Options } from "yargs"; import { deploy } from "./deploy/deploy"; import { createWalletClient, http, Hex, isHex } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; import { loadConfig } from "@latticexyz/config/node"; import { World as WorldConfig } from "@latticexyz/world"; import { worldToV1 } from "@latticexyz/world/config/v2"; @@ -90,18 +91,24 @@ in your contracts directory to use the default anvil private key.`, const resolvedConfig = resolveConfig({ config, forgeSourceDir: srcDir, forgeOutDir: outDir }); - const kmsInstance = new KMSClient({ - endpoint: process.env.KMS_ENDPOINT, - region: "local", - credentials: { - accessKeyId: "", - secretAccessKey: "", - }, - }); - - const keyId = ""; - - const account = await createKmsAccount(keyId, kmsInstance); + const accessKeyId = "PLACEHOLDER"; + const secretAccessKey = "PLACEHOLDER"; + const keyId = "PLACEHOLDER"; + const endpoint = "PLACEHOLDER"; + + const account = configV2.deploy.useKms + ? await createKmsAccount( + keyId, + new KMSClient({ + endpoint, + region: "local", + credentials: { + accessKeyId, + secretAccessKey, + }, + }), + ) + : privateKeyToAccount(privateKey); const client = createWalletClient({ transport: http(rpc, { From ed3919d4059e4f62ad673d6ebfb0217e2c059422 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Mon, 22 Apr 2024 15:32:27 +0100 Subject: [PATCH 34/53] refactor: types --- packages/cli/src/runDeploy.ts | 18 +++++------------- packages/world/ts/config/v2/defaults.ts | 2 +- packages/world/ts/config/v2/input.ts | 10 +++++++++- packages/world/ts/config/v2/output.ts | 10 +++++++++- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 435f36cc75..c61bc9a4e4 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -91,21 +91,13 @@ in your contracts directory to use the default anvil private key.`, const resolvedConfig = resolveConfig({ config, forgeSourceDir: srcDir, forgeOutDir: outDir }); - const accessKeyId = "PLACEHOLDER"; - const secretAccessKey = "PLACEHOLDER"; - const keyId = "PLACEHOLDER"; - const endpoint = "PLACEHOLDER"; - - const account = configV2.deploy.useKms + const account = configV2.deploy.kms ? await createKmsAccount( - keyId, + configV2.deploy.kms.keyId, new KMSClient({ - endpoint, - region: "local", - credentials: { - accessKeyId, - secretAccessKey, - }, + endpoint: configV2.deploy.kms.endpoint, + region: configV2.deploy.kms.region, + credentials: configV2.deploy.kms.credentials, }), ) : privateKeyToAccount(privateKey); diff --git a/packages/world/ts/config/v2/defaults.ts b/packages/world/ts/config/v2/defaults.ts index f9ab5f7a3c..6b89d1cd81 100644 --- a/packages/world/ts/config/v2/defaults.ts +++ b/packages/world/ts/config/v2/defaults.ts @@ -20,7 +20,7 @@ export const DEPLOY_DEFAULTS = { deploysDirectory: "./deploys", worldsFile: "./worlds.json", useProxy: false, - useKms: false, + kms: undefined, } as const; export type DEPLOY_DEFAULTS = typeof DEPLOY_DEFAULTS; diff --git a/packages/world/ts/config/v2/input.ts b/packages/world/ts/config/v2/input.ts index 9974e4a3ea..8d0fccb70f 100644 --- a/packages/world/ts/config/v2/input.ts +++ b/packages/world/ts/config/v2/input.ts @@ -43,7 +43,15 @@ export type CodegenInput = { /** Deploy the World as an upgradable proxy */ useProxy?: boolean; /** Deploy the World using KMS */ - useKms?: boolean; + kms?: { + endpoint: string; + region: string; + credentials: { + accessKeyId: string; + secretAccessKey: string; + }; + keyId: string; + }; }; export type WorldInput = evaluate< diff --git a/packages/world/ts/config/v2/output.ts b/packages/world/ts/config/v2/output.ts index f3ef179118..068ef8133b 100644 --- a/packages/world/ts/config/v2/output.ts +++ b/packages/world/ts/config/v2/output.ts @@ -44,7 +44,15 @@ export type Deploy = { /** Deploy the World as an upgradable proxy */ readonly useProxy: boolean; /** Deploy the World using KMS */ - readonly useKms: boolean; + readonly kms?: { + readonly endpoint: string; + readonly region: string; + readonly credentials: { + readonly accessKeyId: string; + readonly secretAccessKey: string; + }; + readonly keyId: string; + }; }; export type Codegen = { From 605cfa7d2855ad25954f079cae786d4bc28ef631 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 23 Apr 2024 14:28:56 +0100 Subject: [PATCH 35/53] refactor: get credentials from env --- packages/cli/src/runDeploy.ts | 1 - packages/world/ts/config/v2/input.ts | 4 ---- packages/world/ts/config/v2/output.ts | 4 ---- 3 files changed, 9 deletions(-) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index c61bc9a4e4..4e1afac0fe 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -97,7 +97,6 @@ in your contracts directory to use the default anvil private key.`, new KMSClient({ endpoint: configV2.deploy.kms.endpoint, region: configV2.deploy.kms.region, - credentials: configV2.deploy.kms.credentials, }), ) : privateKeyToAccount(privateKey); diff --git a/packages/world/ts/config/v2/input.ts b/packages/world/ts/config/v2/input.ts index 8d0fccb70f..68e86ddd26 100644 --- a/packages/world/ts/config/v2/input.ts +++ b/packages/world/ts/config/v2/input.ts @@ -46,10 +46,6 @@ export type CodegenInput = { kms?: { endpoint: string; region: string; - credentials: { - accessKeyId: string; - secretAccessKey: string; - }; keyId: string; }; }; diff --git a/packages/world/ts/config/v2/output.ts b/packages/world/ts/config/v2/output.ts index 068ef8133b..af4bdbb294 100644 --- a/packages/world/ts/config/v2/output.ts +++ b/packages/world/ts/config/v2/output.ts @@ -47,10 +47,6 @@ export type Deploy = { readonly kms?: { readonly endpoint: string; readonly region: string; - readonly credentials: { - readonly accessKeyId: string; - readonly secretAccessKey: string; - }; readonly keyId: string; }; }; From cd41a7c478a3d52cb1ca9f8aba408880ea6c7e49 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 23 Apr 2024 14:34:17 +0100 Subject: [PATCH 36/53] chore: cli doesnt need asn1 --- packages/cli/package.json | 3 +-- pnpm-lock.yaml | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index f39fee568c..b5ac7b6014 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -82,7 +82,6 @@ "vitest": "0.34.6" }, "peerDependencies": { - "@aws-sdk/client-kms": "^3.556.0", - "asn1.js": "^5.4.1" + "@aws-sdk/client-kms": "^3.556.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df17adb13e..757318b0a3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -178,9 +178,6 @@ importers: '@latticexyz/world-modules': specifier: workspace:* version: link:../world-modules - asn1.js: - specifier: ^5.4.1 - version: 5.4.1 chalk: specifier: ^5.0.1 version: 5.2.0 From 2e0ae9fa03347b109cd03099aa4584561f0265fa Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 23 Apr 2024 15:30:45 +0100 Subject: [PATCH 37/53] refactor: region and endpoint from CLI --- packages/cli/src/runDeploy.ts | 8 +------- packages/world/ts/config/v2/input.ts | 2 -- packages/world/ts/config/v2/output.ts | 2 -- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 4e1afac0fe..3ae317528d 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -92,13 +92,7 @@ in your contracts directory to use the default anvil private key.`, const resolvedConfig = resolveConfig({ config, forgeSourceDir: srcDir, forgeOutDir: outDir }); const account = configV2.deploy.kms - ? await createKmsAccount( - configV2.deploy.kms.keyId, - new KMSClient({ - endpoint: configV2.deploy.kms.endpoint, - region: configV2.deploy.kms.region, - }), - ) + ? await createKmsAccount(configV2.deploy.kms.keyId, new KMSClient({})) : privateKeyToAccount(privateKey); const client = createWalletClient({ diff --git a/packages/world/ts/config/v2/input.ts b/packages/world/ts/config/v2/input.ts index 68e86ddd26..7e7e2b80ad 100644 --- a/packages/world/ts/config/v2/input.ts +++ b/packages/world/ts/config/v2/input.ts @@ -44,8 +44,6 @@ export type CodegenInput = { useProxy?: boolean; /** Deploy the World using KMS */ kms?: { - endpoint: string; - region: string; keyId: string; }; }; diff --git a/packages/world/ts/config/v2/output.ts b/packages/world/ts/config/v2/output.ts index af4bdbb294..fd0256955e 100644 --- a/packages/world/ts/config/v2/output.ts +++ b/packages/world/ts/config/v2/output.ts @@ -45,8 +45,6 @@ export type Deploy = { readonly useProxy: boolean; /** Deploy the World using KMS */ readonly kms?: { - readonly endpoint: string; - readonly region: string; readonly keyId: string; }; }; From 59f2420f9d9ecd2d67d1707b9821f39385246afa Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 23 Apr 2024 15:41:05 +0100 Subject: [PATCH 38/53] refactor: just keyid --- packages/cli/src/runDeploy.ts | 4 ++-- packages/world/ts/config/v2/defaults.ts | 2 +- packages/world/ts/config/v2/input.ts | 4 +--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 3ae317528d..8442f1d284 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -91,8 +91,8 @@ in your contracts directory to use the default anvil private key.`, const resolvedConfig = resolveConfig({ config, forgeSourceDir: srcDir, forgeOutDir: outDir }); - const account = configV2.deploy.kms - ? await createKmsAccount(configV2.deploy.kms.keyId, new KMSClient({})) + const account = configV2.deploy.keyId + ? await createKmsAccount(configV2.deploy.keyId, new KMSClient({})) : privateKeyToAccount(privateKey); const client = createWalletClient({ diff --git a/packages/world/ts/config/v2/defaults.ts b/packages/world/ts/config/v2/defaults.ts index 6b89d1cd81..10d5e379e1 100644 --- a/packages/world/ts/config/v2/defaults.ts +++ b/packages/world/ts/config/v2/defaults.ts @@ -20,7 +20,7 @@ export const DEPLOY_DEFAULTS = { deploysDirectory: "./deploys", worldsFile: "./worlds.json", useProxy: false, - kms: undefined, + keyId: undefined, } as const; export type DEPLOY_DEFAULTS = typeof DEPLOY_DEFAULTS; diff --git a/packages/world/ts/config/v2/input.ts b/packages/world/ts/config/v2/input.ts index 7e7e2b80ad..3e992db754 100644 --- a/packages/world/ts/config/v2/input.ts +++ b/packages/world/ts/config/v2/input.ts @@ -43,9 +43,7 @@ export type CodegenInput = { /** Deploy the World as an upgradable proxy */ useProxy?: boolean; /** Deploy the World using KMS */ - kms?: { - keyId: string; - }; + keyId?: string; }; export type WorldInput = evaluate< From eb48a9673d8bfefc709b7877e3986d0dbd6f4aff Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 23 Apr 2024 15:42:34 +0100 Subject: [PATCH 39/53] fix: output --- packages/world/ts/config/v2/output.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/world/ts/config/v2/output.ts b/packages/world/ts/config/v2/output.ts index fd0256955e..39afa535bc 100644 --- a/packages/world/ts/config/v2/output.ts +++ b/packages/world/ts/config/v2/output.ts @@ -44,9 +44,7 @@ export type Deploy = { /** Deploy the World as an upgradable proxy */ readonly useProxy: boolean; /** Deploy the World using KMS */ - readonly kms?: { - readonly keyId: string; - }; + readonly keyId?: string; }; export type Codegen = { From 22fc7b7c6dad0813a312458384c5bdbfc27fc725 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 23 Apr 2024 15:44:46 +0100 Subject: [PATCH 40/53] refactor: no object --- packages/cli/src/runDeploy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 8442f1d284..e633df52de 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -92,7 +92,7 @@ in your contracts directory to use the default anvil private key.`, const resolvedConfig = resolveConfig({ config, forgeSourceDir: srcDir, forgeOutDir: outDir }); const account = configV2.deploy.keyId - ? await createKmsAccount(configV2.deploy.keyId, new KMSClient({})) + ? await createKmsAccount(configV2.deploy.keyId, new KMSClient()) : privateKeyToAccount(privateKey); const client = createWalletClient({ From bf0059d50dbbd24585c88214a416365702fed9c1 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 23 Apr 2024 15:55:07 +0100 Subject: [PATCH 41/53] refactor: cli option --- packages/cli/src/runDeploy.ts | 8 +++++--- packages/world/ts/config/v2/defaults.ts | 1 - packages/world/ts/config/v2/input.ts | 6 ++---- packages/world/ts/config/v2/output.ts | 2 -- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index e633df52de..34541c8430 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -43,6 +43,10 @@ export const deployOptions = { type: "string", desc: "The deployment salt to use. Defaults to a random salt.", }, + keyId: { + type: "string", + desc: "The AWS KMS keyId. If set, the World is deployed using KMS instead of local private key.", + }, } as const satisfies Record; export type DeployOptions = InferredOptionTypes; @@ -91,9 +95,7 @@ in your contracts directory to use the default anvil private key.`, const resolvedConfig = resolveConfig({ config, forgeSourceDir: srcDir, forgeOutDir: outDir }); - const account = configV2.deploy.keyId - ? await createKmsAccount(configV2.deploy.keyId, new KMSClient()) - : privateKeyToAccount(privateKey); + const account = opts.keyId ? await createKmsAccount(opts.keyId, new KMSClient()) : privateKeyToAccount(privateKey); const client = createWalletClient({ transport: http(rpc, { diff --git a/packages/world/ts/config/v2/defaults.ts b/packages/world/ts/config/v2/defaults.ts index 10d5e379e1..034a031e25 100644 --- a/packages/world/ts/config/v2/defaults.ts +++ b/packages/world/ts/config/v2/defaults.ts @@ -20,7 +20,6 @@ export const DEPLOY_DEFAULTS = { deploysDirectory: "./deploys", worldsFile: "./worlds.json", useProxy: false, - keyId: undefined, } as const; export type DEPLOY_DEFAULTS = typeof DEPLOY_DEFAULTS; diff --git a/packages/world/ts/config/v2/input.ts b/packages/world/ts/config/v2/input.ts index 3e992db754..40f5d69e54 100644 --- a/packages/world/ts/config/v2/input.ts +++ b/packages/world/ts/config/v2/input.ts @@ -31,6 +31,8 @@ export type DeployInput = { deploysDirectory?: string; /** JSON file to write to with chain -> latest world deploy address (Default "./worlds.json") */ worldsFile?: string; + /** Deploy the World as an upgradable proxy */ + useProxy?: boolean; }; export type CodegenInput = { @@ -40,10 +42,6 @@ export type CodegenInput = { worldgenDirectory?: string; /** Path for world package imports. Default is "@latticexyz/world/src/" */ worldImportPath?: string; - /** Deploy the World as an upgradable proxy */ - useProxy?: boolean; - /** Deploy the World using KMS */ - keyId?: string; }; export type WorldInput = evaluate< diff --git a/packages/world/ts/config/v2/output.ts b/packages/world/ts/config/v2/output.ts index 39afa535bc..980fb76b66 100644 --- a/packages/world/ts/config/v2/output.ts +++ b/packages/world/ts/config/v2/output.ts @@ -43,8 +43,6 @@ export type Deploy = { readonly worldsFile: string; /** Deploy the World as an upgradable proxy */ readonly useProxy: boolean; - /** Deploy the World using KMS */ - readonly keyId?: string; }; export type Codegen = { From 8b99b6da5f7dcdde00ea018f2b2ecea6ece35eeb Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 23 Apr 2024 16:16:37 +0100 Subject: [PATCH 42/53] fix: packages --- packages/cli/package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index b5ac7b6014..a59bea8a19 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -30,6 +30,7 @@ "test:ci": "pnpm run test" }, "dependencies": { + "@aws-sdk/client-kms": "^3.556.0", "@ethersproject/abi": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@improbable-eng/grpc-web": "^0.15.0", @@ -45,6 +46,7 @@ "@latticexyz/utils": "workspace:*", "@latticexyz/world": "workspace:*", "@latticexyz/world-modules": "workspace:*", + "asn1.js": "^5.4.1", "chalk": "^5.0.1", "chokidar": "^3.5.3", "debug": "^4.3.4", @@ -80,8 +82,5 @@ "tsup": "^6.7.0", "tsx": "^3.12.6", "vitest": "0.34.6" - }, - "peerDependencies": { - "@aws-sdk/client-kms": "^3.556.0" } } From 97d6764e0cb1742e2b6a6e5af726113fe4f3dcb9 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 23 Apr 2024 16:17:36 +0100 Subject: [PATCH 43/53] refactor: rename option, env variable --- packages/cli/src/runDeploy.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 34541c8430..cad4431370 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -43,7 +43,7 @@ export const deployOptions = { type: "string", desc: "The deployment salt to use. Defaults to a random salt.", }, - keyId: { + awsKmsKeyId: { type: "string", desc: "The AWS KMS keyId. If set, the World is deployed using KMS instead of local private key.", }, @@ -95,7 +95,8 @@ in your contracts directory to use the default anvil private key.`, const resolvedConfig = resolveConfig({ config, forgeSourceDir: srcDir, forgeOutDir: outDir }); - const account = opts.keyId ? await createKmsAccount(opts.keyId, new KMSClient()) : privateKeyToAccount(privateKey); + const keyId = opts.awsKmsKeyId ?? process.env.AWS_KMS_KEY_ID; + const account = keyId ? await createKmsAccount(keyId, new KMSClient()) : privateKeyToAccount(privateKey); const client = createWalletClient({ transport: http(rpc, { From bd1f8d9409fd041ff00344a3ac1b8a1acc7dbd06 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 23 Apr 2024 16:17:42 +0100 Subject: [PATCH 44/53] chore: install --- pnpm-lock.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 757318b0a3..df17adb13e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -178,6 +178,9 @@ importers: '@latticexyz/world-modules': specifier: workspace:* version: link:../world-modules + asn1.js: + specifier: ^5.4.1 + version: 5.4.1 chalk: specifier: ^5.0.1 version: 5.2.0 From caa9e00147a19c398d4d93642182a31a40f39fe5 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 23 Apr 2024 16:19:04 +0100 Subject: [PATCH 45/53] chore: tweak description --- packages/cli/src/runDeploy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index cad4431370..955ab28745 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -45,7 +45,7 @@ export const deployOptions = { }, awsKmsKeyId: { type: "string", - desc: "The AWS KMS keyId. If set, the World is deployed using KMS instead of local private key.", + desc: "The AWS KMS keyId. If set, the World is deployed using a KMS signer instead of local private key.", }, } as const satisfies Record; From 8a8856e1f16175f4fa432267b8b6a04b5f64f3ef Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 23 Apr 2024 16:19:50 +0100 Subject: [PATCH 46/53] chore: changeset --- .changeset/early-teachers-greet.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/early-teachers-greet.md diff --git a/.changeset/early-teachers-greet.md b/.changeset/early-teachers-greet.md new file mode 100644 index 0000000000..9d4287ac77 --- /dev/null +++ b/.changeset/early-teachers-greet.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/cli": patch +--- + +Added a `awsKmsKeyId` flag to `mud deploy` that deploys the world using an AWS KMS signer. From bdcd332a8dff2e694298963eed66bf9ccd6e7451 Mon Sep 17 00:00:00 2001 From: yonada Date: Tue, 23 Apr 2024 17:53:48 +0100 Subject: [PATCH 47/53] docs: tweak description Co-authored-by: Kevin Ingersoll --- packages/cli/src/runDeploy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 955ab28745..33533b42bc 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -45,7 +45,7 @@ export const deployOptions = { }, awsKmsKeyId: { type: "string", - desc: "The AWS KMS keyId. If set, the World is deployed using a KMS signer instead of local private key.", + desc: "Optional AWS KMS key ID. If set, the World is deployed using a KMS signer instead of local private key.", }, } as const satisfies Record; From b0bc562abb03b3d7730bf8ffa1aa271cdd37695d Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 23 Apr 2024 17:54:15 +0100 Subject: [PATCH 48/53] refactor: useProxy thing --- packages/world/ts/config/v2/input.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/world/ts/config/v2/input.ts b/packages/world/ts/config/v2/input.ts index 40f5d69e54..3f6bf09baa 100644 --- a/packages/world/ts/config/v2/input.ts +++ b/packages/world/ts/config/v2/input.ts @@ -31,8 +31,6 @@ export type DeployInput = { deploysDirectory?: string; /** JSON file to write to with chain -> latest world deploy address (Default "./worlds.json") */ worldsFile?: string; - /** Deploy the World as an upgradable proxy */ - useProxy?: boolean; }; export type CodegenInput = { @@ -42,6 +40,8 @@ export type CodegenInput = { worldgenDirectory?: string; /** Path for world package imports. Default is "@latticexyz/world/src/" */ worldImportPath?: string; + /** Deploy the World as an upgradable proxy */ + useProxy?: boolean; }; export type WorldInput = evaluate< From 581c04c4b71c0b41d2ac15f41ce8f2447db38390 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Wed, 24 Apr 2024 15:36:41 +0100 Subject: [PATCH 49/53] fix: dev-contracts --- packages/cli/src/commands/dev-contracts.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/src/commands/dev-contracts.ts b/packages/cli/src/commands/dev-contracts.ts index 53c5572693..1cb7fecc89 100644 --- a/packages/cli/src/commands/dev-contracts.ts +++ b/packages/cli/src/commands/dev-contracts.ts @@ -91,6 +91,7 @@ const commandModule: CommandModule Date: Wed, 24 Apr 2024 15:37:45 +0100 Subject: [PATCH 50/53] chore: reset examples --- examples/multiple-accounts/packages/contracts/.env | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 examples/multiple-accounts/packages/contracts/.env diff --git a/examples/multiple-accounts/packages/contracts/.env b/examples/multiple-accounts/packages/contracts/.env new file mode 100644 index 0000000000..84889c8ef2 --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/.env @@ -0,0 +1,11 @@ +# This .env file is for demonstration purposes only. +# +# This should usually be excluded via .gitignore and the env vars attached to +# your deployment environment, but we're including this here for ease of local +# development. Please do not commit changes to this file! +# +# Enable debug logs for MUD CLI +DEBUG=mud:* +# +# Anvil default private key: +PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 From 1a2ee2f173c2578d8049bca9074cd881c7dd8065 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Wed, 24 Apr 2024 15:38:30 +0100 Subject: [PATCH 51/53] chore: delete unused files --- packages/common/src/account/kms.ts | 55 --------------- packages/common/src/account/sign.ts | 103 ---------------------------- 2 files changed, 158 deletions(-) delete mode 100644 packages/common/src/account/kms.ts delete mode 100644 packages/common/src/account/sign.ts diff --git a/packages/common/src/account/kms.ts b/packages/common/src/account/kms.ts deleted file mode 100644 index 25c277dd67..0000000000 --- a/packages/common/src/account/kms.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { - GetPublicKeyCommand, - GetPublicKeyCommandOutput, - KMSClient, - SignCommand, - SignCommandInput, - SignCommandOutput, -} from "@aws-sdk/client-kms"; -import { Address, Hex, fromHex } from "viem"; -import { getEthAddressFromPublicKey } from "./sign"; - -type GetPublicKeyParams = { - keyId: SignCommandInput["KeyId"]; - kmsInstance: KMSClient; -}; - -type GetEthAddressFromKMSparams = { - keyId: SignCommandInput["KeyId"]; - kmsInstance: KMSClient; -}; - -type SignParams = { - hash: Hex; - keyId: SignCommandInput["KeyId"]; - kmsInstance: KMSClient; -}; - -const getPublicKey = (getPublicKeyParams: GetPublicKeyParams): Promise => { - const { keyId, kmsInstance } = getPublicKeyParams; - const command = new GetPublicKeyCommand({ KeyId: keyId }); - - return kmsInstance.send(command); -}; - -export const getEthAddressFromKMS = async ( - getEthAddressFromKMSparams: GetEthAddressFromKMSparams, -): Promise
=> { - const { keyId, kmsInstance } = getEthAddressFromKMSparams; - const KMSKey = await getPublicKey({ keyId, kmsInstance }); - - return getEthAddressFromPublicKey(KMSKey.PublicKey as Uint8Array); -}; - -export const signWithKMS = async ({ keyId, hash, kmsInstance }: SignParams): Promise => { - const formatted = Buffer.from(fromHex(hash, "bytes")); - - const command = new SignCommand({ - KeyId: keyId, - Message: formatted, - SigningAlgorithm: "ECDSA_SHA_256", - MessageType: "DIGEST", - }); - - return kmsInstance.send(command); -}; diff --git a/packages/common/src/account/sign.ts b/packages/common/src/account/sign.ts deleted file mode 100644 index cdaca736e0..0000000000 --- a/packages/common/src/account/sign.ts +++ /dev/null @@ -1,103 +0,0 @@ -// @ts-expect-error types -import asn1 from "asn1.js"; -import { Address, Hex, isAddressEqual, signatureToHex, toHex } from "viem"; -import { publicKeyToAddress, recoverAddress } from "viem/utils"; -import { KMSClient, SignCommandInput } from "@aws-sdk/client-kms"; -import { signWithKMS } from "./kms"; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const EcdsaSigAsnParse = asn1.define("EcdsaSig", function (this: any) { - this.seq().obj(this.key("r").int(), this.key("s").int()); -}); - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const EcdsaPubKey = asn1.define("EcdsaPubKey", function (this: any) { - this.seq().obj(this.key("algo").seq().obj(this.key("a").objid(), this.key("b").objid()), this.key("pubKey").bitstr()); -}); - -const getRS = async (signParams: { - hash: Hex; - keyId: SignCommandInput["KeyId"]; - kmsInstance: KMSClient; -}): Promise<{ r: Hex; s: Hex }> => { - const signature = await signWithKMS(signParams); - - if (signature.Signature === undefined) { - throw new Error("Signature is undefined."); - } - - const decoded = EcdsaSigAsnParse.decode(Buffer.from(signature.Signature), "der"); - - const r = BigInt(decoded.r); - let s = BigInt(decoded.s); - - const secp256k1N = BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); - const secp256k1halfN = secp256k1N / 2n; - - if (s > secp256k1halfN) { - s = secp256k1N - s; - } - - return { - r: toHex(r), - s: toHex(s), - }; -}; - -const getRecovery = async (hash: Hex, r: Hex, s: Hex, expectedAddress: Hex): Promise => { - let recovery: number; - for (recovery = 0; recovery <= 1; recovery++) { - const signature = signatureToHex({ - r, - s, - v: recovery ? 28n : 27n, - yParity: recovery, - }); - - const address = await recoverAddress({ hash, signature }); - - if (isAddressEqual(address, expectedAddress)) { - return recovery; - } - } - throw new Error("Failed to calculate recovery param"); -}; - -export const getEthAddressFromPublicKey = (publicKey: Uint8Array): Address => { - const res = EcdsaPubKey.decode(Buffer.from(publicKey)); - - const publicKeyBuffer: Buffer = res.pubKey.data; - - const publicKeyHex = toHex(publicKeyBuffer); - const address = publicKeyToAddress(publicKeyHex); - - return address; -}; - -type SignParameters = { - hash: Hex; - keyId: SignCommandInput["KeyId"]; - kmsInstance: KMSClient; - address: Hex; -}; - -type SignReturnType = Hex; - -/** - * @description Signs a hash with a given KMS key. - * - * @param hash The hash to sign. - * - * @returns The signature. - */ -export const sign = async ({ hash, address, keyId, kmsInstance }: SignParameters): Promise => { - const { r, s } = await getRS({ keyId, hash, kmsInstance }); - const recovery = await getRecovery(hash, r, s, address); - - return signatureToHex({ - r, - s, - v: recovery ? 28n : 27n, - yParity: recovery, - }); -}; From 74ddd0546fcfd859cb4e3f8305b0cdd9995005c0 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Thu, 25 Apr 2024 13:54:16 +0100 Subject: [PATCH 52/53] fix: import after merge --- packages/cli/src/runDeploy.ts | 4 ++-- packages/common/src/account/kms/kmsKeyToAccount.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index b94ba9002d..33d5207d7c 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -15,7 +15,7 @@ import { getChainId } from "viem/actions"; import { postDeploy } from "./utils/postDeploy"; import { WorldDeploy } from "./deploy/common"; import { build } from "./build"; -import { createKmsAccount } from "@latticexyz/common"; +import { kmsKeyToAccount } from "@latticexyz/common/kms"; export const deployOptions = { configPath: { type: "string", desc: "Path to the MUD config file" }, @@ -96,7 +96,7 @@ in your contracts directory to use the default anvil private key.`, const resolvedConfig = resolveConfig({ config, forgeSourceDir: srcDir, forgeOutDir: outDir }); const keyId = opts.awsKmsKeyId ?? process.env.AWS_KMS_KEY_ID; - const account = keyId ? await createKmsAccount({ keyId }) : privateKeyToAccount(privateKey); + const account = keyId ? await kmsKeyToAccount({ keyId }) : privateKeyToAccount(privateKey); const client = createWalletClient({ transport: http(rpc, { diff --git a/packages/common/src/account/kms/kmsKeyToAccount.ts b/packages/common/src/account/kms/kmsKeyToAccount.ts index c9f82ff00a..e0d43d150a 100644 --- a/packages/common/src/account/kms/kmsKeyToAccount.ts +++ b/packages/common/src/account/kms/kmsKeyToAccount.ts @@ -18,7 +18,6 @@ export type KmsAccount = LocalAccount<"aws-kms"> & { * * @returns A Local Account. */ - export async function kmsKeyToAccount({ keyId, client = new KMSClient(), From a4ccd8fba08e576f3166a3ec4c5460f2692887d0 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Thu, 25 Apr 2024 06:11:45 -0700 Subject: [PATCH 53/53] Update .changeset/early-teachers-greet.md --- .changeset/early-teachers-greet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/early-teachers-greet.md b/.changeset/early-teachers-greet.md index 9d4287ac77..816f97716b 100644 --- a/.changeset/early-teachers-greet.md +++ b/.changeset/early-teachers-greet.md @@ -2,4 +2,4 @@ "@latticexyz/cli": patch --- -Added a `awsKmsKeyId` flag to `mud deploy` that deploys the world using an AWS KMS signer. +Added a `--awsKmsKeyId` flag to `mud deploy` that deploys the world using an AWS KMS key as a transaction signer.